Commit | Line | Data |
---|---|---|
e44b7b75 PM |
1 | /* |
2 | * ACPI wakeup real mode startup stub | |
3 | */ | |
8e029fcd | 4 | #include <linux/linkage.h> |
e44b7b75 PM |
5 | #include <asm/segment.h> |
6 | #include <asm/msr-index.h> | |
0341c14d JF |
7 | #include <asm/page_types.h> |
8 | #include <asm/pgtable_types.h> | |
4b4f7280 | 9 | #include <asm/processor-flags.h> |
c4845474 | 10 | #include "realmode.h" |
d1ee4335 | 11 | #include "wakeup.h" |
e44b7b75 | 12 | |
8e029fcd | 13 | .code16 |
e44b7b75 PM |
14 | |
15 | /* This should match the structure in wakeup.h */ | |
8e029fcd JS |
16 | .section ".data", "aw" |
17 | ||
18 | .balign 16 | |
19 | GLOBAL(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 | |
33 | END(wakeup_header) | |
e44b7b75 PM |
34 | |
35 | .text | |
e44b7b75 | 36 | .code16 |
8e029fcd JS |
37 | |
38 | .balign 16 | |
39 | ENTRY(wakeup_start) | |
c9b77ccb | 40 | cli |
e44b7b75 PM |
41 | cld |
42 | ||
e5684ec4 | 43 | LJMPW_RM(3f) |
c9b77ccb | 44 | 3: |
4b4f7280 PA |
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 | |
c9b77ccb | 54 | ljmpw $8, $2f |
4b4f7280 PA |
55 | 2: |
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 | |
e5684ec4 | 64 | LJMPW_RM(3f) |
4b4f7280 | 65 | 3: |
e44b7b75 PM |
66 | /* Set up segments */ |
67 | movw %cs, %ax | |
8e029fcd JS |
68 | movw %ax, %ss |
69 | movl $rm_stack_end, %esp | |
e44b7b75 PM |
70 | movw %ax, %ds |
71 | movw %ax, %es | |
8e029fcd JS |
72 | movw %ax, %fs |
73 | movw %ax, %gs | |
e44b7b75 | 74 | |
8e029fcd | 75 | lidtl wakeup_idt |
e44b7b75 | 76 | |
1396adc3 | 77 | /* Clear the EFLAGS */ |
73201dbe PA |
78 | pushl $0 |
79 | popfl | |
e44b7b75 PM |
80 | |
81 | /* Check header signature... */ | |
82 | movl signature, %eax | |
d1ee4335 | 83 | cmpl $WAKEUP_HEADER_SIGNATURE, %eax |
e44b7b75 PM |
84 | jne bogus_real_magic |
85 | ||
86 | /* Check we really have everything... */ | |
87 | movl end_signature, %eax | |
61f54461 | 88 | cmpl $REALMODE_END_SIGNATURE, %eax |
e44b7b75 PM |
89 | jne bogus_real_magic |
90 | ||
91 | /* Call the C code */ | |
92 | calll main | |
93 | ||
7a313666 KC |
94 | /* Restore MISC_ENABLE before entering protected mode, in case |
95 | BIOS decided to clear XD_DISABLE during S3. */ | |
73201dbe PA |
96 | movl pmode_behavior, %edi |
97 | btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi | |
7a313666 KC |
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 | |
104 | 1: | |
105 | ||
e44b7b75 PM |
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 | ||
73201dbe | 113 | btl $WAKEUP_BEHAVIOR_RESTORE_CR4, %edi |
1396adc3 | 114 | jnc 1f |
73201dbe PA |
115 | movl pmode_cr4, %eax |
116 | movl %eax, %cr4 | |
e44b7b75 | 117 | 1: |
73201dbe | 118 | btl $WAKEUP_BEHAVIOR_RESTORE_EFER, %edi |
1396adc3 | 119 | jnc 1f |
e44b7b75 PM |
120 | movl pmode_efer, %eax |
121 | movl pmode_efer + 4, %edx | |
cfaa71ee | 122 | movl $MSR_EFER, %ecx |
e44b7b75 PM |
123 | wrmsr |
124 | 1: | |
125 | ||
126 | lgdtl pmode_gdt | |
127 | ||
128 | /* This really couldn't... */ | |
968ff9ee PA |
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 */ | |
e44b7b75 | 134 | #else |
f37240f1 | 135 | jmp trampoline_start |
e44b7b75 PM |
136 | #endif |
137 | ||
138 | bogus_real_magic: | |
139 | 1: | |
140 | hlt | |
141 | jmp 1b | |
142 | ||
c9b77ccb JS |
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 | ||
c9b77ccb | 153 | .balign 16 |
8e029fcd | 154 | GLOBAL(wakeup_gdt) |
c9b77ccb JS |
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 */ | |
8e029fcd | 166 | END(wakeup_gdt) |
c9b77ccb | 167 | |
8e029fcd | 168 | .section ".rodata","a" |
4b4f7280 PA |
169 | .balign 8 |
170 | ||
171 | /* This is the standard real-mode IDT */ | |
8e029fcd JS |
172 | .balign 16 |
173 | GLOBAL(wakeup_idt) | |
4b4f7280 PA |
174 | .word 0xffff /* limit */ |
175 | .long 0 /* address */ | |
176 | .word 0 | |
8e029fcd | 177 | END(wakeup_idt) |