]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/acpi.c
xnu-1456.1.26.tar.gz
[apple/xnu.git] / osfmk / i386 / acpi.c
CommitLineData
91447636 1/*
e2fac8b1 2 * Copyright (c) 2000-2009 Apple Inc. All rights reserved.
91447636 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
91447636 5 *
2d21ac55
A
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.
8f6c56a5 14 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
91447636
A
27 */
28
b0d623f7
A
29#include <i386/pmap.h>
30#include <i386/proc_reg.h>
31#include <i386/mp_desc.h>
91447636 32#include <i386/misc_protos.h>
b0d623f7 33#include <i386/mp.h>
2d21ac55 34#include <i386/cpu_data.h>
91447636 35#include <i386/mtrr.h>
b0d623f7 36#if CONFIG_VMX
2d21ac55 37#include <i386/vmx/vmx_cpu.h>
b0d623f7 38#endif
91447636 39#include <i386/acpi.h>
0c530ab8 40#include <i386/fpu.h>
593a1d5f 41#include <i386/lapic.h>
91447636 42#include <i386/mp.h>
0c530ab8 43#include <i386/mp_desc.h>
2d21ac55 44#include <i386/serial_io.h>
b0d623f7 45#if CONFIG_MCA
0c530ab8 46#include <i386/machine_check.h>
b0d623f7 47#endif
593a1d5f 48#include <i386/pmCPU.h>
91447636
A
49
50#include <kern/cpu_data.h>
2d21ac55 51#include <console/serial_protos.h>
3a60a9f5 52
2d21ac55 53#if HIBERNATION
3a60a9f5 54#include <IOKit/IOHibernatePrivate.h>
2d21ac55 55#endif
91447636
A
56#include <IOKit/IOPlatformExpert.h>
57
b0d623f7 58#if CONFIG_SLEEP
91447636 59extern void acpi_sleep_cpu(acpi_sleep_callback, void * refcon);
b0d623f7
A
60extern void acpi_wake_prot(void);
61#endif
91447636 62
0c530ab8
A
63extern void fpinit(void);
64
91447636
A
65vm_offset_t
66acpi_install_wake_handler(void)
67{
b0d623f7
A
68#if CONFIG_SLEEP
69 install_real_mode_bootstrap(acpi_wake_prot);
70 return REAL_MODE_BOOTSTRAP_OFFSET;
71#else
72 return 0;
73#endif
91447636
A
74}
75
2d21ac55
A
76#if HIBERNATION
77struct acpi_hibernate_callback_data {
78 acpi_sleep_callback func;
79 void *refcon;
80};
81typedef struct acpi_hibernate_callback_data acpi_hibernate_callback_data_t;
91447636 82
b0d623f7 83#if CONFIG_SLEEP
91447636 84static void
3a60a9f5 85acpi_hibernate(void *refcon)
91447636 86{
2d21ac55 87 uint32_t mode;
3a60a9f5 88
2d21ac55
A
89 acpi_hibernate_callback_data_t *data =
90 (acpi_hibernate_callback_data_t *)refcon;
91447636 91
2d21ac55 92 if (current_cpu_datap()->cpu_hibernate)
0c530ab8 93 {
b0d623f7 94#if defined(__i386__)
2d21ac55 95 cpu_IA32e_enable(current_cpu_datap());
b0d623f7 96#endif
2d21ac55
A
97
98 mode = hibernate_write_image();
99
100 if( mode == kIOHibernatePostWriteHalt )
101 {
102 // off
103 HIBLOG("power off\n");
104 if (PE_halt_restart) (*PE_halt_restart)(kPEHaltCPU);
105 }
106 else if( mode == kIOHibernatePostWriteRestart )
107 {
108 // restart
109 HIBLOG("restart\n");
110 if (PE_halt_restart) (*PE_halt_restart)(kPERestartCPU);
111 }
112 else
113 {
114 // sleep
115 HIBLOG("sleep\n");
116
117 // should we come back via regular wake, set the state in memory.
118 cpu_datap(0)->cpu_hibernate = 0;
119 }
120
b0d623f7 121#if defined(__i386__)
2d21ac55
A
122 /*
123 * If we're in 64-bit mode, drop back into legacy mode during sleep.
124 */
125 cpu_IA32e_disable(current_cpu_datap());
b0d623f7 126#endif
0c530ab8 127 }
91447636 128
2d21ac55 129 (data->func)(data->refcon);
91447636 130
2d21ac55 131 /* should never get here! */
91447636 132}
b0d623f7
A
133#endif /* CONFIG_SLEEP */
134#endif /* HIBERNATION */
91447636 135
0c530ab8 136static uint64_t acpi_sleep_abstime;
b0d623f7 137extern void slave_pstart(void);
0c530ab8 138
91447636
A
139void
140acpi_sleep_kernel(acpi_sleep_callback func, void *refcon)
141{
2d21ac55
A
142#if HIBERNATION
143 acpi_hibernate_callback_data_t data;
2d21ac55 144#endif
b0d623f7 145 boolean_t did_hibernate;
e2fac8b1
A
146 unsigned int cpu;
147 kern_return_t rc;
148 unsigned int my_cpu;
91447636 149
2d21ac55
A
150 kprintf("acpi_sleep_kernel hib=%d\n",
151 current_cpu_datap()->cpu_hibernate);
0c530ab8 152
b0d623f7
A
153 /* Get all CPUs to be in the "off" state */
154 my_cpu = cpu_number();
e2fac8b1
A
155 for (cpu = 0; cpu < real_ncpus; cpu += 1) {
156 if (cpu == my_cpu)
157 continue;
158 rc = pmCPUExitHaltToOff(cpu);
159 if (rc != KERN_SUCCESS)
b0d623f7
A
160 panic("Error %d trying to transition CPU %d to OFF",
161 rc, cpu);
e2fac8b1
A
162 }
163
2d21ac55
A
164 /* shutdown local APIC before passing control to BIOS */
165 lapic_shutdown();
91447636 166
2d21ac55
A
167#if HIBERNATION
168 data.func = func;
169 data.refcon = refcon;
170#endif
91447636 171
593a1d5f
A
172 /* Save power management timer state */
173 pmTimerSave();
0c530ab8 174
b0d623f7 175#if CONFIG_VMX
2d21ac55
A
176 /*
177 * Turn off VT, otherwise switching to legacy mode will fail
178 */
179 vmx_suspend();
b0d623f7 180#endif
2d21ac55 181
b0d623f7 182#if defined(__i386__)
2d21ac55
A
183 /*
184 * If we're in 64-bit mode, drop back into legacy mode during sleep.
185 */
0c530ab8 186 cpu_IA32e_disable(current_cpu_datap());
b0d623f7 187#endif
0c530ab8
A
188
189 acpi_sleep_abstime = mach_absolute_time();
2d21ac55 190
b0d623f7 191#if CONFIG_SLEEP
2d21ac55
A
192 /*
193 * Save master CPU state and sleep platform.
194 * Will not return until platform is woken up,
195 * or if sleep failed.
196 */
b0d623f7
A
197#ifdef __x86_64__
198 uint64_t old_cr3 = x86_64_pre_sleep();
199#endif
2d21ac55
A
200#if HIBERNATION
201 acpi_sleep_cpu(acpi_hibernate, &data);
202#else
203 acpi_sleep_cpu(func, refcon);
204#endif
b0d623f7
A
205#ifdef __x86_64__
206 x86_64_post_sleep(old_cr3);
207#endif
208
209#endif /* CONFIG_SLEEP */
2d21ac55 210
4a3eedf9
A
211 /* Reset UART if kprintf is enabled.
212 * However kprintf should not be used before rtc_sleep_wakeup()
213 * for compatibility with firewire kprintf.
214 */
215
2d21ac55
A
216 if (FALSE == disable_serial_output)
217 serial_init();
218
219#if HIBERNATION
220 if (current_cpu_datap()->cpu_hibernate) {
b0d623f7 221#if defined(__i386__)
2d21ac55
A
222 int i;
223 for (i = 0; i < PMAP_NWINDOWS; i++)
224 *current_cpu_datap()->cpu_pmap->mapwindow[i].prv_CMAP = 0;
b0d623f7 225#endif
2d21ac55
A
226 did_hibernate = TRUE;
227
228 } else
229#endif
230 {
231 did_hibernate = FALSE;
0c530ab8 232 }
3a60a9f5 233
2d21ac55
A
234 /* Re-enable mode (including 64-bit if applicable) */
235 cpu_mode_init(current_cpu_datap());
3a60a9f5 236
b0d623f7 237#if CONFIG_MCA
2d21ac55
A
238 /* Re-enable machine check handling */
239 mca_cpu_init();
b0d623f7 240#endif
91447636 241
2d21ac55
A
242 /* restore MTRR settings */
243 mtrr_update_cpu();
0c530ab8 244
b0d623f7 245#if CONFIG_VMX
2d21ac55
A
246 /*
247 * Restore VT mode
248 */
249 vmx_resume();
b0d623f7 250#endif
0c530ab8 251
2d21ac55
A
252 /* set up PAT following boot processor power up */
253 pat_init();
91447636 254
593a1d5f
A
255 /*
256 * Go through all of the CPUs and mark them as requiring
257 * a full restart.
258 */
259 pmMarkAllCPUsOff();
260
0c530ab8
A
261 /* let the realtime clock reset */
262 rtc_sleep_wakeup(acpi_sleep_abstime);
91447636 263
b0d623f7 264 if (did_hibernate){
2d21ac55 265 hibernate_machine_init();
b0d623f7
A
266 current_cpu_datap()->cpu_hibernate = 0;
267 }
2d21ac55
A
268 /* re-enable and re-init local apic */
269 if (lapic_probe())
593a1d5f
A
270 lapic_configure();
271
272 /* Restore power management register state */
273 pmCPUMarkRunning(current_cpu_datap());
91447636 274
593a1d5f
A
275 /* Restore power management timer state */
276 pmTimerRestore();
0c530ab8
A
277
278 /* Restart tick interrupts from the LAPIC timer */
279 rtc_lapic_start_ticking();
280
2d21ac55
A
281 fpinit();
282 clear_fpu();
283
284#if HIBERNATION
b0d623f7
A
285#ifdef __i386__
286 /* The image is written out using the copy engine, which disables
287 * preemption. Since the copy engine writes out the page which contains
288 * the preemption variable when it is disabled, we need to explicitly
289 * enable it here */
2d21ac55
A
290 if (did_hibernate)
291 enable_preemption();
b0d623f7 292#endif
91447636 293
2d21ac55
A
294 kprintf("ret from acpi_sleep_cpu hib=%d\n", did_hibernate);
295#endif
b0d623f7
A
296
297#if CONFIG_SLEEP
298 /* Becase we don't save the bootstrap page, and we share it
299 * between sleep and mp slave init, we need to recreate it
300 * after coming back from sleep or hibernate */
301 install_real_mode_bootstrap(slave_pstart);
302#endif
91447636 303}
b0d623f7
A
304
305extern char real_mode_bootstrap_end[];
306extern char real_mode_bootstrap_base[];
307
308void
309install_real_mode_bootstrap(void *prot_entry)
310{
311 /*
312 * Copy the boot entry code to the real-mode vector area REAL_MODE_BOOTSTRAP_OFFSET.
313 * This is in page 1 which has been reserved for this purpose by
314 * machine_startup() from the boot processor.
315 * The slave boot code is responsible for switching to protected
316 * mode and then jumping to the common startup, _start().
317 */
318 bcopy_phys(kvtophys((vm_offset_t) real_mode_bootstrap_base),
319 (addr64_t) REAL_MODE_BOOTSTRAP_OFFSET,
320 real_mode_bootstrap_end-real_mode_bootstrap_base);
321
322 /*
323 * Set the location at the base of the stack to point to the
324 * common startup entry.
325 */
326 ml_phys_write_word(
327 PROT_MODE_START+REAL_MODE_BOOTSTRAP_OFFSET,
328 (unsigned int)kvtophys((vm_offset_t)prot_entry));
329
330 /* Flush caches */
331 __asm__("wbinvd");
332}
333