]> git.saurik.com Git - apple/xnu.git/blob - osfmk/i386/acpi.c
4106c92831deea8b2f6b514224094ca3fe7320c8
[apple/xnu.git] / osfmk / i386 / acpi.c
1 /*
2 * Copyright (c) 2000-2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <i386/pmap.h>
30 #include <i386/proc_reg.h>
31 #include <i386/mp_desc.h>
32 #include <i386/misc_protos.h>
33 #include <i386/mp.h>
34 #include <i386/cpu_data.h>
35 #if CONFIG_MTRR
36 #include <i386/mtrr.h>
37 #endif
38 #if CONFIG_VMX
39 #include <i386/vmx/vmx_cpu.h>
40 #endif
41 #include <i386/ucode.h>
42 #include <i386/acpi.h>
43 #include <i386/fpu.h>
44 #include <i386/lapic.h>
45 #include <i386/mp.h>
46 #include <i386/mp_desc.h>
47 #include <i386/serial_io.h>
48 #if CONFIG_MCA
49 #include <i386/machine_check.h>
50 #endif
51 #include <i386/pmCPU.h>
52
53 #include <i386/tsc.h>
54
55 #include <kern/cpu_data.h>
56 #include <console/serial_protos.h>
57 #include <machine/pal_routines.h>
58 #include <vm/vm_page.h>
59
60 #if HIBERNATION
61 #include <IOKit/IOHibernatePrivate.h>
62 #endif
63 #include <IOKit/IOPlatformExpert.h>
64
65 #include <sys/kdebug.h>
66
67 #if CONFIG_SLEEP
68 extern void acpi_sleep_cpu(acpi_sleep_callback, void * refcon);
69 extern void acpi_wake_prot(void);
70 #endif
71 extern kern_return_t IOCPURunPlatformQuiesceActions(void);
72 extern kern_return_t IOCPURunPlatformActiveActions(void);
73
74 extern void fpinit(void);
75
76 vm_offset_t
77 acpi_install_wake_handler(void)
78 {
79 #if CONFIG_SLEEP
80 install_real_mode_bootstrap(acpi_wake_prot);
81 return REAL_MODE_BOOTSTRAP_OFFSET;
82 #else
83 return 0;
84 #endif
85 }
86
87 #if HIBERNATION
88 struct acpi_hibernate_callback_data {
89 acpi_sleep_callback func;
90 void *refcon;
91 };
92 typedef struct acpi_hibernate_callback_data acpi_hibernate_callback_data_t;
93
94 unsigned int save_kdebug_enable = 0;
95 static uint64_t acpi_sleep_abstime;
96
97 #if CONFIG_SLEEP
98 static void
99 acpi_hibernate(void *refcon)
100 {
101 uint32_t mode;
102
103 acpi_hibernate_callback_data_t *data =
104 (acpi_hibernate_callback_data_t *)refcon;
105
106 if (current_cpu_datap()->cpu_hibernate)
107 {
108 #if defined(__i386__)
109 cpu_IA32e_enable(current_cpu_datap());
110 #endif
111 mode = hibernate_write_image();
112
113 if( mode == kIOHibernatePostWriteHalt )
114 {
115 // off
116 HIBLOG("power off\n");
117 if (PE_halt_restart) (*PE_halt_restart)(kPEHaltCPU);
118 }
119 else if( mode == kIOHibernatePostWriteRestart )
120 {
121 // restart
122 HIBLOG("restart\n");
123 if (PE_halt_restart) (*PE_halt_restart)(kPERestartCPU);
124 }
125 else
126 {
127 // sleep
128 HIBLOG("sleep\n");
129
130 // should we come back via regular wake, set the state in memory.
131 cpu_datap(0)->cpu_hibernate = 0;
132 }
133
134 #if defined(__i386__)
135 /*
136 * If we're in 64-bit mode, drop back into legacy mode during sleep.
137 */
138 cpu_IA32e_disable(current_cpu_datap());
139 #endif
140 }
141 kdebug_enable = 0;
142
143 IOCPURunPlatformQuiesceActions();
144
145 acpi_sleep_abstime = mach_absolute_time();
146
147 (data->func)(data->refcon);
148
149 /* should never get here! */
150 }
151 #endif /* CONFIG_SLEEP */
152 #endif /* HIBERNATION */
153
154 extern void slave_pstart(void);
155
156
157 void
158 acpi_sleep_kernel(acpi_sleep_callback func, void *refcon)
159 {
160 #if HIBERNATION
161 acpi_hibernate_callback_data_t data;
162 #endif
163 boolean_t did_hibernate;
164 unsigned int cpu;
165 kern_return_t rc;
166 unsigned int my_cpu;
167 uint64_t now;
168 uint64_t my_tsc;
169 uint64_t my_abs;
170
171 kprintf("acpi_sleep_kernel hib=%d, cpu=%d\n",
172 current_cpu_datap()->cpu_hibernate, cpu_number());
173
174 /* Get all CPUs to be in the "off" state */
175 my_cpu = cpu_number();
176 for (cpu = 0; cpu < real_ncpus; cpu += 1) {
177 if (cpu == my_cpu)
178 continue;
179 rc = pmCPUExitHaltToOff(cpu);
180 if (rc != KERN_SUCCESS)
181 panic("Error %d trying to transition CPU %d to OFF",
182 rc, cpu);
183 }
184
185 /* shutdown local APIC before passing control to firmware */
186 lapic_shutdown();
187
188 #if HIBERNATION
189 data.func = func;
190 data.refcon = refcon;
191 #endif
192
193 /* Save power management timer state */
194 pmTimerSave();
195
196 #if CONFIG_VMX
197 /*
198 * Turn off VT, otherwise switching to legacy mode will fail
199 */
200 vmx_suspend();
201 #endif
202
203 #if defined(__i386__)
204 /*
205 * If we're in 64-bit mode, drop back into legacy mode during sleep.
206 */
207 cpu_IA32e_disable(current_cpu_datap());
208 #endif
209 /*
210 * Enable FPU/SIMD unit for potential hibernate acceleration
211 */
212 clear_ts();
213
214 KERNEL_DEBUG_CONSTANT(IOKDBG_CODE(DBG_HIBERNATE, 0) | DBG_FUNC_START, 0, 0, 0, 0, 0);
215
216 save_kdebug_enable = kdebug_enable;
217 kdebug_enable = 0;
218
219 acpi_sleep_abstime = mach_absolute_time();
220
221 #if CONFIG_SLEEP
222 /*
223 * Save master CPU state and sleep platform.
224 * Will not return until platform is woken up,
225 * or if sleep failed.
226 */
227 #ifdef __x86_64__
228 uint64_t old_cr3 = x86_64_pre_sleep();
229 #endif
230 #if HIBERNATION
231 acpi_sleep_cpu(acpi_hibernate, &data);
232 #else
233 acpi_sleep_cpu(func, refcon);
234 #endif
235
236 #ifdef __x86_64__
237 x86_64_post_sleep(old_cr3);
238 #endif
239
240 #endif /* CONFIG_SLEEP */
241
242 /* Reset UART if kprintf is enabled.
243 * However kprintf should not be used before rtc_sleep_wakeup()
244 * for compatibility with firewire kprintf.
245 */
246
247 if (FALSE == disable_serial_output)
248 pal_serial_init();
249
250 #if HIBERNATION
251 if (current_cpu_datap()->cpu_hibernate) {
252 #if defined(__i386__)
253 int i;
254 for (i = 0; i < PMAP_NWINDOWS; i++)
255 *current_cpu_datap()->cpu_pmap->mapwindow[i].prv_CMAP = 0;
256 #endif
257 did_hibernate = TRUE;
258
259 } else
260 #endif
261 {
262 did_hibernate = FALSE;
263 }
264
265 /* Re-enable mode (including 64-bit if applicable) */
266 cpu_mode_init(current_cpu_datap());
267
268 #if CONFIG_MCA
269 /* Re-enable machine check handling */
270 mca_cpu_init();
271 #endif
272
273 #if CONFIG_MTRR
274 /* restore MTRR settings */
275 mtrr_update_cpu();
276 #endif
277
278 /* update CPU microcode */
279 ucode_update_wake();
280
281 #if CONFIG_VMX
282 /*
283 * Restore VT mode
284 */
285 vmx_resume();
286 #endif
287
288 #if CONFIG_MTRR
289 /* set up PAT following boot processor power up */
290 pat_init();
291 #endif
292
293 /*
294 * Go through all of the CPUs and mark them as requiring
295 * a full restart.
296 */
297 pmMarkAllCPUsOff();
298
299 ml_get_timebase(&now);
300
301 /* re-enable and re-init local apic (prior to starting timers) */
302 if (lapic_probe())
303 lapic_configure();
304
305 /* let the realtime clock reset */
306 rtc_sleep_wakeup(acpi_sleep_abstime);
307
308 kdebug_enable = save_kdebug_enable;
309
310 IOCPURunPlatformActiveActions();
311
312 if (did_hibernate) {
313
314 my_tsc = (now >> 32) | (now << 32);
315 my_abs = tmrCvt(my_tsc, tscFCvtt2n);
316
317 KERNEL_DEBUG_CONSTANT(IOKDBG_CODE(DBG_HIBERNATE, 2) | DBG_FUNC_START,
318 (uint32_t)(my_abs >> 32), (uint32_t)my_abs, 0, 0, 0);
319 hibernate_machine_init();
320 KERNEL_DEBUG_CONSTANT(IOKDBG_CODE(DBG_HIBERNATE, 2) | DBG_FUNC_END, 0, 0, 0, 0, 0);
321
322 current_cpu_datap()->cpu_hibernate = 0;
323
324 KERNEL_DEBUG_CONSTANT(IOKDBG_CODE(DBG_HIBERNATE, 0) | DBG_FUNC_END, 0, 0, 0, 0, 0);
325 } else
326 KERNEL_DEBUG_CONSTANT(IOKDBG_CODE(DBG_HIBERNATE, 0) | DBG_FUNC_END, 0, 0, 0, 0, 0);
327
328 /* Restore power management register state */
329 pmCPUMarkRunning(current_cpu_datap());
330
331 /* Restore power management timer state */
332 pmTimerRestore();
333
334 /* Restart timer interrupts */
335 rtc_timer_start();
336
337 /* Reconfigure FP/SIMD unit */
338 init_fpu();
339
340 #if HIBERNATION
341 #ifdef __i386__
342 /* The image is written out using the copy engine, which disables
343 * preemption. Since the copy engine writes out the page which contains
344 * the preemption variable when it is disabled, we need to explicitly
345 * enable it here */
346 if (did_hibernate)
347 enable_preemption();
348 #endif
349
350 kprintf("ret from acpi_sleep_cpu hib=%d\n", did_hibernate);
351 #endif
352
353 #if CONFIG_SLEEP
354 /* Becase we don't save the bootstrap page, and we share it
355 * between sleep and mp slave init, we need to recreate it
356 * after coming back from sleep or hibernate */
357 install_real_mode_bootstrap(slave_pstart);
358 #endif
359 }
360
361 extern char real_mode_bootstrap_end[];
362 extern char real_mode_bootstrap_base[];
363
364 void
365 install_real_mode_bootstrap(void *prot_entry)
366 {
367 /*
368 * Copy the boot entry code to the real-mode vector area REAL_MODE_BOOTSTRAP_OFFSET.
369 * This is in page 1 which has been reserved for this purpose by
370 * machine_startup() from the boot processor.
371 * The slave boot code is responsible for switching to protected
372 * mode and then jumping to the common startup, _start().
373 */
374 bcopy_phys(kvtophys((vm_offset_t) real_mode_bootstrap_base),
375 (addr64_t) REAL_MODE_BOOTSTRAP_OFFSET,
376 real_mode_bootstrap_end-real_mode_bootstrap_base);
377
378 /*
379 * Set the location at the base of the stack to point to the
380 * common startup entry.
381 */
382 ml_phys_write_word(
383 PROT_MODE_START+REAL_MODE_BOOTSTRAP_OFFSET,
384 (unsigned int)kvtophys((vm_offset_t)prot_entry));
385
386 /* Flush caches */
387 __asm__("wbinvd");
388 }
389