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