1c4845474SJarkko Sakkinen/* 2c4845474SJarkko Sakkinen * ACPI wakeup real mode startup stub 3c4845474SJarkko Sakkinen */ 4c4845474SJarkko Sakkinen#include <linux/linkage.h> 5c4845474SJarkko Sakkinen#include <asm/segment.h> 6c4845474SJarkko Sakkinen#include <asm/msr-index.h> 7c4845474SJarkko Sakkinen#include <asm/page_types.h> 8c4845474SJarkko Sakkinen#include <asm/pgtable_types.h> 9c4845474SJarkko Sakkinen#include <asm/processor-flags.h> 10c4845474SJarkko Sakkinen#include "realmode.h" 11c4845474SJarkko Sakkinen#include "wakeup.h" 12c4845474SJarkko Sakkinen 13c4845474SJarkko Sakkinen .code16 14c4845474SJarkko Sakkinen 15c4845474SJarkko Sakkinen/* This should match the structure in wakeup.h */ 16c4845474SJarkko Sakkinen .section ".data", "aw" 17c4845474SJarkko Sakkinen 18c4845474SJarkko Sakkinen .balign 16 19c4845474SJarkko SakkinenGLOBAL(wakeup_header) 20c4845474SJarkko Sakkinen video_mode: .short 0 /* Video mode number */ 21c4845474SJarkko Sakkinen pmode_entry: .long 0 22c4845474SJarkko Sakkinen pmode_cs: .short __KERNEL_CS 23c4845474SJarkko Sakkinen pmode_cr0: .long 0 /* Saved %cr0 */ 24c4845474SJarkko Sakkinen pmode_cr3: .long 0 /* Saved %cr3 */ 25c4845474SJarkko Sakkinen pmode_cr4: .long 0 /* Saved %cr4 */ 26c4845474SJarkko Sakkinen pmode_efer: .quad 0 /* Saved EFER */ 27c4845474SJarkko Sakkinen pmode_gdt: .quad 0 28c4845474SJarkko Sakkinen pmode_misc_en: .quad 0 /* Saved MISC_ENABLE MSR */ 29c4845474SJarkko Sakkinen pmode_behavior: .long 0 /* Wakeup behavior flags */ 30c4845474SJarkko Sakkinen realmode_flags: .long 0 31c4845474SJarkko Sakkinen real_magic: .long 0 32c4845474SJarkko Sakkinen signature: .long WAKEUP_HEADER_SIGNATURE 33c4845474SJarkko SakkinenEND(wakeup_header) 34c4845474SJarkko Sakkinen 35c4845474SJarkko Sakkinen .text 36c4845474SJarkko Sakkinen .code16 37c4845474SJarkko Sakkinen 38c4845474SJarkko Sakkinen .balign 16 39c4845474SJarkko SakkinenENTRY(wakeup_start) 40c4845474SJarkko Sakkinen cli 41c4845474SJarkko Sakkinen cld 42c4845474SJarkko Sakkinen 43c4845474SJarkko Sakkinen LJMPW_RM(3f) 44c4845474SJarkko Sakkinen3: 45c4845474SJarkko Sakkinen /* Apparently some dimwit BIOS programmers don't know how to 46c4845474SJarkko Sakkinen program a PM to RM transition, and we might end up here with 47c4845474SJarkko Sakkinen junk in the data segment descriptor registers. The only way 48c4845474SJarkko Sakkinen to repair that is to go into PM and fix it ourselves... */ 49c4845474SJarkko Sakkinen movw $16, %cx 50c4845474SJarkko Sakkinen lgdtl %cs:wakeup_gdt 51c4845474SJarkko Sakkinen movl %cr0, %eax 52c4845474SJarkko Sakkinen orb $X86_CR0_PE, %al 53c4845474SJarkko Sakkinen movl %eax, %cr0 54c4845474SJarkko Sakkinen ljmpw $8, $2f 55c4845474SJarkko Sakkinen2: 56c4845474SJarkko Sakkinen movw %cx, %ds 57c4845474SJarkko Sakkinen movw %cx, %es 58c4845474SJarkko Sakkinen movw %cx, %ss 59c4845474SJarkko Sakkinen movw %cx, %fs 60c4845474SJarkko Sakkinen movw %cx, %gs 61c4845474SJarkko Sakkinen 62c4845474SJarkko Sakkinen andb $~X86_CR0_PE, %al 63c4845474SJarkko Sakkinen movl %eax, %cr0 64c4845474SJarkko Sakkinen LJMPW_RM(3f) 65c4845474SJarkko Sakkinen3: 66c4845474SJarkko Sakkinen /* Set up segments */ 67c4845474SJarkko Sakkinen movw %cs, %ax 68c4845474SJarkko Sakkinen movw %ax, %ss 69c4845474SJarkko Sakkinen movl $rm_stack_end, %esp 70c4845474SJarkko Sakkinen movw %ax, %ds 71c4845474SJarkko Sakkinen movw %ax, %es 72c4845474SJarkko Sakkinen movw %ax, %fs 73c4845474SJarkko Sakkinen movw %ax, %gs 74c4845474SJarkko Sakkinen 75c4845474SJarkko Sakkinen lidtl wakeup_idt 76c4845474SJarkko Sakkinen 77c4845474SJarkko Sakkinen /* Clear the EFLAGS */ 78c4845474SJarkko Sakkinen pushl $0 79c4845474SJarkko Sakkinen popfl 80c4845474SJarkko Sakkinen 81c4845474SJarkko Sakkinen /* Check header signature... */ 82c4845474SJarkko Sakkinen movl signature, %eax 83c4845474SJarkko Sakkinen cmpl $WAKEUP_HEADER_SIGNATURE, %eax 84c4845474SJarkko Sakkinen jne bogus_real_magic 85c4845474SJarkko Sakkinen 86c4845474SJarkko Sakkinen /* Check we really have everything... */ 87c4845474SJarkko Sakkinen movl end_signature, %eax 88*61f54461SH. Peter Anvin cmpl $REALMODE_END_SIGNATURE, %eax 89c4845474SJarkko Sakkinen jne bogus_real_magic 90c4845474SJarkko Sakkinen 91c4845474SJarkko Sakkinen /* Call the C code */ 92c4845474SJarkko Sakkinen calll main 93c4845474SJarkko Sakkinen 94c4845474SJarkko Sakkinen /* Restore MISC_ENABLE before entering protected mode, in case 95c4845474SJarkko Sakkinen BIOS decided to clear XD_DISABLE during S3. */ 96c4845474SJarkko Sakkinen movl pmode_behavior, %eax 97c4845474SJarkko Sakkinen btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %eax 98c4845474SJarkko Sakkinen jnc 1f 99c4845474SJarkko Sakkinen 100c4845474SJarkko Sakkinen movl pmode_misc_en, %eax 101c4845474SJarkko Sakkinen movl pmode_misc_en + 4, %edx 102c4845474SJarkko Sakkinen movl $MSR_IA32_MISC_ENABLE, %ecx 103c4845474SJarkko Sakkinen wrmsr 104c4845474SJarkko Sakkinen1: 105c4845474SJarkko Sakkinen 106c4845474SJarkko Sakkinen /* Do any other stuff... */ 107c4845474SJarkko Sakkinen 108c4845474SJarkko Sakkinen#ifndef CONFIG_64BIT 109c4845474SJarkko Sakkinen /* This could also be done in C code... */ 110c4845474SJarkko Sakkinen movl pmode_cr3, %eax 111c4845474SJarkko Sakkinen movl %eax, %cr3 112c4845474SJarkko Sakkinen 113c4845474SJarkko Sakkinen movl pmode_cr4, %ecx 114c4845474SJarkko Sakkinen jecxz 1f 115c4845474SJarkko Sakkinen movl %ecx, %cr4 116c4845474SJarkko Sakkinen1: 117c4845474SJarkko Sakkinen movl pmode_efer, %eax 118c4845474SJarkko Sakkinen movl pmode_efer + 4, %edx 119c4845474SJarkko Sakkinen movl %eax, %ecx 120c4845474SJarkko Sakkinen orl %edx, %ecx 121c4845474SJarkko Sakkinen jz 1f 122c4845474SJarkko Sakkinen movl $MSR_EFER, %ecx 123c4845474SJarkko Sakkinen wrmsr 124c4845474SJarkko Sakkinen1: 125c4845474SJarkko Sakkinen 126c4845474SJarkko Sakkinen lgdtl pmode_gdt 127c4845474SJarkko Sakkinen 128c4845474SJarkko Sakkinen /* This really couldn't... */ 129c4845474SJarkko Sakkinen movl pmode_entry, %eax 130c4845474SJarkko Sakkinen movl pmode_cr0, %ecx 131c4845474SJarkko Sakkinen movl %ecx, %cr0 132c4845474SJarkko Sakkinen ljmpl $__KERNEL_CS, $pa_startup_32 133c4845474SJarkko Sakkinen /* -> jmp *%eax in trampoline_32.S */ 134c4845474SJarkko Sakkinen#else 135f37240f1SJarkko Sakkinen jmp trampoline_start 136c4845474SJarkko Sakkinen#endif 137c4845474SJarkko Sakkinen 138c4845474SJarkko Sakkinenbogus_real_magic: 139c4845474SJarkko Sakkinen1: 140c4845474SJarkko Sakkinen hlt 141c4845474SJarkko Sakkinen jmp 1b 142c4845474SJarkko Sakkinen 143c4845474SJarkko Sakkinen .section ".rodata","a" 144c4845474SJarkko Sakkinen 145c4845474SJarkko Sakkinen /* 146c4845474SJarkko Sakkinen * Set up the wakeup GDT. We set these up as Big Real Mode, 147c4845474SJarkko Sakkinen * that is, with limits set to 4 GB. At least the Lenovo 148c4845474SJarkko Sakkinen * Thinkpad X61 is known to need this for the video BIOS 149c4845474SJarkko Sakkinen * initialization quirk to work; this is likely to also 150c4845474SJarkko Sakkinen * be the case for other laptops or integrated video devices. 151c4845474SJarkko Sakkinen */ 152c4845474SJarkko Sakkinen 153c4845474SJarkko Sakkinen .balign 16 154c4845474SJarkko SakkinenGLOBAL(wakeup_gdt) 155c4845474SJarkko Sakkinen .word 3*8-1 /* Self-descriptor */ 156c4845474SJarkko Sakkinen .long pa_wakeup_gdt 157c4845474SJarkko Sakkinen .word 0 158c4845474SJarkko Sakkinen 159c4845474SJarkko Sakkinen .word 0xffff /* 16-bit code segment @ real_mode_base */ 160c4845474SJarkko Sakkinen .long 0x9b000000 + pa_real_mode_base 161c4845474SJarkko Sakkinen .word 0x008f /* big real mode */ 162c4845474SJarkko Sakkinen 163c4845474SJarkko Sakkinen .word 0xffff /* 16-bit data segment @ real_mode_base */ 164c4845474SJarkko Sakkinen .long 0x93000000 + pa_real_mode_base 165c4845474SJarkko Sakkinen .word 0x008f /* big real mode */ 166c4845474SJarkko SakkinenEND(wakeup_gdt) 167c4845474SJarkko Sakkinen 168c4845474SJarkko Sakkinen .section ".rodata","a" 169c4845474SJarkko Sakkinen .balign 8 170c4845474SJarkko Sakkinen 171c4845474SJarkko Sakkinen /* This is the standard real-mode IDT */ 172c4845474SJarkko Sakkinen .balign 16 173c4845474SJarkko SakkinenGLOBAL(wakeup_idt) 174c4845474SJarkko Sakkinen .word 0xffff /* limit */ 175c4845474SJarkko Sakkinen .long 0 /* address */ 176c4845474SJarkko Sakkinen .word 0 177c4845474SJarkko SakkinenEND(wakeup_idt) 178