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 */ 78 pushl $0 79 popfl 80 81 /* Check header signature... */ 82 movl signature, %eax 83 cmpl $WAKEUP_HEADER_SIGNATURE, %eax 84 jne bogus_real_magic 85 86 /* Check we really have everything... */ 87 movl end_signature, %eax 88 cmpl $REALMODE_END_SIGNATURE, %eax 89 jne bogus_real_magic 90 91 /* Call the C code */ 92 calll main 93 94 /* Restore MISC_ENABLE before entering protected mode, in case 95 BIOS decided to clear XD_DISABLE during S3. */ 96 movl pmode_behavior, %eax 97 btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %eax 98 jnc 1f 99 100 movl pmode_misc_en, %eax 101 movl pmode_misc_en + 4, %edx 102 movl $MSR_IA32_MISC_ENABLE, %ecx 103 wrmsr 1041: 105 106 /* Do any other stuff... */ 107 108#ifndef CONFIG_64BIT 109 /* This could also be done in C code... */ 110 movl pmode_cr3, %eax 111 movl %eax, %cr3 112 113 movl pmode_cr4, %ecx 114 jecxz 1f 115 movl %ecx, %cr4 1161: 117 movl pmode_efer, %eax 118 movl pmode_efer + 4, %edx 119 movl %eax, %ecx 120 orl %edx, %ecx 121 jz 1f 122 movl $MSR_EFER, %ecx 123 wrmsr 1241: 125 126 lgdtl pmode_gdt 127 128 /* This really couldn't... */ 129 movl pmode_entry, %eax 130 movl pmode_cr0, %ecx 131 movl %ecx, %cr0 132 ljmpl $__KERNEL_CS, $pa_startup_32 133 /* -> jmp *%eax in trampoline_32.S */ 134#else 135 jmp trampoline_start 136#endif 137 138bogus_real_magic: 1391: 140 hlt 141 jmp 1b 142 143 .section ".rodata","a" 144 145 /* 146 * Set up the wakeup GDT. We set these up as Big Real Mode, 147 * that is, with limits set to 4 GB. At least the Lenovo 148 * Thinkpad X61 is known to need this for the video BIOS 149 * initialization quirk to work; this is likely to also 150 * be the case for other laptops or integrated video devices. 151 */ 152 153 .balign 16 154GLOBAL(wakeup_gdt) 155 .word 3*8-1 /* Self-descriptor */ 156 .long pa_wakeup_gdt 157 .word 0 158 159 .word 0xffff /* 16-bit code segment @ real_mode_base */ 160 .long 0x9b000000 + pa_real_mode_base 161 .word 0x008f /* big real mode */ 162 163 .word 0xffff /* 16-bit data segment @ real_mode_base */ 164 .long 0x93000000 + pa_real_mode_base 165 .word 0x008f /* big real mode */ 166END(wakeup_gdt) 167 168 .section ".rodata","a" 169 .balign 8 170 171 /* This is the standard real-mode IDT */ 172 .balign 16 173GLOBAL(wakeup_idt) 174 .word 0xffff /* limit */ 175 .long 0 /* address */ 176 .word 0 177END(wakeup_idt) 178