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