linux/arch/x86/realmode/rm/wakeup_asm.S
H. Peter Anvin 61f5446169 x86, realmode: Move end signature into header.S
The end signature was defined in wakeup_asm.S as it originally came
from the ACPI wakeup code.  However, we rely on the existence of the
.signature section to expand .bss, otherwise we would have to include
code to explicitly zero the .bss depending on the configuration.
Since the expanded .bss is just in .init.data anyway, it's easier to
always have it expanded.

This fixes failures when compiled without CONFIG_ACPI_SLEEP.

Reported-by: Ingo Molnar <mingo@kernel.org>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Cc: Jarkko Sakkinen <jarkko.sakkinen@intel.com>
2012-05-21 00:02:45 -07:00

178 lines
3.6 KiB
ArmAsm

/*
* ACPI wakeup real mode startup stub
*/
#include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/msr-index.h>
#include <asm/page_types.h>
#include <asm/pgtable_types.h>
#include <asm/processor-flags.h>
#include "realmode.h"
#include "wakeup.h"
.code16
/* This should match the structure in wakeup.h */
.section ".data", "aw"
.balign 16
GLOBAL(wakeup_header)
video_mode: .short 0 /* Video mode number */
pmode_entry: .long 0
pmode_cs: .short __KERNEL_CS
pmode_cr0: .long 0 /* Saved %cr0 */
pmode_cr3: .long 0 /* Saved %cr3 */
pmode_cr4: .long 0 /* Saved %cr4 */
pmode_efer: .quad 0 /* Saved EFER */
pmode_gdt: .quad 0
pmode_misc_en: .quad 0 /* Saved MISC_ENABLE MSR */
pmode_behavior: .long 0 /* Wakeup behavior flags */
realmode_flags: .long 0
real_magic: .long 0
signature: .long WAKEUP_HEADER_SIGNATURE
END(wakeup_header)
.text
.code16
.balign 16
ENTRY(wakeup_start)
cli
cld
LJMPW_RM(3f)
3:
/* Apparently some dimwit BIOS programmers don't know how to
program a PM to RM transition, and we might end up here with
junk in the data segment descriptor registers. The only way
to repair that is to go into PM and fix it ourselves... */
movw $16, %cx
lgdtl %cs:wakeup_gdt
movl %cr0, %eax
orb $X86_CR0_PE, %al
movl %eax, %cr0
ljmpw $8, $2f
2:
movw %cx, %ds
movw %cx, %es
movw %cx, %ss
movw %cx, %fs
movw %cx, %gs
andb $~X86_CR0_PE, %al
movl %eax, %cr0
LJMPW_RM(3f)
3:
/* Set up segments */
movw %cs, %ax
movw %ax, %ss
movl $rm_stack_end, %esp
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
lidtl wakeup_idt
/* Clear the EFLAGS */
pushl $0
popfl
/* Check header signature... */
movl signature, %eax
cmpl $WAKEUP_HEADER_SIGNATURE, %eax
jne bogus_real_magic
/* Check we really have everything... */
movl end_signature, %eax
cmpl $REALMODE_END_SIGNATURE, %eax
jne bogus_real_magic
/* Call the C code */
calll main
/* Restore MISC_ENABLE before entering protected mode, in case
BIOS decided to clear XD_DISABLE during S3. */
movl pmode_behavior, %eax
btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %eax
jnc 1f
movl pmode_misc_en, %eax
movl pmode_misc_en + 4, %edx
movl $MSR_IA32_MISC_ENABLE, %ecx
wrmsr
1:
/* Do any other stuff... */
#ifndef CONFIG_64BIT
/* This could also be done in C code... */
movl pmode_cr3, %eax
movl %eax, %cr3
movl pmode_cr4, %ecx
jecxz 1f
movl %ecx, %cr4
1:
movl pmode_efer, %eax
movl pmode_efer + 4, %edx
movl %eax, %ecx
orl %edx, %ecx
jz 1f
movl $MSR_EFER, %ecx
wrmsr
1:
lgdtl pmode_gdt
/* This really couldn't... */
movl pmode_entry, %eax
movl pmode_cr0, %ecx
movl %ecx, %cr0
ljmpl $__KERNEL_CS, $pa_startup_32
/* -> jmp *%eax in trampoline_32.S */
#else
jmp trampoline_start
#endif
bogus_real_magic:
1:
hlt
jmp 1b
.section ".rodata","a"
/*
* Set up the wakeup GDT. We set these up as Big Real Mode,
* that is, with limits set to 4 GB. At least the Lenovo
* Thinkpad X61 is known to need this for the video BIOS
* initialization quirk to work; this is likely to also
* be the case for other laptops or integrated video devices.
*/
.balign 16
GLOBAL(wakeup_gdt)
.word 3*8-1 /* Self-descriptor */
.long pa_wakeup_gdt
.word 0
.word 0xffff /* 16-bit code segment @ real_mode_base */
.long 0x9b000000 + pa_real_mode_base
.word 0x008f /* big real mode */
.word 0xffff /* 16-bit data segment @ real_mode_base */
.long 0x93000000 + pa_real_mode_base
.word 0x008f /* big real mode */
END(wakeup_gdt)
.section ".rodata","a"
.balign 8
/* This is the standard real-mode IDT */
.balign 16
GLOBAL(wakeup_idt)
.word 0xffff /* limit */
.long 0 /* address */
.word 0
END(wakeup_idt)