1 /* $NetBSD: acpi_wakeup.c,v 1.14 2009/03/27 19:53:19 drochner Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Takuya SHIOZAKI. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: acpi_wakeup.c,v 1.14 2009/03/27 19:53:19 drochner Exp $"); 34 35 /*- 36 * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org> 37 * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki@jp.freebsd.org> 38 * All rights reserved. 39 * 40 * Redistribution and use in source and binary forms, with or without 41 * modification, are permitted provided that the following conditions 42 * are met: 43 * 1. Redistributions of source code must retain the above copyright 44 * notice, this list of conditions and the following disclaimer. 45 * 2. Redistributions in binary form must reproduce the above copyright 46 * notice, this list of conditions and the following disclaimer in the 47 * documentation and/or other materials provided with the distribution. 48 * 49 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 52 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 59 * SUCH DAMAGE. 60 * 61 * FreeBSD: src/sys/i386/acpica/acpi_wakeup.c,v 1.9 2002/01/10 03:26:46 wes Exp 62 */ 63 64 #include <sys/param.h> 65 #include <sys/systm.h> 66 #include <sys/kernel.h> 67 #include <machine/bus.h> 68 #include <sys/proc.h> 69 #include <sys/sysctl.h> 70 71 #include <uvm/uvm_extern.h> 72 #include <uvm/uvm_page.h> 73 74 #ifdef __i386__ 75 #include "opt_mtrr.h" 76 #endif 77 #include "ioapic.h" 78 #include "lapic.h" 79 80 #if NLAPIC > 0 81 #include <machine/i82489var.h> 82 #endif 83 #if NIOAPIC > 0 84 #include <machine/i82093var.h> 85 #endif 86 #include <machine/i8259.h> 87 88 #include "acpi.h" 89 90 #include <dev/ic/i8253reg.h> 91 #include <dev/acpi/acpica.h> 92 #include <dev/acpi/acpivar.h> 93 #define ACPI_MACHDEP_PRIVATE 94 #include <machine/acpi_machdep.h> 95 #include <machine/cpu.h> 96 #ifdef __i386__ 97 # include <machine/npx.h> 98 #else 99 # include <machine/fpu.h> 100 #endif 101 #include <machine/mtrr.h> 102 103 #include <x86/cpuvar.h> 104 #include <x86/x86/tsc.h> 105 106 #include "acpi_wakecode.h" 107 108 /* Address is also hard-coded in acpi_wakecode.S */ 109 static paddr_t acpi_wakeup_paddr = 3 * PAGE_SIZE; 110 static vaddr_t acpi_wakeup_vaddr; 111 112 static int acpi_md_node = CTL_EOL; 113 int acpi_md_vbios_reset = 1; /* Referenced by dev/pci/vga_pci.c */ 114 static int acpi_md_beep_on_reset = 0; 115 116 static int sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS); 117 static int sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS); 118 119 /* Implemented in acpi_wakeup_low.S. */ 120 int acpi_md_sleep_prepare(int); 121 int acpi_md_sleep_exit(int); 122 /* Referenced by acpi_wakeup_low.S. */ 123 void acpi_md_sleep_enter(int); 124 125 #ifdef MULTIPROCESSOR 126 /* Referenced in ipifuncs.c. */ 127 void acpi_cpu_sleep(struct cpu_info *); 128 #endif 129 130 static void 131 acpi_md_sleep_patch(struct cpu_info *ci) 132 { 133 #define WAKECODE_FIXUP(offset, type, val) do { \ 134 type *addr; \ 135 addr = (type *)(acpi_wakeup_vaddr + offset); \ 136 *addr = val; \ 137 } while (0) 138 139 #define WAKECODE_BCOPY(offset, type, val) do { \ 140 void **addr; \ 141 addr = (void **)(acpi_wakeup_vaddr + offset); \ 142 memcpy( addr, &(val), sizeof(type)); \ 143 } while (0) 144 145 paddr_t tmp_pdir; 146 147 tmp_pdir = pmap_init_tmp_pgtbl(acpi_wakeup_paddr); 148 149 /* Execute Sleep */ 150 memcpy( (void *)acpi_wakeup_vaddr, wakecode, sizeof(wakecode)); 151 152 if (CPU_IS_PRIMARY(ci)) { 153 WAKECODE_FIXUP(WAKEUP_vbios_reset, uint8_t, acpi_md_vbios_reset); 154 WAKECODE_FIXUP(WAKEUP_beep_on_reset, uint8_t, acpi_md_beep_on_reset); 155 } else { 156 WAKECODE_FIXUP(WAKEUP_vbios_reset, uint8_t, 0); 157 WAKECODE_FIXUP(WAKEUP_beep_on_reset, uint8_t, 0); 158 } 159 160 #ifdef __i386__ 161 WAKECODE_FIXUP(WAKEUP_r_cr4, uint32_t, ci->ci_suspend_cr4); 162 #else 163 WAKECODE_FIXUP(WAKEUP_efer, uint32_t, ci->ci_suspend_efer); 164 #endif 165 166 WAKECODE_FIXUP(WAKEUP_curcpu, void *, ci); 167 #ifdef __i386__ 168 WAKECODE_FIXUP(WAKEUP_r_cr3, uint32_t, tmp_pdir); 169 #else 170 WAKECODE_FIXUP(WAKEUP_r_cr3, uint64_t, tmp_pdir); 171 #endif 172 WAKECODE_FIXUP(WAKEUP_restorecpu, void *, acpi_md_sleep_exit); 173 #undef WAKECODE_FIXUP 174 #undef WAKECODE_BCOPY 175 } 176 177 /* 178 * S4 sleep using S4BIOS support, from FreeBSD. 179 * 180 * FreeBSD: src/sys/dev/acpica/acpica_support.c,v 1.4 2002/03/12 09:45:17 dfr Exp 181 */ 182 183 static ACPI_STATUS 184 enter_s4_with_bios(void) 185 { 186 ACPI_OBJECT_LIST ArgList; 187 ACPI_OBJECT Arg; 188 UINT32 ret; 189 ACPI_STATUS status; 190 191 /* run the _PTS and _GTS methods */ 192 193 ACPI_MEMSET(&ArgList, 0, sizeof(ArgList)); 194 ArgList.Count = 1; 195 ArgList.Pointer = &Arg; 196 197 ACPI_MEMSET(&Arg, 0, sizeof(Arg)); 198 Arg.Type = ACPI_TYPE_INTEGER; 199 Arg.Integer.Value = ACPI_STATE_S4; 200 201 AcpiEvaluateObject(NULL, "\\_PTS", &ArgList, NULL); 202 AcpiEvaluateObject(NULL, "\\_GTS", &ArgList, NULL); 203 204 /* clear wake status */ 205 206 AcpiSetRegister(ACPI_BITREG_WAKE_STATUS, 1); 207 208 AcpiHwDisableAllGpes(); 209 AcpiHwEnableAllWakeupGpes(); 210 211 /* flush caches */ 212 213 ACPI_FLUSH_CPU_CACHE(); 214 215 /* 216 * write the value to command port and wait until we enter sleep state 217 */ 218 do { 219 AcpiOsStall(1000000); 220 AcpiOsWritePort(AcpiGbl_FADT.SmiCommand, 221 AcpiGbl_FADT.S4BiosRequest, 8); 222 status = AcpiGetRegister(ACPI_BITREG_WAKE_STATUS, &ret); 223 if (ACPI_FAILURE(status)) 224 break; 225 } while (!ret); 226 227 AcpiHwDisableAllGpes(); 228 AcpiHwEnableAllRuntimeGpes(); 229 230 return (AE_OK); 231 } 232 233 void 234 acpi_md_sleep_enter(int state) 235 { 236 ACPI_STATUS status; 237 struct cpu_info *ci; 238 239 ci = curcpu(); 240 241 #ifdef MULTIPROCESSOR 242 if (!CPU_IS_PRIMARY(ci)) { 243 atomic_and_32(&ci->ci_flags, ~CPUF_RUNNING); 244 atomic_and_32(&cpus_running, ~ci->ci_cpumask); 245 246 ACPI_FLUSH_CPU_CACHE(); 247 248 for (;;) 249 x86_hlt(); 250 } 251 #endif 252 253 acpi_md_sleep_patch(ci); 254 255 ACPI_FLUSH_CPU_CACHE(); 256 257 if (state == ACPI_STATE_S4) { 258 ACPI_TABLE_FACS *facs; 259 status = AcpiGetTable(ACPI_SIG_FACS, 0, (ACPI_TABLE_HEADER **)&facs); 260 if (ACPI_FAILURE(status)) { 261 printf("acpi: S4BIOS not supported: cannot load FACS\n"); 262 return; 263 } 264 if (facs == NULL || 265 (facs->Flags & ACPI_FACS_S4_BIOS_PRESENT) == 0) { 266 printf("acpi: S4BIOS not supported: not present"); 267 return; 268 } 269 status = enter_s4_with_bios(); 270 } else { 271 status = AcpiEnterSleepState(state); 272 } 273 274 if (ACPI_FAILURE(status)) { 275 printf("acpi: AcpiEnterSleepState failed: %s\n", 276 AcpiFormatException(status)); 277 return; 278 } 279 280 for (;;) 281 x86_hlt(); 282 } 283 284 #ifdef MULTIPROCESSOR 285 void 286 acpi_cpu_sleep(struct cpu_info *ci) 287 { 288 KASSERT(!CPU_IS_PRIMARY(ci)); 289 KASSERT(ci == curcpu()); 290 291 x86_disable_intr(); 292 293 if (acpi_md_sleep_prepare(-1)) 294 return; 295 296 /* Execute Wakeup */ 297 #ifdef __i386__ 298 npxinit(ci); 299 #else 300 cpu_init_msrs(ci, false); 301 fpuinit(ci); 302 #endif 303 #if NLAPIC > 0 304 lapic_enable(); 305 lapic_set_lvt(); 306 lapic_initclocks(); 307 #endif 308 309 atomic_or_32(&ci->ci_flags, CPUF_RUNNING); 310 atomic_or_32(&cpus_running, ci->ci_cpumask); 311 tsc_sync_ap(ci); 312 313 x86_enable_intr(); 314 } 315 #endif 316 317 int 318 acpi_md_sleep(int state) 319 { 320 int s, ret = 0; 321 #ifdef MULTIPROCESSOR 322 struct cpu_info *ci; 323 CPU_INFO_ITERATOR cii; 324 #endif 325 326 KASSERT(acpi_wakeup_paddr != 0); 327 KASSERT(sizeof(wakecode) <= PAGE_SIZE); 328 329 if (!CPU_IS_PRIMARY(curcpu())) { 330 printf("acpi0: WARNING: ignoring sleep from secondary CPU\n"); 331 return -1; 332 } 333 334 AcpiSetFirmwareWakingVector(acpi_wakeup_paddr); 335 336 s = splhigh(); 337 #ifdef __i386__ 338 npxsave_cpu(true); 339 #else 340 fpusave_cpu(true); 341 #endif 342 x86_disable_intr(); 343 344 #ifdef MULTIPROCESSOR 345 /* Save and suspend Application Processors */ 346 x86_broadcast_ipi(X86_IPI_ACPI_CPU_SLEEP); 347 while (cpus_running != curcpu()->ci_cpumask) 348 delay(1); 349 #endif 350 351 if (acpi_md_sleep_prepare(state)) 352 goto out; 353 354 /* Execute Wakeup */ 355 #ifdef __i386__ 356 npxinit(&cpu_info_primary); 357 #else 358 cpu_init_msrs(&cpu_info_primary, false); 359 fpuinit(&cpu_info_primary); 360 #endif 361 i8259_reinit(); 362 #if NLAPIC > 0 363 lapic_enable(); 364 lapic_set_lvt(); 365 lapic_initclocks(); 366 #endif 367 #if NIOAPIC > 0 368 ioapic_reenable(); 369 #endif 370 371 initrtclock(TIMER_FREQ); 372 inittodr(time_second); 373 374 AcpiClearEvent(ACPI_EVENT_PMTIMER); 375 AcpiClearEvent(ACPI_EVENT_GLOBAL); 376 AcpiClearEvent(ACPI_EVENT_POWER_BUTTON); 377 AcpiClearEvent(ACPI_EVENT_SLEEP_BUTTON); 378 AcpiClearEvent(ACPI_EVENT_RTC); 379 AcpiHwDisableAllGpes (); 380 381 acpi_pci_link_resume(); 382 383 out: 384 385 #ifdef MULTIPROCESSOR 386 for (CPU_INFO_FOREACH(cii, ci)) { 387 if (CPU_IS_PRIMARY(ci)) 388 continue; 389 acpi_md_sleep_patch(ci); 390 391 CPU_STARTUP(ci, acpi_wakeup_paddr); 392 CPU_START_CLEANUP(ci); 393 394 while ((ci->ci_flags & CPUF_RUNNING) == 0) 395 x86_pause(); 396 397 tsc_sync_bp(ci); 398 } 399 #endif 400 401 x86_enable_intr(); 402 splx(s); 403 404 #ifdef MTRR 405 if (mtrr_funcs != NULL) 406 mtrr_commit(); 407 #endif 408 409 return (ret); 410 } 411 412 void 413 acpi_md_sleep_init(void) 414 { 415 /* Map ACPI wakecode */ 416 acpi_wakeup_vaddr = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, 417 UVM_KMF_VAONLY); 418 if (acpi_wakeup_vaddr == 0) 419 panic("acpi: can't allocate address for wakecode.\n"); 420 421 pmap_kenter_pa(acpi_wakeup_vaddr, acpi_wakeup_paddr, 422 VM_PROT_READ | VM_PROT_WRITE); 423 pmap_update(pmap_kernel()); 424 } 425 426 SYSCTL_SETUP(sysctl_md_acpi_setup, "acpi x86 sysctl setup") 427 { 428 const struct sysctlnode *node; 429 const struct sysctlnode *ssnode; 430 431 if (sysctl_createv(NULL, 0, NULL, &node, CTLFLAG_PERMANENT, 432 CTLTYPE_NODE, "machdep", NULL, NULL, 0, NULL, 0, CTL_MACHDEP, 433 CTL_EOL) != 0) 434 return; 435 if (sysctl_createv(NULL, 0, &node, &ssnode, CTLFLAG_READWRITE, 436 CTLTYPE_INT, "acpi_vbios_reset", NULL, sysctl_md_acpi_vbios_reset, 437 0, NULL, 0, CTL_CREATE, CTL_EOL) != 0) 438 return; 439 if (sysctl_createv(NULL, 0, &node, &ssnode, CTLFLAG_READWRITE, 440 CTLTYPE_INT, "acpi_beep_on_reset", NULL, sysctl_md_acpi_beep_on_reset, 441 0, NULL, 0, CTL_CREATE, CTL_EOL) != 0) 442 return; 443 444 acpi_md_node = node->sysctl_num; 445 } 446 447 static int 448 sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS) 449 { 450 int error, t; 451 struct sysctlnode node; 452 453 node = *rnode; 454 t = acpi_md_vbios_reset; 455 node.sysctl_data = &t; 456 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 457 if (error || newp == NULL) 458 return error; 459 460 if (t < 0 || t > 2) 461 return EINVAL; 462 463 acpi_md_vbios_reset = t; 464 465 return 0; 466 } 467 468 static int 469 sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS) 470 { 471 int error, t; 472 struct sysctlnode node; 473 474 node = *rnode; 475 t = acpi_md_beep_on_reset; 476 node.sysctl_data = &t; 477 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 478 if (error || newp == NULL) 479 return error; 480 481 if (t < 0 || t > 1) 482 return EINVAL; 483 484 acpi_md_beep_on_reset = t; 485 486 return 0; 487 } 488