mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
3.8 KiB
179 lines
3.8 KiB
/* SPDX-License-Identifier: GPL-2.0 */ |
|
/* |
|
* ACPI wakeup real mode startup stub |
|
*/ |
|
#include <linux/linkage.h> |
|
#include <asm/segment.h> |
|
#include <asm/msr-index.h> |
|
#include <asm/page_types.h> |
|
#include <asm/pgtable_types.h> |
|
#include <asm/processor-flags.h> |
|
#include "realmode.h" |
|
#include "wakeup.h" |
|
|
|
.code16 |
|
|
|
/* This should match the structure in wakeup.h */ |
|
.section ".data", "aw" |
|
|
|
.balign 16 |
|
SYM_DATA_START(wakeup_header) |
|
video_mode: .short 0 /* Video mode number */ |
|
pmode_entry: .long 0 |
|
pmode_cs: .short __KERNEL_CS |
|
pmode_cr0: .long 0 /* Saved %cr0 */ |
|
pmode_cr3: .long 0 /* Saved %cr3 */ |
|
pmode_cr4: .long 0 /* Saved %cr4 */ |
|
pmode_efer: .quad 0 /* Saved EFER */ |
|
pmode_gdt: .quad 0 |
|
pmode_misc_en: .quad 0 /* Saved MISC_ENABLE MSR */ |
|
pmode_behavior: .long 0 /* Wakeup behavior flags */ |
|
realmode_flags: .long 0 |
|
real_magic: .long 0 |
|
signature: .long WAKEUP_HEADER_SIGNATURE |
|
SYM_DATA_END(wakeup_header) |
|
|
|
.text |
|
.code16 |
|
|
|
.balign 16 |
|
SYM_CODE_START(wakeup_start) |
|
cli |
|
cld |
|
|
|
LJMPW_RM(3f) |
|
3: |
|
/* Apparently some dimwit BIOS programmers don't know how to |
|
program a PM to RM transition, and we might end up here with |
|
junk in the data segment descriptor registers. The only way |
|
to repair that is to go into PM and fix it ourselves... */ |
|
movw $16, %cx |
|
lgdtl %cs:wakeup_gdt |
|
movl %cr0, %eax |
|
orb $X86_CR0_PE, %al |
|
movl %eax, %cr0 |
|
ljmpw $8, $2f |
|
2: |
|
movw %cx, %ds |
|
movw %cx, %es |
|
movw %cx, %ss |
|
movw %cx, %fs |
|
movw %cx, %gs |
|
|
|
andb $~X86_CR0_PE, %al |
|
movl %eax, %cr0 |
|
LJMPW_RM(3f) |
|
3: |
|
/* Set up segments */ |
|
movw %cs, %ax |
|
movw %ax, %ss |
|
movl $rm_stack_end, %esp |
|
movw %ax, %ds |
|
movw %ax, %es |
|
movw %ax, %fs |
|
movw %ax, %gs |
|
|
|
lidtl .Lwakeup_idt |
|
|
|
/* Clear the EFLAGS */ |
|
pushl $0 |
|
popfl |
|
|
|
/* Check header signature... */ |
|
movl signature, %eax |
|
cmpl $WAKEUP_HEADER_SIGNATURE, %eax |
|
jne bogus_real_magic |
|
|
|
/* Check we really have everything... */ |
|
movl end_signature, %eax |
|
cmpl $REALMODE_END_SIGNATURE, %eax |
|
jne bogus_real_magic |
|
|
|
/* Call the C code */ |
|
calll main |
|
|
|
/* Restore MISC_ENABLE before entering protected mode, in case |
|
BIOS decided to clear XD_DISABLE during S3. */ |
|
movl pmode_behavior, %edi |
|
btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi |
|
jnc 1f |
|
|
|
movl pmode_misc_en, %eax |
|
movl pmode_misc_en + 4, %edx |
|
movl $MSR_IA32_MISC_ENABLE, %ecx |
|
wrmsr |
|
1: |
|
|
|
/* Do any other stuff... */ |
|
|
|
#ifndef CONFIG_64BIT |
|
/* This could also be done in C code... */ |
|
movl pmode_cr3, %eax |
|
movl %eax, %cr3 |
|
|
|
btl $WAKEUP_BEHAVIOR_RESTORE_CR4, %edi |
|
jnc 1f |
|
movl pmode_cr4, %eax |
|
movl %eax, %cr4 |
|
1: |
|
btl $WAKEUP_BEHAVIOR_RESTORE_EFER, %edi |
|
jnc 1f |
|
movl pmode_efer, %eax |
|
movl pmode_efer + 4, %edx |
|
movl $MSR_EFER, %ecx |
|
wrmsr |
|
1: |
|
|
|
lgdtl pmode_gdt |
|
|
|
/* This really couldn't... */ |
|
movl pmode_entry, %eax |
|
movl pmode_cr0, %ecx |
|
movl %ecx, %cr0 |
|
ljmpl $__KERNEL_CS, $pa_startup_32 |
|
/* -> jmp *%eax in trampoline_32.S */ |
|
#else |
|
jmp trampoline_start |
|
#endif |
|
SYM_CODE_END(wakeup_start) |
|
|
|
bogus_real_magic: |
|
1: |
|
hlt |
|
jmp 1b |
|
|
|
.section ".rodata","a" |
|
|
|
/* |
|
* Set up the wakeup GDT. We set these up as Big Real Mode, |
|
* that is, with limits set to 4 GB. At least the Lenovo |
|
* Thinkpad X61 is known to need this for the video BIOS |
|
* initialization quirk to work; this is likely to also |
|
* be the case for other laptops or integrated video devices. |
|
*/ |
|
|
|
.balign 16 |
|
SYM_DATA_START(wakeup_gdt) |
|
.word 3*8-1 /* Self-descriptor */ |
|
.long pa_wakeup_gdt |
|
.word 0 |
|
|
|
.word 0xffff /* 16-bit code segment @ real_mode_base */ |
|
.long 0x9b000000 + pa_real_mode_base |
|
.word 0x008f /* big real mode */ |
|
|
|
.word 0xffff /* 16-bit data segment @ real_mode_base */ |
|
.long 0x93000000 + pa_real_mode_base |
|
.word 0x008f /* big real mode */ |
|
SYM_DATA_END(wakeup_gdt) |
|
|
|
.section ".rodata","a" |
|
.balign 8 |
|
|
|
/* This is the standard real-mode IDT */ |
|
.balign 16 |
|
SYM_DATA_START_LOCAL(.Lwakeup_idt) |
|
.word 0xffff /* limit */ |
|
.long 0 /* address */ |
|
.word 0 |
|
SYM_DATA_END(.Lwakeup_idt)
|
|
|