1 /* $NetBSD: acpi_wakeup.c,v 1.23 2010/04/14 19:32:35 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.23 2010/04/14 19:32:35 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 static int acpi_md_node = CTL_EOL; 115 int acpi_md_vbios_reset = 1; /* Referenced by dev/pci/vga_pci.c */ 116 int acpi_md_vesa_modenum = 0; /* Referenced by arch/x86/x86/genfb_machdep.c */ 117 static int acpi_md_beep_on_reset = 0; 118 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 /* Referenced by acpi_wakeup_low.S. */ 126 void acpi_md_sleep_enter(int); 127 128 #ifdef MULTIPROCESSOR 129 /* Referenced in ipifuncs.c. */ 130 void acpi_cpu_sleep(struct cpu_info *); 131 #endif 132 133 static void 134 acpi_md_sleep_patch(struct cpu_info *ci) 135 { 136 #define WAKECODE_FIXUP(offset, type, val) do { \ 137 type *addr; \ 138 addr = (type *)(acpi_wakeup_vaddr + offset); \ 139 *addr = val; \ 140 } while (0) 141 142 #define WAKECODE_BCOPY(offset, type, val) do { \ 143 void **addr; \ 144 addr = (void **)(acpi_wakeup_vaddr + offset); \ 145 memcpy(addr, &(val), sizeof(type)); \ 146 } while (0) 147 148 paddr_t tmp_pdir; 149 150 tmp_pdir = pmap_init_tmp_pgtbl(acpi_wakeup_paddr); 151 152 /* Execute Sleep */ 153 memcpy((void *)acpi_wakeup_vaddr, wakecode, sizeof(wakecode)); 154 155 if (CPU_IS_PRIMARY(ci)) { 156 WAKECODE_FIXUP(WAKEUP_vesa_modenum, uint16_t, acpi_md_vesa_modenum); 157 WAKECODE_FIXUP(WAKEUP_vbios_reset, uint8_t, acpi_md_vbios_reset); 158 WAKECODE_FIXUP(WAKEUP_beep_on_reset, uint8_t, acpi_md_beep_on_reset); 159 } else { 160 WAKECODE_FIXUP(WAKEUP_vesa_modenum, uint16_t, 0); 161 WAKECODE_FIXUP(WAKEUP_vbios_reset, uint8_t, 0); 162 WAKECODE_FIXUP(WAKEUP_beep_on_reset, uint8_t, 0); 163 } 164 165 #ifdef __i386__ 166 WAKECODE_FIXUP(WAKEUP_r_cr4, uint32_t, ci->ci_suspend_cr4); 167 #else 168 WAKECODE_FIXUP(WAKEUP_efer, uint32_t, ci->ci_suspend_efer); 169 #endif 170 171 WAKECODE_FIXUP(WAKEUP_curcpu, void *, ci); 172 #ifdef __i386__ 173 WAKECODE_FIXUP(WAKEUP_r_cr3, uint32_t, tmp_pdir); 174 #else 175 WAKECODE_FIXUP(WAKEUP_r_cr3, uint64_t, tmp_pdir); 176 #endif 177 WAKECODE_FIXUP(WAKEUP_restorecpu, void *, acpi_md_sleep_exit); 178 #undef WAKECODE_FIXUP 179 #undef WAKECODE_BCOPY 180 } 181 182 /* 183 * S4 sleep using S4BIOS support, from FreeBSD. 184 * 185 * FreeBSD: src/sys/dev/acpica/acpica_support.c,v 1.4 2002/03/12 09:45:17 dfr Exp 186 */ 187 188 static ACPI_STATUS 189 enter_s4_with_bios(void) 190 { 191 ACPI_OBJECT_LIST ArgList; 192 ACPI_OBJECT Arg; 193 uint32_t ret; 194 ACPI_STATUS status; 195 196 /* run the _PTS and _GTS methods */ 197 198 (void)memset(&ArgList, 0, sizeof(ArgList)); 199 200 ArgList.Count = 1; 201 ArgList.Pointer = &Arg; 202 203 (void)memset(&Arg, 0, sizeof(Arg)); 204 205 Arg.Type = ACPI_TYPE_INTEGER; 206 Arg.Integer.Value = ACPI_STATE_S4; 207 208 AcpiEvaluateObject(NULL, "\\_PTS", &ArgList, NULL); 209 AcpiEvaluateObject(NULL, "\\_GTS", &ArgList, NULL); 210 211 /* clear wake status */ 212 213 AcpiWriteBitRegister(ACPI_BITREG_WAKE_STATUS, 1); 214 215 AcpiHwDisableAllGpes(); 216 AcpiHwEnableAllWakeupGpes(); 217 218 /* flush caches */ 219 220 ACPI_FLUSH_CPU_CACHE(); 221 222 /* 223 * write the value to command port and wait until we enter sleep state 224 */ 225 do { 226 AcpiOsStall(1000000); 227 AcpiOsWritePort(AcpiGbl_FADT.SmiCommand, 228 AcpiGbl_FADT.S4BiosRequest, 8); 229 status = AcpiReadBitRegister(ACPI_BITREG_WAKE_STATUS, &ret); 230 if (ACPI_FAILURE(status)) 231 break; 232 } while (!ret); 233 234 AcpiHwDisableAllGpes(); 235 AcpiHwEnableAllRuntimeGpes(); 236 237 return (AE_OK); 238 } 239 240 void 241 acpi_md_sleep_enter(int state) 242 { 243 ACPI_STATUS status; 244 struct cpu_info *ci; 245 246 ci = curcpu(); 247 248 #ifdef MULTIPROCESSOR 249 if (!CPU_IS_PRIMARY(ci)) { 250 atomic_and_32(&ci->ci_flags, ~CPUF_RUNNING); 251 atomic_and_32(&cpus_running, ~ci->ci_cpumask); 252 253 ACPI_FLUSH_CPU_CACHE(); 254 255 for (;;) 256 x86_hlt(); 257 } 258 #endif 259 260 acpi_md_sleep_patch(ci); 261 262 ACPI_FLUSH_CPU_CACHE(); 263 264 if (state == ACPI_STATE_S4) { 265 ACPI_TABLE_FACS *facs; 266 status = AcpiGetTable(ACPI_SIG_FACS, 0, (ACPI_TABLE_HEADER **)&facs); 267 if (ACPI_FAILURE(status)) { 268 printf("acpi: S4BIOS not supported: cannot load FACS\n"); 269 return; 270 } 271 if (facs == NULL || 272 (facs->Flags & ACPI_FACS_S4_BIOS_PRESENT) == 0) { 273 printf("acpi: S4BIOS not supported: not present"); 274 return; 275 } 276 status = enter_s4_with_bios(); 277 } else { 278 status = AcpiEnterSleepState(state); 279 } 280 281 if (ACPI_FAILURE(status)) { 282 printf("acpi: AcpiEnterSleepState failed: %s\n", 283 AcpiFormatException(status)); 284 return; 285 } 286 287 for (;;) 288 x86_hlt(); 289 } 290 291 #ifdef MULTIPROCESSOR 292 void 293 acpi_cpu_sleep(struct cpu_info *ci) 294 { 295 KASSERT(!CPU_IS_PRIMARY(ci)); 296 KASSERT(ci == curcpu()); 297 298 x86_disable_intr(); 299 300 if (acpi_md_sleep_prepare(-1)) 301 return; 302 303 /* Execute Wakeup */ 304 #ifdef __i386__ 305 npxinit(ci); 306 #else 307 cpu_init_msrs(ci, false); 308 fpuinit(ci); 309 #endif 310 #if NLAPIC > 0 311 lapic_enable(); 312 lapic_set_lvt(); 313 lapic_initclocks(); 314 #endif 315 316 atomic_or_32(&ci->ci_flags, CPUF_RUNNING); 317 atomic_or_32(&cpus_running, ci->ci_cpumask); 318 tsc_sync_ap(ci); 319 320 x86_enable_intr(); 321 } 322 #endif 323 324 int 325 acpi_md_sleep(int state) 326 { 327 int s, ret = 0; 328 #ifdef MULTIPROCESSOR 329 struct cpu_info *ci; 330 CPU_INFO_ITERATOR cii; 331 #endif 332 333 KASSERT(acpi_wakeup_paddr != 0); 334 KASSERT(sizeof(wakecode) <= PAGE_SIZE); 335 336 if (!CPU_IS_PRIMARY(curcpu())) { 337 printf("acpi0: WARNING: ignoring sleep from secondary CPU\n"); 338 return -1; 339 } 340 341 AcpiSetFirmwareWakingVector(acpi_wakeup_paddr); 342 343 s = splhigh(); 344 #ifdef __i386__ 345 npxsave_cpu(true); 346 #else 347 fpusave_cpu(true); 348 #endif 349 x86_disable_intr(); 350 351 #ifdef MULTIPROCESSOR 352 /* Save and suspend Application Processors */ 353 x86_broadcast_ipi(X86_IPI_ACPI_CPU_SLEEP); 354 while (cpus_running != curcpu()->ci_cpumask) 355 delay(1); 356 #endif 357 358 if (acpi_md_sleep_prepare(state)) 359 goto out; 360 361 /* Execute Wakeup */ 362 #ifdef __i386__ 363 npxinit(&cpu_info_primary); 364 #else 365 cpu_init_msrs(&cpu_info_primary, false); 366 fpuinit(&cpu_info_primary); 367 #endif 368 i8259_reinit(); 369 #if NLAPIC > 0 370 lapic_enable(); 371 lapic_set_lvt(); 372 lapic_initclocks(); 373 #endif 374 #if NIOAPIC > 0 375 ioapic_reenable(); 376 #endif 377 378 initrtclock(TIMER_FREQ); 379 inittodr(time_second); 380 381 AcpiClearEvent(ACPI_EVENT_PMTIMER); 382 AcpiClearEvent(ACPI_EVENT_GLOBAL); 383 AcpiClearEvent(ACPI_EVENT_POWER_BUTTON); 384 AcpiClearEvent(ACPI_EVENT_SLEEP_BUTTON); 385 AcpiClearEvent(ACPI_EVENT_RTC); 386 AcpiHwDisableAllGpes (); 387 388 acpi_pci_link_resume(); 389 390 out: 391 392 #ifdef MULTIPROCESSOR 393 for (CPU_INFO_FOREACH(cii, ci)) { 394 if (CPU_IS_PRIMARY(ci)) 395 continue; 396 acpi_md_sleep_patch(ci); 397 398 CPU_STARTUP(ci, acpi_wakeup_paddr); 399 CPU_START_CLEANUP(ci); 400 401 while ((ci->ci_flags & CPUF_RUNNING) == 0) 402 x86_pause(); 403 404 tsc_sync_bp(ci); 405 } 406 #endif 407 408 x86_enable_intr(); 409 splx(s); 410 411 #ifdef MTRR 412 if (mtrr_funcs != NULL) 413 mtrr_commit(); 414 #endif 415 416 return (ret); 417 } 418 419 void 420 acpi_md_sleep_init(void) 421 { 422 /* Map ACPI wakecode */ 423 acpi_wakeup_vaddr = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, 424 UVM_KMF_VAONLY); 425 if (acpi_wakeup_vaddr == 0) 426 panic("acpi: can't allocate address for wakecode.\n"); 427 428 pmap_kenter_pa(acpi_wakeup_vaddr, acpi_wakeup_paddr, 429 VM_PROT_READ | VM_PROT_WRITE, 0); 430 pmap_update(pmap_kernel()); 431 } 432 433 SYSCTL_SETUP(sysctl_md_acpi_setup, "acpi x86 sysctl setup") 434 { 435 const struct sysctlnode *node; 436 const struct sysctlnode *ssnode; 437 438 if (sysctl_createv(NULL, 0, NULL, &node, CTLFLAG_PERMANENT, 439 CTLTYPE_NODE, "machdep", NULL, NULL, 0, NULL, 0, CTL_MACHDEP, 440 CTL_EOL) != 0) 441 return; 442 if (sysctl_createv(NULL, 0, &node, &ssnode, CTLFLAG_READWRITE, 443 CTLTYPE_INT, "acpi_vbios_reset", NULL, sysctl_md_acpi_vbios_reset, 444 0, NULL, 0, CTL_CREATE, CTL_EOL) != 0) 445 return; 446 if (sysctl_createv(NULL, 0, &node, &ssnode, CTLFLAG_READWRITE, 447 CTLTYPE_BOOL, "acpi_beep_on_reset", NULL, sysctl_md_acpi_beep_on_reset, 448 0, NULL, 0, CTL_CREATE, CTL_EOL) != 0) 449 return; 450 451 acpi_md_node = node->sysctl_num; 452 } 453 454 static int 455 sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS) 456 { 457 int error, t; 458 struct sysctlnode node; 459 460 node = *rnode; 461 t = acpi_md_vbios_reset; 462 node.sysctl_data = &t; 463 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 464 if (error || newp == NULL) 465 return error; 466 467 if (t < 0 || t > 2) 468 return EINVAL; 469 470 #ifndef VGA_POST 471 if (t == 2) { 472 aprint_error("WARNING: machdep.acpi_vbios_reset=2 " 473 "unsupported (no option VGA_POST in kernel config)\n"); 474 return EINVAL; 475 } 476 #endif 477 478 acpi_md_vbios_reset = t; 479 480 return 0; 481 } 482 483 static int 484 sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS) 485 { 486 int error, t; 487 struct sysctlnode node; 488 489 node = *rnode; 490 t = acpi_md_beep_on_reset; 491 node.sysctl_data = &t; 492 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 493 if (error || newp == NULL) 494 return error; 495 496 if (t < 0 || t > 1) 497 return EINVAL; 498 499 acpi_md_beep_on_reset = t; 500 501 return 0; 502 } 503