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/misc_protos.h> 30#include <i386/cpu_data.h> 31#include <i386/proc_reg.h> 32#include <i386/pmap.h> 33#include <i386/mtrr.h> 34#include <i386/vmx/vmx_cpu.h> 35#include <i386/acpi.h> 36#include <i386/fpu.h> 37#include <i386/lapic.h> 38#include <i386/mp.h> 39#include <i386/mp_desc.h> 40#include <i386/serial_io.h> 41#include <i386/machine_check.h> 42#include <i386/pmCPU.h> 43 44#include <kern/cpu_data.h> 45#include <console/serial_protos.h> 46 47#if HIBERNATION 48#include <IOKit/IOHibernatePrivate.h> 49#endif 50#include <IOKit/IOPlatformExpert.h> 51 52extern void acpi_sleep_cpu(acpi_sleep_callback, void * refcon); 53extern char acpi_wake_start[]; 54extern char acpi_wake_end[]; 55 56extern void set_kbd_leds(int leds); 57 58extern void fpinit(void); 59 60vm_offset_t 61acpi_install_wake_handler(void) 62{ 63 /* copy wake code to ACPI_WAKE_ADDR in low memory */ 64 bcopy_phys(kvtophys((vm_offset_t)acpi_wake_start), 65 (addr64_t) ACPI_WAKE_ADDR, 66 acpi_wake_end - acpi_wake_start); 67 68 /* flush cache */ 69 wbinvd(); 70 71 /* return physical address of the wakeup code */ 72 return ACPI_WAKE_ADDR; 73} 74 75#if HIBERNATION 76struct acpi_hibernate_callback_data { 77 acpi_sleep_callback func; 78 void *refcon; 79}; 80typedef struct acpi_hibernate_callback_data acpi_hibernate_callback_data_t; 81 82static void 83acpi_hibernate(void *refcon) 84{ 85 uint32_t mode; 86 87 acpi_hibernate_callback_data_t *data = 88 (acpi_hibernate_callback_data_t *)refcon; 89 90 if (current_cpu_datap()->cpu_hibernate) 91 { 92 cpu_IA32e_enable(current_cpu_datap()); 93 94 mode = hibernate_write_image(); 95 96 if( mode == kIOHibernatePostWriteHalt ) 97 { 98 // off 99 HIBLOG("power off\n"); 100 if (PE_halt_restart) (*PE_halt_restart)(kPEHaltCPU); 101 } 102 else if( mode == kIOHibernatePostWriteRestart ) 103 { 104 // restart 105 HIBLOG("restart\n"); 106 if (PE_halt_restart) (*PE_halt_restart)(kPERestartCPU); 107 } 108 else 109 { 110 // sleep 111 HIBLOG("sleep\n"); 112 113 // should we come back via regular wake, set the state in memory. 114 cpu_datap(0)->cpu_hibernate = 0; 115 } 116 117 /* 118 * If we're in 64-bit mode, drop back into legacy mode during sleep. 119 */ 120 cpu_IA32e_disable(current_cpu_datap()); 121 122 } 123 124 (data->func)(data->refcon); 125 126 /* should never get here! */ 127} 128#endif 129 130static uint64_t acpi_sleep_abstime; 131 132void 133acpi_sleep_kernel(acpi_sleep_callback func, void *refcon) 134{ 135#if HIBERNATION 136 acpi_hibernate_callback_data_t data; 137 boolean_t did_hibernate; 138#endif 139 unsigned int cpu; 140 kern_return_t rc; 141 unsigned int my_cpu; 142 143 kprintf("acpi_sleep_kernel hib=%d\n", 144 current_cpu_datap()->cpu_hibernate); 145 146 /* Geta ll CPUs to be in the "off" state */ 147 my_cpu = cpu_number(); 148 for (cpu = 0; cpu < real_ncpus; cpu += 1) { 149 if (cpu == my_cpu) 150 continue; 151 rc = pmCPUExitHaltToOff(cpu); 152 if (rc != KERN_SUCCESS) 153 panic("Error %d trying to transition CPU %d to OFF", 154 rc, cpu); 155 } 156 157 /* shutdown local APIC before passing control to BIOS */ 158 lapic_shutdown(); 159 160#if HIBERNATION 161 data.func = func; 162 data.refcon = refcon; 163#endif 164 165 /* Save power management timer state */ 166 pmTimerSave(); 167 168 /* 169 * Turn off VT, otherwise switching to legacy mode will fail 170 */ 171 vmx_suspend(); 172 173 /* 174 * If we're in 64-bit mode, drop back into legacy mode during sleep. 175 */ 176 cpu_IA32e_disable(current_cpu_datap()); 177 178 acpi_sleep_abstime = mach_absolute_time(); 179 180 /* 181 * Save master CPU state and sleep platform. 182 * Will not return until platform is woken up, 183 * or if sleep failed. 184 */ 185#if HIBERNATION 186 acpi_sleep_cpu(acpi_hibernate, &data); 187#else 188 acpi_sleep_cpu(func, refcon); 189#endif 190 191 /* Reset UART if kprintf is enabled. 192 * However kprintf should not be used before rtc_sleep_wakeup() 193 * for compatibility with firewire kprintf. 194 */ 195 196 if (FALSE == disable_serial_output) 197 serial_init(); 198 199#if HIBERNATION 200 if (current_cpu_datap()->cpu_hibernate) { 201 int i; 202 for (i = 0; i < PMAP_NWINDOWS; i++) 203 *current_cpu_datap()->cpu_pmap->mapwindow[i].prv_CMAP = 0; 204 current_cpu_datap()->cpu_hibernate = 0; 205 did_hibernate = TRUE; 206 207 } else 208#endif 209 { 210 did_hibernate = FALSE; 211 } 212 213 /* Re-enable mode (including 64-bit if applicable) */ 214 cpu_mode_init(current_cpu_datap()); 215 216 /* Re-enable machine check handling */ 217 mca_cpu_init(); 218 219 /* restore MTRR settings */ 220 mtrr_update_cpu(); 221 222 /* 223 * Restore VT mode 224 */ 225 vmx_resume(); 226 227 /* set up PAT following boot processor power up */ 228 pat_init(); 229 230 /* 231 * Go through all of the CPUs and mark them as requiring 232 * a full restart. 233 */ 234 pmMarkAllCPUsOff(); 235 236 /* let the realtime clock reset */ 237 rtc_sleep_wakeup(acpi_sleep_abstime); 238 239 if (did_hibernate) 240 hibernate_machine_init(); 241 242 /* re-enable and re-init local apic */ 243 if (lapic_probe()) 244 lapic_configure(); 245 246 /* Restore power management register state */ 247 pmCPUMarkRunning(current_cpu_datap()); 248 249 /* Restore power management timer state */ 250 pmTimerRestore(); 251 252 /* Restart tick interrupts from the LAPIC timer */ 253 rtc_lapic_start_ticking(); 254 255 fpinit(); 256 clear_fpu(); 257 258#if HIBERNATION 259 if (did_hibernate) 260 enable_preemption(); 261 262 kprintf("ret from acpi_sleep_cpu hib=%d\n", did_hibernate); 263#endif 264} 265