1 /* $NetBSD: acpi_wakeup.c,v 1.28 2011/02/16 18:55:50 jruoho 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.28 2011/02/16 18:55:50 jruoho 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 "acpica.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 "opt_vga.h" 107 108 #include "acpi_wakecode.h" 109 110 /* Address is also hard-coded in acpi_wakecode.S */ 111 static paddr_t acpi_wakeup_paddr = 3 * PAGE_SIZE; 112 static vaddr_t acpi_wakeup_vaddr; 113 114 int acpi_md_vbios_reset = 1; /* Referenced by dev/pci/vga_pci.c */ 115 int acpi_md_vesa_modenum = 0; /* Referenced by arch/x86/x86/genfb_machdep.c */ 116 static int acpi_md_beep_on_reset = 0; 117 118 static int acpi_md_s4bios(void); 119 static int sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS); 120 static int sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS); 121 122 /* Implemented in acpi_wakeup_low.S. */ 123 int acpi_md_sleep_prepare(int); 124 int acpi_md_sleep_exit(int); 125 126 /* Referenced by acpi_wakeup_low.S. */ 127 void acpi_md_sleep_enter(int); 128 129 #ifdef MULTIPROCESSOR 130 /* Referenced in ipifuncs.c. */ 131 void acpi_cpu_sleep(struct cpu_info *); 132 #endif 133 134 static void 135 acpi_md_sleep_patch(struct cpu_info *ci) 136 { 137 #define WAKECODE_FIXUP(offset, type, val) do { \ 138 type *addr; \ 139 addr = (type *)(acpi_wakeup_vaddr + offset); \ 140 *addr = val; \ 141 } while (0) 142 143 #define WAKECODE_BCOPY(offset, type, val) do { \ 144 void **addr; \ 145 addr = (void **)(acpi_wakeup_vaddr + offset); \ 146 memcpy(addr, &(val), sizeof(type)); \ 147 } while (0) 148 149 paddr_t tmp_pdir; 150 151 tmp_pdir = pmap_init_tmp_pgtbl(acpi_wakeup_paddr); 152 153 /* Execute Sleep */ 154 memcpy((void *)acpi_wakeup_vaddr, wakecode, sizeof(wakecode)); 155 156 if (CPU_IS_PRIMARY(ci)) { 157 WAKECODE_FIXUP(WAKEUP_vesa_modenum, uint16_t, acpi_md_vesa_modenum); 158 WAKECODE_FIXUP(WAKEUP_vbios_reset, uint8_t, acpi_md_vbios_reset); 159 WAKECODE_FIXUP(WAKEUP_beep_on_reset, uint8_t, acpi_md_beep_on_reset); 160 } else { 161 WAKECODE_FIXUP(WAKEUP_vesa_modenum, uint16_t, 0); 162 WAKECODE_FIXUP(WAKEUP_vbios_reset, uint8_t, 0); 163 WAKECODE_FIXUP(WAKEUP_beep_on_reset, uint8_t, 0); 164 } 165 166 #ifdef __i386__ 167 WAKECODE_FIXUP(WAKEUP_r_cr4, uint32_t, ci->ci_suspend_cr4); 168 #else 169 WAKECODE_FIXUP(WAKEUP_efer, uint32_t, ci->ci_suspend_efer); 170 #endif 171 172 WAKECODE_FIXUP(WAKEUP_curcpu, void *, ci); 173 #ifdef __i386__ 174 WAKECODE_FIXUP(WAKEUP_r_cr3, uint32_t, tmp_pdir); 175 #else 176 WAKECODE_FIXUP(WAKEUP_r_cr3, uint64_t, tmp_pdir); 177 #endif 178 WAKECODE_FIXUP(WAKEUP_restorecpu, void *, acpi_md_sleep_exit); 179 #undef WAKECODE_FIXUP 180 #undef WAKECODE_BCOPY 181 } 182 183 static int 184 acpi_md_s4bios(void) 185 { 186 ACPI_TABLE_FACS *facs; 187 ACPI_STATUS rv; 188 189 rv = AcpiGetTable(ACPI_SIG_FACS, 0, (ACPI_TABLE_HEADER **)&facs); 190 191 if (ACPI_FAILURE(rv) || facs == NULL) 192 return 0; 193 194 if ((facs->Flags & ACPI_FACS_S4_BIOS_PRESENT) == 0) 195 return 0; 196 197 return 1; 198 } 199 200 void 201 acpi_md_sleep_enter(int state) 202 { 203 static int s4bios = -1; 204 struct cpu_info *ci; 205 ACPI_STATUS rv; 206 207 ci = curcpu(); 208 209 #ifdef MULTIPROCESSOR 210 if (!CPU_IS_PRIMARY(ci)) { 211 atomic_and_32(&ci->ci_flags, ~CPUF_RUNNING); 212 atomic_and_32(&cpus_running, ~ci->ci_cpumask); 213 214 ACPI_FLUSH_CPU_CACHE(); 215 216 for (;;) 217 x86_hlt(); 218 } 219 #endif 220 221 acpi_md_sleep_patch(ci); 222 223 ACPI_FLUSH_CPU_CACHE(); 224 225 switch (state) { 226 227 case ACPI_STATE_S4: 228 229 if (s4bios < 0) 230 s4bios = acpi_md_s4bios(); 231 232 if (s4bios == 0) { 233 aprint_error("acpi0: S4 not supported\n"); 234 return; 235 } 236 237 rv = AcpiEnterSleepStateS4bios(); 238 break; 239 240 default: 241 rv = AcpiEnterSleepState(state); 242 break; 243 } 244 245 if (ACPI_FAILURE(rv)) { 246 aprint_error("acpi0: failed to enter S%d\n", state); 247 return; 248 } 249 250 for (;;) 251 x86_hlt(); 252 } 253 254 #ifdef MULTIPROCESSOR 255 void 256 acpi_cpu_sleep(struct cpu_info *ci) 257 { 258 KASSERT(!CPU_IS_PRIMARY(ci)); 259 KASSERT(ci == curcpu()); 260 261 x86_disable_intr(); 262 263 if (acpi_md_sleep_prepare(-1)) 264 return; 265 266 /* Execute Wakeup */ 267 #ifdef __i386__ 268 npxinit(ci); 269 #else 270 cpu_init_msrs(ci, false); 271 fpuinit(ci); 272 #endif 273 #if NLAPIC > 0 274 lapic_enable(); 275 lapic_set_lvt(); 276 lapic_initclocks(); 277 #endif 278 279 atomic_or_32(&ci->ci_flags, CPUF_RUNNING); 280 atomic_or_32(&cpus_running, ci->ci_cpumask); 281 tsc_sync_ap(ci); 282 283 x86_enable_intr(); 284 } 285 #endif 286 287 int 288 acpi_md_sleep(int state) 289 { 290 int s, ret = 0; 291 #ifdef MULTIPROCESSOR 292 struct cpu_info *ci; 293 CPU_INFO_ITERATOR cii; 294 #endif 295 296 KASSERT(acpi_wakeup_paddr != 0); 297 KASSERT(sizeof(wakecode) <= PAGE_SIZE); 298 299 if (!CPU_IS_PRIMARY(curcpu())) { 300 printf("acpi0: WARNING: ignoring sleep from secondary CPU\n"); 301 return -1; 302 } 303 304 AcpiSetFirmwareWakingVector(acpi_wakeup_paddr); 305 306 s = splhigh(); 307 #ifdef __i386__ 308 npxsave_cpu(true); 309 #else 310 fpusave_cpu(true); 311 #endif 312 x86_disable_intr(); 313 314 #ifdef MULTIPROCESSOR 315 /* Save and suspend Application Processors */ 316 x86_broadcast_ipi(X86_IPI_ACPI_CPU_SLEEP); 317 while (cpus_running != curcpu()->ci_cpumask) 318 delay(1); 319 #endif 320 321 if (acpi_md_sleep_prepare(state)) 322 goto out; 323 324 /* Execute Wakeup */ 325 #ifdef __i386__ 326 npxinit(&cpu_info_primary); 327 #else 328 cpu_init_msrs(&cpu_info_primary, false); 329 fpuinit(&cpu_info_primary); 330 #endif 331 i8259_reinit(); 332 #if NLAPIC > 0 333 lapic_enable(); 334 lapic_set_lvt(); 335 lapic_initclocks(); 336 #endif 337 #if NIOAPIC > 0 338 ioapic_reenable(); 339 #endif 340 341 initrtclock(TIMER_FREQ); 342 inittodr(time_second); 343 344 /* 345 * The BIOS should always re-enable the SCI upon 346 * resume from the S3 state. The following is a 347 * workaround for systems that fail to do this. 348 */ 349 (void)AcpiWriteBitRegister(ACPI_BITREG_SCI_ENABLE, 1); 350 351 /* 352 * Clear fixed events (see e.g. ACPI 3.0, p. 62). 353 * Also prevent GPEs from misfiring by disabling 354 * all GPEs before interrupts are enabled. The 355 * AcpiLeaveSleepState() function will enable 356 * and handle the general purpose events later. 357 */ 358 (void)AcpiClearEvent(ACPI_EVENT_PMTIMER); 359 (void)AcpiClearEvent(ACPI_EVENT_GLOBAL); 360 (void)AcpiClearEvent(ACPI_EVENT_POWER_BUTTON); 361 (void)AcpiClearEvent(ACPI_EVENT_SLEEP_BUTTON); 362 (void)AcpiClearEvent(ACPI_EVENT_RTC); 363 (void)AcpiHwDisableAllGpes(); 364 365 acpi_pci_link_resume(); 366 367 out: 368 369 #ifdef MULTIPROCESSOR 370 for (CPU_INFO_FOREACH(cii, ci)) { 371 if (CPU_IS_PRIMARY(ci)) 372 continue; 373 acpi_md_sleep_patch(ci); 374 375 CPU_STARTUP(ci, acpi_wakeup_paddr); 376 CPU_START_CLEANUP(ci); 377 378 while ((ci->ci_flags & CPUF_RUNNING) == 0) 379 x86_pause(); 380 381 tsc_sync_bp(ci); 382 } 383 #endif 384 385 x86_enable_intr(); 386 splx(s); 387 388 #ifdef MTRR 389 if (mtrr_funcs != NULL) 390 mtrr_commit(); 391 #endif 392 393 return (ret); 394 } 395 396 void 397 acpi_md_sleep_init(void) 398 { 399 /* Map ACPI wakecode */ 400 acpi_wakeup_vaddr = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, 401 UVM_KMF_VAONLY); 402 if (acpi_wakeup_vaddr == 0) 403 panic("acpi: can't allocate address for wakecode.\n"); 404 405 pmap_kenter_pa(acpi_wakeup_vaddr, acpi_wakeup_paddr, 406 VM_PROT_READ | VM_PROT_WRITE, 0); 407 pmap_update(pmap_kernel()); 408 } 409 410 SYSCTL_SETUP(sysctl_md_acpi_setup, "acpi x86 sysctl setup") 411 { 412 const struct sysctlnode *rnode, *mnode; 413 int err; 414 415 err = sysctl_createv(clog, 0, NULL, &rnode, 416 CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", 417 NULL, NULL, 0, NULL, 0, CTL_HW, CTL_EOL); 418 419 if (err != 0) 420 goto out; 421 422 err = sysctl_createv(clog, 0, &rnode, &rnode, 423 CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", NULL, 424 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 425 426 if (err != 0) 427 goto out; 428 429 err = sysctl_createv(clog, 0, &rnode, &rnode, 430 CTLFLAG_PERMANENT, CTLTYPE_NODE, 431 "sleep", SYSCTL_DESCR("ACPI sleep"), 432 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 433 434 if (err != 0) 435 goto out; 436 437 (void)sysctl_createv(NULL, 0, &rnode, NULL, 438 CTLFLAG_READWRITE, CTLTYPE_BOOL, "beep", 439 NULL, sysctl_md_acpi_beep_on_reset, 440 0, NULL, 0, CTL_CREATE, CTL_EOL); 441 442 (void)sysctl_createv(NULL, 0, &rnode, NULL, 443 CTLFLAG_READWRITE, CTLTYPE_INT, "vbios", 444 NULL, sysctl_md_acpi_vbios_reset, 445 0, NULL, 0, CTL_CREATE, CTL_EOL); 446 447 /* 448 * All ACPI-specific sysctl(9) nodes are centralized 449 * under hw.acpi. The two variables below are provided 450 * for backwards compatibility. 451 */ 452 out: 453 err = sysctl_createv(NULL, 0, NULL, &mnode, 454 CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", 455 NULL, NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL); 456 457 if (err != 0) 458 return; 459 460 (void)sysctl_createv(NULL, 0, &mnode, NULL, 461 CTLFLAG_READWRITE, CTLTYPE_INT, "acpi_vbios_reset", 462 NULL, sysctl_md_acpi_vbios_reset, 463 0, NULL, 0, CTL_CREATE, CTL_EOL); 464 465 (void)sysctl_createv(NULL, 0, &mnode, NULL, 466 CTLFLAG_READWRITE, CTLTYPE_BOOL, "acpi_beep_on_reset", 467 NULL, sysctl_md_acpi_beep_on_reset, 468 0, NULL, 0, CTL_CREATE, CTL_EOL); 469 } 470 471 static int 472 sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS) 473 { 474 int error, t; 475 struct sysctlnode node; 476 477 node = *rnode; 478 t = acpi_md_vbios_reset; 479 node.sysctl_data = &t; 480 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 481 if (error || newp == NULL) 482 return error; 483 484 if (t < 0 || t > 2) 485 return EINVAL; 486 487 #ifndef VGA_POST 488 if (t == 2) { 489 aprint_error("WARNING: hw.acpi.sleep.vbios=2 " 490 "unsupported (no option VGA_POST in kernel config)\n"); 491 return EINVAL; 492 } 493 #endif 494 495 acpi_md_vbios_reset = t; 496 497 return 0; 498 } 499 500 static int 501 sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS) 502 { 503 int error, t; 504 struct sysctlnode node; 505 506 node = *rnode; 507 t = acpi_md_beep_on_reset; 508 node.sysctl_data = &t; 509 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 510 if (error || newp == NULL) 511 return error; 512 513 if (t < 0 || t > 1) 514 return EINVAL; 515 516 acpi_md_beep_on_reset = t; 517 518 return 0; 519 } 520