1 /* SPDX-License-Identifier: GPL-2.0 */
3 * ACPI wakeup real mode startup stub
5 #include <linux/linkage.h>
6 #include <asm/segment.h>
7 #include <asm/msr-index.h>
8 #include <asm/page_types.h>
9 #include <asm/pgtable_types.h>
10 #include <asm/processor-flags.h>
16 /* This should match the structure in wakeup.h */
17 .section ".data", "aw"
20 SYM_DATA_START(wakeup_header)
21 video_mode: .short 0 /* Video mode number */
23 pmode_cs: .short __KERNEL_CS
24 pmode_cr0: .long 0 /* Saved %cr0 */
25 pmode_cr3: .long 0 /* Saved %cr3 */
26 pmode_cr4: .long 0 /* Saved %cr4 */
27 pmode_efer: .quad 0 /* Saved EFER */
29 pmode_misc_en: .quad 0 /* Saved MISC_ENABLE MSR */
30 pmode_behavior: .long 0 /* Wakeup behavior flags */
31 realmode_flags: .long 0
33 signature: .long WAKEUP_HEADER_SIGNATURE
34 SYM_DATA_END(wakeup_header)
40 SYM_CODE_START(wakeup_start)
46 /* Apparently some dimwit BIOS programmers don't know how to
47 program a PM to RM transition, and we might end up here with
48 junk in the data segment descriptor registers. The only way
49 to repair that is to go into PM and fix it ourselves... */
63 andb $~X86_CR0_PE, %al
70 movl $rm_stack_end, %esp
78 /* Clear the EFLAGS */
82 /* Check header signature... */
84 cmpl $WAKEUP_HEADER_SIGNATURE, %eax
87 /* Check we really have everything... */
88 movl end_signature, %eax
89 cmpl $REALMODE_END_SIGNATURE, %eax
95 /* Restore MISC_ENABLE before entering protected mode, in case
96 BIOS decided to clear XD_DISABLE during S3. */
97 movl pmode_behavior, %edi
98 btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi
101 movl pmode_misc_en, %eax
102 movl pmode_misc_en + 4, %edx
103 movl $MSR_IA32_MISC_ENABLE, %ecx
107 /* Do any other stuff... */
110 /* This could also be done in C code... */
114 btl $WAKEUP_BEHAVIOR_RESTORE_CR4, %edi
119 btl $WAKEUP_BEHAVIOR_RESTORE_EFER, %edi
121 movl pmode_efer, %eax
122 movl pmode_efer + 4, %edx
129 /* This really couldn't... */
130 movl pmode_entry, %eax
133 ljmpl $__KERNEL_CS, $pa_startup_32
134 /* -> jmp *%eax in trampoline_32.S */
138 SYM_CODE_END(wakeup_start)
145 .section ".rodata","a"
148 * Set up the wakeup GDT. We set these up as Big Real Mode,
149 * that is, with limits set to 4 GB. At least the Lenovo
150 * Thinkpad X61 is known to need this for the video BIOS
151 * initialization quirk to work; this is likely to also
152 * be the case for other laptops or integrated video devices.
156 SYM_DATA_START(wakeup_gdt)
157 .word 3*8-1 /* Self-descriptor */
161 .word 0xffff /* 16-bit code segment @ real_mode_base */
162 .long 0x9b000000 + pa_real_mode_base
163 .word 0x008f /* big real mode */
165 .word 0xffff /* 16-bit data segment @ real_mode_base */
166 .long 0x93000000 + pa_real_mode_base
167 .word 0x008f /* big real mode */
168 SYM_DATA_END(wakeup_gdt)
170 .section ".rodata","a"
173 /* This is the standard real-mode IDT */
175 SYM_DATA_START_LOCAL(.Lwakeup_idt)
176 .word 0xffff /* limit */
177 .long 0 /* address */
179 SYM_DATA_END(.Lwakeup_idt)