1/* SPDX-License-Identifier: GPL-2.0 */ 2/* 3 * ACPI wakeup real mode startup stub 4 */ 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> 11#include "realmode.h" 12#include "wakeup.h" 13 14 .code16 15 16/* This should match the structure in wakeup.h */ 17 .section ".data", "aw" 18 19 .balign 16 20SYM_DATA_START(wakeup_header) 21 video_mode: .short 0 /* Video mode number */ 22 pmode_entry: .long 0 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 */ 28 pmode_gdt: .quad 0 29 pmode_misc_en: .quad 0 /* Saved MISC_ENABLE MSR */ 30 pmode_behavior: .long 0 /* Wakeup behavior flags */ 31 realmode_flags: .long 0 32 real_magic: .long 0 33 signature: .long WAKEUP_HEADER_SIGNATURE 34SYM_DATA_END(wakeup_header) 35 36 .text 37 .code16 38 39 .balign 16 40SYM_CODE_START(wakeup_start) 41 cli 42 cld 43 44 LJMPW_RM(3f) 453: 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... */ 50 movw $16, %cx 51 lgdtl %cs:wakeup_gdt 52 movl %cr0, %eax 53 orb $X86_CR0_PE, %al 54 movl %eax, %cr0 55 ljmpw $8, $2f 562: 57 movw %cx, %ds 58 movw %cx, %es 59 movw %cx, %ss 60 movw %cx, %fs 61 movw %cx, %gs 62 63 andb $~X86_CR0_PE, %al 64 movl %eax, %cr0 65 LJMPW_RM(3f) 663: 67 /* Set up segments */ 68 movw %cs, %ax 69 movw %ax, %ss 70 movl $rm_stack_end, %esp 71 movw %ax, %ds 72 movw %ax, %es 73 movw %ax, %fs 74 movw %ax, %gs 75 76 lidtl .Lwakeup_idt 77 78 /* Clear the EFLAGS */ 79 pushl $0 80 popfl 81 82 /* Check header signature... */ 83 movl signature, %eax 84 cmpl $WAKEUP_HEADER_SIGNATURE, %eax 85 jne bogus_real_magic 86 87 /* Check we really have everything... */ 88 movl end_signature, %eax 89 cmpl $REALMODE_END_SIGNATURE, %eax 90 jne bogus_real_magic 91 92 /* Call the C code */ 93 calll main 94 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 99 jnc 1f 100 101 movl pmode_misc_en, %eax 102 movl pmode_misc_en + 4, %edx 103 movl $MSR_IA32_MISC_ENABLE, %ecx 104 wrmsr 1051: 106 107 /* Do any other stuff... */ 108 109#ifndef CONFIG_64BIT 110 /* This could also be done in C code... */ 111 movl pmode_cr3, %eax 112 movl %eax, %cr3 113 114 btl $WAKEUP_BEHAVIOR_RESTORE_CR4, %edi 115 jnc 1f 116 movl pmode_cr4, %eax 117 movl %eax, %cr4 1181: 119 btl $WAKEUP_BEHAVIOR_RESTORE_EFER, %edi 120 jnc 1f 121 movl pmode_efer, %eax 122 movl pmode_efer + 4, %edx 123 movl $MSR_EFER, %ecx 124 wrmsr 1251: 126 127 lgdtl pmode_gdt 128 129 /* This really couldn't... */ 130 movl pmode_entry, %eax 131 movl pmode_cr0, %ecx 132 movl %ecx, %cr0 133 ljmpl $__KERNEL_CS, $pa_startup_32 134 /* -> jmp *%eax in trampoline_32.S */ 135#else 136 jmp trampoline_start 137#endif 138SYM_CODE_END(wakeup_start) 139 140bogus_real_magic: 1411: 142 hlt 143 jmp 1b 144 145 .section ".rodata","a" 146 147 /* 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. 153 */ 154 155 .balign 16 156SYM_DATA_START(wakeup_gdt) 157 .word 3*8-1 /* Self-descriptor */ 158 .long pa_wakeup_gdt 159 .word 0 160 161 .word 0xffff /* 16-bit code segment @ real_mode_base */ 162 .long 0x9b000000 + pa_real_mode_base 163 .word 0x008f /* big real mode */ 164 165 .word 0xffff /* 16-bit data segment @ real_mode_base */ 166 .long 0x93000000 + pa_real_mode_base 167 .word 0x008f /* big real mode */ 168SYM_DATA_END(wakeup_gdt) 169 170 .section ".rodata","a" 171 .balign 8 172 173 /* This is the standard real-mode IDT */ 174 .balign 16 175SYM_DATA_START_LOCAL(.Lwakeup_idt) 176 .word 0xffff /* limit */ 177 .long 0 /* address */ 178 .word 0 179SYM_DATA_END(.Lwakeup_idt) 180