/* SPDX-License-Identifier: GPL-2.0 */1/*2* ACPI wakeup real mode startup stub3*/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"1213.code161415/* This should match the structure in wakeup.h */16.section ".data", "aw"1718.balign 1619SYM_DATA_START(wakeup_header)20video_mode: .short 0 /* Video mode number */21pmode_entry: .long 022pmode_cs: .short __KERNEL_CS23pmode_cr0: .long 0 /* Saved %cr0 */24pmode_cr3: .long 0 /* Saved %cr3 */25pmode_cr4: .long 0 /* Saved %cr4 */26pmode_efer: .quad 0 /* Saved EFER */27pmode_gdt: .quad 028pmode_misc_en: .quad 0 /* Saved MISC_ENABLE MSR */29pmode_behavior: .long 0 /* Wakeup behavior flags */30realmode_flags: .long 031real_magic: .long 032signature: .long WAKEUP_HEADER_SIGNATURE33SYM_DATA_END(wakeup_header)3435.text36.code163738.balign 1639SYM_CODE_START(wakeup_start)40cli41cld4243LJMPW_RM(3f)443:45/* Apparently some dimwit BIOS programmers don't know how to46program a PM to RM transition, and we might end up here with47junk in the data segment descriptor registers. The only way48to repair that is to go into PM and fix it ourselves... */49movw $16, %cx50lgdtl %cs:wakeup_gdt51movl %cr0, %eax52orb $X86_CR0_PE, %al53movl %eax, %cr054ljmpw $8, $2f552:56movw %cx, %ds57movw %cx, %es58movw %cx, %ss59movw %cx, %fs60movw %cx, %gs6162andb $~X86_CR0_PE, %al63movl %eax, %cr064LJMPW_RM(3f)653:66/* Set up segments */67movw %cs, %ax68movw %ax, %ss69movl $rm_stack_end, %esp70movw %ax, %ds71movw %ax, %es72movw %ax, %fs73movw %ax, %gs7475lidtl .Lwakeup_idt7677/* Clear the EFLAGS */78pushl $079popfl8081/* Check header signature... */82movl signature, %eax83cmpl $WAKEUP_HEADER_SIGNATURE, %eax84jne bogus_real_magic8586/* Check we really have everything... */87movl end_signature, %eax88cmpl $REALMODE_END_SIGNATURE, %eax89jne bogus_real_magic9091/* Call the C code */92calll main9394/* Restore MISC_ENABLE before entering protected mode, in case95BIOS decided to clear XD_DISABLE during S3. */96movl pmode_behavior, %edi97btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi98jnc 1f99100movl pmode_misc_en, %eax101movl pmode_misc_en + 4, %edx102movl $MSR_IA32_MISC_ENABLE, %ecx103wrmsr1041:105106/* Do any other stuff... */107108#ifndef CONFIG_64BIT109/* This could also be done in C code... */110movl pmode_cr3, %eax111movl %eax, %cr3112113btl $WAKEUP_BEHAVIOR_RESTORE_CR4, %edi114jnc 1f115movl pmode_cr4, %eax116movl %eax, %cr41171:118btl $WAKEUP_BEHAVIOR_RESTORE_EFER, %edi119jnc 1f120movl pmode_efer, %eax121movl pmode_efer + 4, %edx122movl $MSR_EFER, %ecx123wrmsr1241:125126lgdtl pmode_gdt127128/* This really couldn't... */129movl pmode_entry, %eax130movl pmode_cr0, %ecx131movl %ecx, %cr0132ljmpl $__KERNEL_CS, $pa_startup_32133/* -> jmp *%eax in trampoline_32.S */134#else135jmp trampoline_start136#endif137SYM_CODE_END(wakeup_start)138139bogus_real_magic:1401:141hlt142jmp 1b143144.section ".rodata","a"145146/*147* Set up the wakeup GDT. We set these up as Big Real Mode,148* that is, with limits set to 4 GB. At least the Lenovo149* Thinkpad X61 is known to need this for the video BIOS150* initialization quirk to work; this is likely to also151* be the case for other laptops or integrated video devices.152*/153154.balign 16155SYM_DATA_START(wakeup_gdt)156.word 3*8-1 /* Self-descriptor */157.long pa_wakeup_gdt158.word 0159160.word 0xffff /* 16-bit code segment @ real_mode_base */161.long 0x9b000000 + pa_real_mode_base162.word 0x008f /* big real mode */163164.word 0xffff /* 16-bit data segment @ real_mode_base */165.long 0x93000000 + pa_real_mode_base166.word 0x008f /* big real mode */167SYM_DATA_END(wakeup_gdt)168169.section ".rodata","a"170.balign 8171172/* This is the standard real-mode IDT */173.balign 16174SYM_DATA_START_LOCAL(.Lwakeup_idt)175.word 0xffff /* limit */176.long 0 /* address */177.word 0178SYM_DATA_END(.Lwakeup_idt)179180181