]> git.saurik.com Git - apple/xnu.git/blob - osfmk/i386/acpi_wakeup.s
xnu-792.6.56.tar.gz
[apple/xnu.git] / osfmk / i386 / acpi_wakeup.s
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <i386/asm.h>
25 #include <i386/proc_reg.h>
26 #include <i386/postcode.h>
27 #include <i386/acpi.h>
28 #include <assym.s>
29
30 .file "acpi_wakeup.s"
31
32 .text
33 .align 12 /* Page align for single bcopy_phys() */
34
35 #define LJMP(segment, address) \
36 .byte 0xea ;\
37 .long address - EXT(acpi_wake_start) ;\
38 .word segment
39
40 #define PA(addr) ((addr)-KERNELBASE)
41
42 /*
43 * acpi_wake_start
44 *
45 * The code from acpi_wake_start to acpi_wake_end is copied to
46 * memory below 1MB. The firmware waking vector is updated to
47 * point at acpi_wake_start in low memory before sleeping.
48 */
49
50 ENTRY(acpi_wake_start)
51 /*
52 * CPU woke up from sleep, and is back in real mode.
53 * Initialize it just enough to get back to protected mode.
54 */
55 cli
56
57 POSTCODE(ACPI_WAKE_START_ENTRY)
58
59 /* set up DS to match CS */
60 movw %cs, %ax
61 movw %ax, %ds
62
63 /*
64 * Must initialize GDTR before entering protected mode.
65 * Use a temporary GDT that is 0 based, 4GB limit, code and data.
66 * Restoring the actual GDT will come later.
67 */
68 addr16
69 data16
70 lgdt EXT(acpi_gdtr) - EXT(acpi_wake_start)
71
72 /* set CR0.PE to enter protected mode */
73 mov %cr0, %eax
74 data16
75 or $(CR0_PE), %eax
76 mov %eax, %cr0
77
78 /*
79 * Make intra-segment jump to flush pipeline and reload CS register.
80 * If GDT is bogus, it will blow up here.
81 */
82 data16
83 LJMP(0x8, acpi_wake_prot + ACPI_WAKE_ADDR)
84
85 acpi_wake_prot:
86
87 /* protected mode, paging disabled */
88
89 /* setup the protected mode segment registers */
90 mov $0x10, %eax
91 movw %ax, %ds
92 movw %ax, %es
93 movw %ax, %ss
94 movw %ax, %fs
95 movw %ax, %gs
96
97 /* jump back to the sleep function in the kernel */
98 movl PA(saved_eip), %eax
99 jmp *%eax
100
101 /* Segment Descriptor
102 *
103 * 31 24 19 16 7 0
104 * ------------------------------------------------------------
105 * | | |B| |A| | | |1|0|E|W|A| |
106 * | BASE 31..24 |G|/|0|V| LIMIT |P|DPL| TYPE | BASE 23:16 |
107 * | | |D| |L| 19..16| | |1|1|C|R|A| |
108 * ------------------------------------------------------------
109 * | | |
110 * | BASE 15..0 | LIMIT 15..0 |
111 * | | |
112 * ------------------------------------------------------------
113 */
114 ENTRY(acpi_gdt)
115 .word 0, 0 /* 0x0 : null */
116 .byte 0, 0, 0, 0
117
118 .word 0xffff, 0x0000 /* 0x8 : code */
119 .byte 0, 0x9e, 0xcf, 0
120
121 .word 0xffff, 0x0000 /* 0x10 : data */
122 .byte 0, 0x92, 0xcf, 0
123
124 ENTRY(acpi_gdtr)
125 .word 24 /* limit (8*3 segs) */
126 .long EXT(acpi_gdt) - EXT(acpi_wake_start) + ACPI_WAKE_ADDR
127
128 ENTRY(acpi_wake_end)
129
130
131 /*
132 * acpi_sleep_cpu(acpi_sleep_callback func, void * refcon)
133 *
134 * Save CPU state before platform sleep. Restore CPU state
135 * following wake up.
136 */
137
138 ENTRY(acpi_sleep_cpu)
139 pushl %ebp
140 movl %esp, %ebp
141
142 /* save flags */
143 pushfl
144
145 /* save general purpose registers */
146 pushal
147 movl %esp, saved_esp
148
149 /* save control registers */
150 movl %cr0, %eax
151 movl %eax, saved_cr0
152 movl %cr2, %eax
153 movl %eax, saved_cr2
154 movl %cr3, %eax
155 movl %eax, saved_cr3
156 movl %cr4, %eax
157 movl %eax, saved_cr4
158
159 /* save segment registers */
160 movw %es, saved_es
161 movw %fs, saved_fs
162 movw %gs, saved_gs
163 movw %ss, saved_ss
164
165 /* save descriptor table registers */
166 sgdt saved_gdt
167 sldt saved_ldt
168 sidt saved_idt
169 str saved_tr
170
171 /*
172 * When system wakes up, the real mode wake handler will revert to
173 * protected mode, then jump to the address stored at saved_eip.
174 */
175 movl $(PA(wake_prot)), saved_eip
176
177 /*
178 * Call ACPI function provided by the caller to sleep the platform.
179 * This call will not return on success.
180 */
181 pushl B_ARG1
182 movl B_ARG0, %edi
183 call *%edi
184 popl %edi
185
186 /* sleep failed, no cpu context lost */
187 jmp wake_restore
188
189 wake_prot:
190
191 /* protected mode, paging disabled */
192 POSTCODE(ACPI_WAKE_PROT_ENTRY)
193
194 /* restore kernel GDT */
195 lgdt PA(saved_gdt)
196
197 /* restore control registers */
198 movl PA(saved_cr2), %eax
199 movl %eax, %cr2
200
201 #ifdef PAE
202 movl PA(EXT(IdlePDPT)), %eax
203 movl (%eax), %esi /* save orig */
204 movl 24(%eax), %ebx
205 movl %ebx, (%eax) /* identity map low mem */
206 movl %eax, %cr3
207
208 movl PA(saved_cr4), %eax
209 movl %eax, %cr4
210 #else
211 movl PA(saved_cr4), %eax
212 movl %eax, %cr4
213
214 /*
215 * Temporarily use the page tables at IdlePTD
216 * to enable paging. Copy the KPTDI entry to
217 * entry 0 in the PTD to identity map the kernel.
218 */
219 movl PA(EXT(IdlePTD)), %eax
220 movl %eax, %ebx
221 addl $(KPTDI << PTEINDX), %ebx /* bytes per PDE */
222 movl (%ebx), %ebx /* IdlePTD[KPTDI] */
223 movl (%eax), %esi /* save original IdlePTD[0] */
224 movl %ebx, (%eax) /* update IdlePTD[0] */
225 movl %eax, %cr3 /* CR3 = IdlePTD */
226 #endif
227
228 /* restore CR0, paging enabled */
229 movl PA(saved_cr0), %eax
230 movl %eax, %cr0
231
232 /* switch to kernel code segment */
233 ljmpl $(KERNEL_CS), $wake_paged
234
235 wake_paged:
236
237 /* protected mode, paging enabled */
238 POSTCODE(ACPI_WAKE_PAGED_ENTRY)
239
240 /* switch to kernel data segment */
241 movw $(KERNEL_DS), %ax
242 movw %ax, %ds
243
244 /* undo changes to IdlePTD */
245 #ifdef PAE
246 movl EXT(IdlePDPT), %eax
247 #else
248 movl EXT(IdlePTD), %eax
249 #endif
250 addl $(KERNELBASE), %eax /* make virtual */
251 movl %esi, (%eax)
252
253 /* restore real PDE base */
254 movl saved_cr3, %eax
255 movl %eax, %cr3
256
257
258 /* restore local and interrupt descriptor tables */
259 lldt saved_ldt
260 lidt saved_idt
261
262 /* restore segment registers */
263 movw saved_es, %es
264 movw saved_fs, %fs
265 movw saved_gs, %gs
266 movw saved_ss, %ss
267
268 /*
269 * Restore task register. Before doing this, clear the busy flag
270 * in the TSS descriptor set by the CPU.
271 */
272 movl $saved_gdt, %eax
273 movl 2(%eax), %edx /* GDT base, skip limit word */
274 movl $(KERNEL_TSS), %eax /* TSS segment selector */
275 movb $(K_TSS), 5(%edx, %eax) /* clear busy flag */
276 ltr saved_tr /* restore TR */
277
278 wake_restore:
279
280 /* restore general purpose registers */
281 movl saved_esp, %esp
282 popal
283
284 /* restore flags */
285 popfl
286
287 leave
288 ret
289
290
291 .section __HIB, __text
292 .align 2
293
294 .globl EXT(acpi_wake_prot_entry)
295 ENTRY(acpi_wake_prot_entry)
296 /* protected mode, paging enabled */
297 POSTCODE(ACPI_WAKE_PAGED_ENTRY)
298
299 /* restore kernel GDT */
300 lgdt PA(saved_gdt)
301
302 POSTCODE(0x40)
303 /* restore control registers */
304 movl saved_cr2, %eax
305 movl %eax, %cr2
306
307 POSTCODE(0x3E)
308 /* switch to kernel data segment */
309 movw $(KERNEL_DS), %ax
310 movw %ax, %ds
311
312 POSTCODE(0x3D)
313 /* restore real PDE base */
314 movl saved_cr3, %eax
315 movl saved_cr4, %edx
316 movl %eax, %cr3
317 movl %edx, %cr4
318
319 POSTCODE(0x3C)
320 /* restore local and interrupt descriptor tables */
321 lldt saved_ldt
322 lidt saved_idt
323
324 POSTCODE(0x3B)
325 /* restore segment registers */
326 movw saved_es, %es
327 movw saved_fs, %fs
328 movw saved_gs, %gs
329 movw saved_ss, %ss
330
331 POSTCODE(0x3A)
332 /*
333 * Restore task register. Before doing this, clear the busy flag
334 * in the TSS descriptor set by the CPU.
335 */
336 movl $saved_gdt, %eax
337 movl 2(%eax), %edx /* GDT base, skip limit word */
338 movl $(KERNEL_TSS), %eax /* TSS segment selector */
339 movb $(K_TSS), 5(%edx, %eax) /* clear busy flag */
340 ltr saved_tr /* restore TR */
341
342 /* restore general purpose registers */
343 movl saved_esp, %esp
344 popal
345
346 /* restore flags */
347 popfl
348
349 /* make sure interrupts are disabled */
350 cli
351
352 movl $2, %eax
353
354 leave
355 ret
356
357
358 .data
359 .section __HIB, __data
360 .align 2
361
362
363 /*
364 * CPU registers saved across sleep/wake.
365 */
366 saved_esp: .long 0
367 saved_es: .word 0
368 saved_fs: .word 0
369 saved_gs: .word 0
370 saved_ss: .word 0
371 saved_cr0: .long 0
372 saved_cr2: .long 0
373 saved_cr3: .long 0
374 saved_cr4: .long 0
375 saved_gdt: .word 0
376 .long 0
377 saved_idt: .word 0
378 .long 0
379 saved_ldt: .word 0
380 saved_tr: .word 0
381 saved_eip: .long 0
382