123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- /*
- * 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, %edi
- btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi
- 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
- btl $WAKEUP_BEHAVIOR_RESTORE_CR4, %edi
- jnc 1f
- movl pmode_cr4, %eax
- movl %eax, %cr4
- 1:
- btl $WAKEUP_BEHAVIOR_RESTORE_EFER, %edi
- jnc 1f
- movl pmode_efer, %eax
- movl pmode_efer + 4, %edx
- 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)
|