2008-04-11 05:28:10 +08:00
|
|
|
/*
|
|
|
|
* ACPI wakeup real mode startup stub
|
|
|
|
*/
|
|
|
|
#include <asm/segment.h>
|
|
|
|
#include <asm/msr-index.h>
|
2009-02-14 03:14:01 +08:00
|
|
|
#include <asm/page_types.h>
|
|
|
|
#include <asm/pgtable_types.h>
|
2008-06-25 05:03:48 +08:00
|
|
|
#include <asm/processor-flags.h>
|
2008-04-11 05:28:10 +08:00
|
|
|
|
|
|
|
.code16
|
|
|
|
.section ".header", "a"
|
|
|
|
|
|
|
|
/* This should match the structure in wakeup.h */
|
|
|
|
.globl wakeup_header
|
|
|
|
wakeup_header:
|
|
|
|
video_mode: .short 0 /* Video mode number */
|
|
|
|
pmode_return: .byte 0x66, 0xea /* ljmpl */
|
|
|
|
.long 0 /* offset goes here */
|
|
|
|
.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
|
|
|
|
realmode_flags: .long 0
|
|
|
|
real_magic: .long 0
|
|
|
|
trampoline_segment: .word 0
|
2008-06-25 05:03:48 +08:00
|
|
|
_pad1: .byte 0
|
|
|
|
wakeup_jmp: .byte 0xea /* ljmpw */
|
|
|
|
wakeup_jmp_off: .word 3f
|
|
|
|
wakeup_jmp_seg: .word 0
|
|
|
|
wakeup_gdt: .quad 0, 0, 0
|
2008-04-11 05:28:10 +08:00
|
|
|
signature: .long 0x51ee1111
|
|
|
|
|
|
|
|
.text
|
|
|
|
.globl _start
|
|
|
|
.code16
|
|
|
|
wakeup_code:
|
|
|
|
_start:
|
|
|
|
cli
|
|
|
|
cld
|
|
|
|
|
2008-06-25 05:03:48 +08:00
|
|
|
/* 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
|
|
|
|
jmp 1f
|
|
|
|
1: 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
|
|
|
|
jmp wakeup_jmp
|
|
|
|
3:
|
2008-04-11 05:28:10 +08:00
|
|
|
/* Set up segments */
|
|
|
|
movw %cs, %ax
|
|
|
|
movw %ax, %ds
|
|
|
|
movw %ax, %es
|
|
|
|
movw %ax, %ss
|
2008-06-25 05:03:48 +08:00
|
|
|
lidtl wakeup_idt
|
2008-04-11 05:28:10 +08:00
|
|
|
|
|
|
|
movl $wakeup_stack_end, %esp
|
|
|
|
|
|
|
|
/* Clear the EFLAGS */
|
|
|
|
pushl $0
|
|
|
|
popfl
|
|
|
|
|
|
|
|
/* Check header signature... */
|
|
|
|
movl signature, %eax
|
|
|
|
cmpl $0x51ee1111, %eax
|
|
|
|
jne bogus_real_magic
|
|
|
|
|
|
|
|
/* Check we really have everything... */
|
|
|
|
movl end_signature, %eax
|
|
|
|
cmpl $0x65a22c82, %eax
|
|
|
|
jne bogus_real_magic
|
|
|
|
|
|
|
|
/* Call the C code */
|
|
|
|
calll main
|
|
|
|
|
|
|
|
/* Do any other stuff... */
|
|
|
|
|
|
|
|
#ifndef CONFIG_64BIT
|
|
|
|
/* This could also be done in C code... */
|
|
|
|
movl pmode_cr3, %eax
|
|
|
|
movl %eax, %cr3
|
|
|
|
|
|
|
|
movl pmode_cr4, %ecx
|
|
|
|
jecxz 1f
|
|
|
|
movl %ecx, %cr4
|
|
|
|
1:
|
|
|
|
movl pmode_efer, %eax
|
|
|
|
movl pmode_efer + 4, %edx
|
|
|
|
movl %eax, %ecx
|
|
|
|
orl %edx, %ecx
|
|
|
|
jz 1f
|
|
|
|
movl $0xc0000080, %ecx
|
|
|
|
wrmsr
|
|
|
|
1:
|
|
|
|
|
|
|
|
lgdtl pmode_gdt
|
|
|
|
|
|
|
|
/* This really couldn't... */
|
|
|
|
movl pmode_cr0, %eax
|
|
|
|
movl %eax, %cr0
|
|
|
|
jmp pmode_return
|
|
|
|
#else
|
|
|
|
pushw $0
|
|
|
|
pushw trampoline_segment
|
|
|
|
pushw $0
|
|
|
|
lret
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bogus_real_magic:
|
|
|
|
1:
|
|
|
|
hlt
|
|
|
|
jmp 1b
|
|
|
|
|
|
|
|
.data
|
2008-06-25 05:03:48 +08:00
|
|
|
.balign 8
|
|
|
|
|
|
|
|
/* This is the standard real-mode IDT */
|
|
|
|
wakeup_idt:
|
|
|
|
.word 0xffff /* limit */
|
|
|
|
.long 0 /* address */
|
|
|
|
.word 0
|
|
|
|
|
2008-04-11 05:28:10 +08:00
|
|
|
.globl HEAP, heap_end
|
|
|
|
HEAP:
|
|
|
|
.long wakeup_heap
|
|
|
|
heap_end:
|
|
|
|
.long wakeup_stack
|
|
|
|
|
|
|
|
.bss
|
|
|
|
wakeup_heap:
|
|
|
|
.space 2048
|
|
|
|
wakeup_stack:
|
|
|
|
.space 2048
|
|
|
|
wakeup_stack_end:
|