diff options
Diffstat (limited to 'arch/x86/kernel/acpi/realmode/wakeup.S')
-rw-r--r-- | arch/x86/kernel/acpi/realmode/wakeup.S | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/arch/x86/kernel/acpi/realmode/wakeup.S b/arch/x86/kernel/acpi/realmode/wakeup.S new file mode 100644 index 00000000000..b4fd836e405 --- /dev/null +++ b/arch/x86/kernel/acpi/realmode/wakeup.S | |||
@@ -0,0 +1,170 @@ | |||
1 | /* | ||
2 | * ACPI wakeup real mode startup stub | ||
3 | */ | ||
4 | #include <asm/segment.h> | ||
5 | #include <asm/msr-index.h> | ||
6 | #include <asm/page_types.h> | ||
7 | #include <asm/pgtable_types.h> | ||
8 | #include <asm/processor-flags.h> | ||
9 | #include "wakeup.h" | ||
10 | |||
11 | .code16 | ||
12 | .section ".jump", "ax" | ||
13 | .globl _start | ||
14 | _start: | ||
15 | cli | ||
16 | jmp wakeup_code | ||
17 | |||
18 | /* This should match the structure in wakeup.h */ | ||
19 | .section ".header", "a" | ||
20 | .globl wakeup_header | ||
21 | wakeup_header: | ||
22 | video_mode: .short 0 /* Video mode number */ | ||
23 | pmode_return: .byte 0x66, 0xea /* ljmpl */ | ||
24 | .long 0 /* offset goes here */ | ||
25 | .short __KERNEL_CS | ||
26 | pmode_cr0: .long 0 /* Saved %cr0 */ | ||
27 | pmode_cr3: .long 0 /* Saved %cr3 */ | ||
28 | pmode_cr4: .long 0 /* Saved %cr4 */ | ||
29 | pmode_efer: .quad 0 /* Saved EFER */ | ||
30 | pmode_gdt: .quad 0 | ||
31 | pmode_misc_en: .quad 0 /* Saved MISC_ENABLE MSR */ | ||
32 | pmode_behavior: .long 0 /* Wakeup behavior flags */ | ||
33 | realmode_flags: .long 0 | ||
34 | real_magic: .long 0 | ||
35 | trampoline_segment: .word 0 | ||
36 | _pad1: .byte 0 | ||
37 | wakeup_jmp: .byte 0xea /* ljmpw */ | ||
38 | wakeup_jmp_off: .word 3f | ||
39 | wakeup_jmp_seg: .word 0 | ||
40 | wakeup_gdt: .quad 0, 0, 0 | ||
41 | signature: .long WAKEUP_HEADER_SIGNATURE | ||
42 | |||
43 | .text | ||
44 | .code16 | ||
45 | wakeup_code: | ||
46 | cld | ||
47 | |||
48 | /* Apparently some dimwit BIOS programmers don't know how to | ||
49 | program a PM to RM transition, and we might end up here with | ||
50 | junk in the data segment descriptor registers. The only way | ||
51 | to repair that is to go into PM and fix it ourselves... */ | ||
52 | movw $16, %cx | ||
53 | lgdtl %cs:wakeup_gdt | ||
54 | movl %cr0, %eax | ||
55 | orb $X86_CR0_PE, %al | ||
56 | movl %eax, %cr0 | ||
57 | jmp 1f | ||
58 | 1: ljmpw $8, $2f | ||
59 | 2: | ||
60 | movw %cx, %ds | ||
61 | movw %cx, %es | ||
62 | movw %cx, %ss | ||
63 | movw %cx, %fs | ||
64 | movw %cx, %gs | ||
65 | |||
66 | andb $~X86_CR0_PE, %al | ||
67 | movl %eax, %cr0 | ||
68 | jmp wakeup_jmp | ||
69 | 3: | ||
70 | /* Set up segments */ | ||
71 | movw %cs, %ax | ||
72 | movw %ax, %ds | ||
73 | movw %ax, %es | ||
74 | movw %ax, %ss | ||
75 | lidtl wakeup_idt | ||
76 | |||
77 | movl $wakeup_stack_end, %esp | ||
78 | |||
79 | /* Clear the EFLAGS */ | ||
80 | pushl $0 | ||
81 | popfl | ||
82 | |||
83 | /* Check header signature... */ | ||
84 | movl signature, %eax | ||
85 | cmpl $WAKEUP_HEADER_SIGNATURE, %eax | ||
86 | jne bogus_real_magic | ||
87 | |||
88 | /* Check we really have everything... */ | ||
89 | movl end_signature, %eax | ||
90 | cmpl $WAKEUP_END_SIGNATURE, %eax | ||
91 | jne bogus_real_magic | ||
92 | |||
93 | /* Call the C code */ | ||
94 | calll main | ||
95 | |||
96 | /* Restore MISC_ENABLE before entering protected mode, in case | ||
97 | BIOS decided to clear XD_DISABLE during S3. */ | ||
98 | movl pmode_behavior, %eax | ||
99 | btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %eax | ||
100 | jnc 1f | ||
101 | |||
102 | movl pmode_misc_en, %eax | ||
103 | movl pmode_misc_en + 4, %edx | ||
104 | movl $MSR_IA32_MISC_ENABLE, %ecx | ||
105 | wrmsr | ||
106 | 1: | ||
107 | |||
108 | /* Do any other stuff... */ | ||
109 | |||
110 | #ifndef CONFIG_64BIT | ||
111 | /* This could also be done in C code... */ | ||
112 | movl pmode_cr3, %eax | ||
113 | movl %eax, %cr3 | ||
114 | |||
115 | movl pmode_cr4, %ecx | ||
116 | jecxz 1f | ||
117 | movl %ecx, %cr4 | ||
118 | 1: | ||
119 | movl pmode_efer, %eax | ||
120 | movl pmode_efer + 4, %edx | ||
121 | movl %eax, %ecx | ||
122 | orl %edx, %ecx | ||
123 | jz 1f | ||
124 | movl $MSR_EFER, %ecx | ||
125 | wrmsr | ||
126 | 1: | ||
127 | |||
128 | lgdtl pmode_gdt | ||
129 | |||
130 | /* This really couldn't... */ | ||
131 | movl pmode_cr0, %eax | ||
132 | movl %eax, %cr0 | ||
133 | jmp pmode_return | ||
134 | #else | ||
135 | pushw $0 | ||
136 | pushw trampoline_segment | ||
137 | pushw $0 | ||
138 | lret | ||
139 | #endif | ||
140 | |||
141 | bogus_real_magic: | ||
142 | 1: | ||
143 | hlt | ||
144 | jmp 1b | ||
145 | |||
146 | .data | ||
147 | .balign 8 | ||
148 | |||
149 | /* This is the standard real-mode IDT */ | ||
150 | wakeup_idt: | ||
151 | .word 0xffff /* limit */ | ||
152 | .long 0 /* address */ | ||
153 | .word 0 | ||
154 | |||
155 | .globl HEAP, heap_end | ||
156 | HEAP: | ||
157 | .long wakeup_heap | ||
158 | heap_end: | ||
159 | .long wakeup_stack | ||
160 | |||
161 | .bss | ||
162 | wakeup_heap: | ||
163 | .space 2048 | ||
164 | wakeup_stack: | ||
165 | .space 2048 | ||
166 | wakeup_stack_end: | ||
167 | |||
168 | .section ".signature","a" | ||
169 | end_signature: | ||
170 | .long WAKEUP_END_SIGNATURE | ||