1/* $NetBSD: acpi_wakeup.c,v 1.57 2023/10/19 14:59:46 bouyer Exp $ */ 2 3/*- 4 * Copyright (c) 2002, 2011 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/*- 33 * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org> 34 * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki@jp.freebsd.org> 35 * All rights reserved. 36 * 37 * Redistribution and use in source and binary forms, with or without 38 * modification, are permitted provided that the following conditions 39 * are met: 40 * 1. Redistributions of source code must retain the above copyright 41 * notice, this list of conditions and the following disclaimer. 42 * 2. Redistributions in binary form must reproduce the above copyright 43 * notice, this list of conditions and the following disclaimer in the 44 * documentation and/or other materials provided with the distribution. 45 * 46 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 49 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 56 * SUCH DAMAGE. 57 * 58 * FreeBSD: src/sys/i386/acpica/acpi_wakeup.c,v 1.9 2002/01/10 03:26:46 wes Exp 59 */ 60 61#include <sys/cdefs.h> 62__KERNEL_RCSID(0, "$NetBSD: acpi_wakeup.c,v 1.57 2023/10/19 14:59:46 bouyer Exp $"); 63 64#include <sys/param.h> 65#include <sys/systm.h> 66#include <sys/kernel.h> 67#include <sys/bus.h> 68#include <sys/cpu.h> 69#include <sys/kcpuset.h> 70#include <sys/sysctl.h> 71 72#include <uvm/uvm_extern.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#include <machine/mtrr.h> 97 98#include <x86/cpuvar.h> 99#include <x86/x86/tsc.h> 100#include <x86/fpu.h> 101#include <arch/x86/include/genfb_machdep.h> 102 103#include "opt_vga.h" 104 105#include "acpi_wakecode.h" 106 107#ifdef XENPV 108#error acpi_wakeup.c (acpi_md_vesa_modenum) users must be adapted for Xen 109#else 110int acpi_md_vesa_modenum = 0; 111#endif 112 113/* Address is also hard-coded in acpi_wakecode.S */ 114static paddr_t acpi_wakeup_paddr = 3 * PAGE_SIZE; 115static vaddr_t acpi_wakeup_vaddr; 116 117static int acpi_md_beep_on_reset = 0; 118 119static int acpi_md_s4bios(void); 120static int sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS); 121static int sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS); 122 123/* Implemented in acpi_wakeup_low.S. */ 124int acpi_md_sleep_prepare(int); 125int acpi_md_sleep_exit(int); 126 127/* Referenced by acpi_wakeup_low.S. */ 128void acpi_md_sleep_enter(int); 129 130#ifdef MULTIPROCESSOR 131/* Referenced in ipifuncs.c. */ 132void acpi_cpu_sleep(struct cpu_info *); 133#endif 134 135static void 136acpi_md_sleep_patch(struct cpu_info *ci) 137{ 138#define WAKECODE_FIXUP(offset, type, val) do { \ 139 type *addr; \ 140 addr = (type *)(acpi_wakeup_vaddr + offset); \ 141 *addr = val; \ 142} while (0) 143 144 paddr_t tmp_pdir; 145 146 tmp_pdir = pmap_init_tmp_pgtbl(acpi_wakeup_paddr); 147 148 memcpy((void *)acpi_wakeup_vaddr, wakecode, sizeof(wakecode)); 149 150 if (CPU_IS_PRIMARY(ci)) { 151 WAKECODE_FIXUP(WAKEUP_vesa_modenum, uint16_t, acpi_md_vesa_modenum); 152 WAKECODE_FIXUP(WAKEUP_vbios_reset, uint8_t, acpi_md_vbios_reset); 153 WAKECODE_FIXUP(WAKEUP_beep_on_reset, uint8_t, acpi_md_beep_on_reset); 154 } else { 155 WAKECODE_FIXUP(WAKEUP_vesa_modenum, uint16_t, 0); 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#endif 163 WAKECODE_FIXUP(WAKEUP_efer, uint32_t, ci->ci_suspend_efer); 164 WAKECODE_FIXUP(WAKEUP_curcpu, void *, ci); 165#ifdef __i386__ 166 WAKECODE_FIXUP(WAKEUP_r_cr3, uint32_t, tmp_pdir); 167#else 168 WAKECODE_FIXUP(WAKEUP_r_cr3, uint64_t, tmp_pdir); 169#endif 170 WAKECODE_FIXUP(WAKEUP_restorecpu, void *, acpi_md_sleep_exit); 171#undef WAKECODE_FIXUP 172} 173 174static int 175acpi_md_s4bios(void) 176{ 177 ACPI_TABLE_FACS *facs; 178 ACPI_STATUS rv; 179 180 rv = AcpiGetTable(ACPI_SIG_FACS, 0, (ACPI_TABLE_HEADER **)&facs); 181 182 if (ACPI_FAILURE(rv) || facs == NULL) 183 return 0; 184 185 if ((facs->Flags & ACPI_FACS_S4_BIOS_PRESENT) == 0) 186 return 0; 187 188 return 1; 189} 190 191void 192acpi_md_sleep_enter(int state) 193{ 194 static int s4bios = -1; 195 struct cpu_info *ci; 196 ACPI_STATUS rv; 197 198 ci = curcpu(); 199 200#ifdef MULTIPROCESSOR 201 if (!CPU_IS_PRIMARY(ci)) { 202 atomic_and_32(&ci->ci_flags, ~CPUF_RUNNING); 203 kcpuset_atomic_clear(kcpuset_running, cpu_index(ci)); 204 205 ACPI_FLUSH_CPU_CACHE(); 206 207 for (;;) 208 x86_hlt(); 209 } 210#endif 211 212 acpi_md_sleep_patch(ci); 213 214 ACPI_FLUSH_CPU_CACHE(); 215 216 switch (state) { 217 218 case ACPI_STATE_S4: 219 220 if (s4bios < 0) 221 s4bios = acpi_md_s4bios(); 222 223 if (s4bios == 0) { 224 aprint_error("acpi0: S4 not supported\n"); 225 return; 226 } 227 228 rv = AcpiEnterSleepStateS4bios(); 229 break; 230 231 default: 232 rv = AcpiEnterSleepState(state); 233 break; 234 } 235 236 if (ACPI_FAILURE(rv)) { 237 aprint_error("acpi0: failed to enter S%d\n", state); 238 return; 239 } 240 241 for (;;) 242 x86_hlt(); 243} 244 245#ifdef MULTIPROCESSOR 246void 247acpi_cpu_sleep(struct cpu_info *ci) 248{ 249 uint64_t xcr0 = 0; 250 int s; 251 252 KASSERT(!CPU_IS_PRIMARY(ci)); 253 KASSERT(ci == curcpu()); 254 255 s = splhigh(); 256 fpu_save(); 257 x86_disable_intr(); 258 259 /* 260 * XXX also need to save the PMCs, the dbregs, and probably a few 261 * MSRs too. 262 */ 263 if (rcr4() & CR4_OSXSAVE) 264 xcr0 = rdxcr(0); 265 266 /* Go get some sleep */ 267 if (acpi_md_sleep_prepare(-1)) 268 goto out; 269 270 /* 271 * Sleeping and having bad nightmares about what could go wrong 272 * when waking up. 273 */ 274 275 /* We just woke up (cpuN), execution is resumed here */ 276 cpu_init_msrs(ci, false); 277 fpuinit(ci); 278 if (rcr4() & CR4_OSXSAVE) 279 wrxcr(0, xcr0); 280 pat_init(ci); 281 x86_errata(); 282#if NLAPIC > 0 283 lapic_enable(); 284 lapic_set_lvt(); 285 lapic_reset(); 286#endif 287 288 atomic_or_32(&ci->ci_flags, CPUF_RUNNING); 289 kcpuset_atomic_set(kcpuset_running, cpu_index(ci)); 290 tsc_sync_ap(ci); 291 292out: 293 x86_enable_intr(); 294 splx(s); 295} 296#endif 297 298int 299acpi_md_sleep(int state) 300{ 301 uint64_t xcr0 = 0; 302 int s, ret = 0; 303#ifdef MULTIPROCESSOR 304 struct cpu_info *ci; 305 CPU_INFO_ITERATOR cii; 306 cpuid_t cid; 307#endif 308 309 KASSERT(acpi_wakeup_paddr != 0); 310 KASSERT(sizeof(wakecode) <= PAGE_SIZE); 311 312 if (!CPU_IS_PRIMARY(curcpu())) { 313 printf("acpi0: WARNING: ignoring sleep from secondary CPU\n"); 314 return -1; 315 } 316 317 AcpiSetFirmwareWakingVector(acpi_wakeup_paddr, 0); 318 319 s = splhigh(); 320 fpu_save(); 321 x86_disable_intr(); 322 323#ifdef MULTIPROCESSOR 324 /* Save and suspend Application Processors. */ 325 x86_broadcast_ipi(X86_IPI_ACPI_CPU_SLEEP); 326 cid = cpu_index(curcpu()); 327 while (kcpuset_isotherset(kcpuset_running, cid)) { 328 delay(1); 329 } 330#endif 331 332 /* 333 * XXX also need to save the PMCs, the dbregs, and probably a few 334 * MSRs too. 335 */ 336 if (rcr4() & CR4_OSXSAVE) 337 xcr0 = rdxcr(0); 338 339 /* Go get some sleep */ 340 if (acpi_md_sleep_prepare(state)) 341 goto out; 342 343 /* 344 * Sleeping and having bad nightmares about what could go wrong 345 * when waking up. 346 */ 347 348 /* We just woke up (cpu0), execution is resumed here */ 349 tsc_tc_reset(); 350 cpu_init_msrs(&cpu_info_primary, false); 351 fpuinit(&cpu_info_primary); 352 if (rcr4() & CR4_OSXSAVE) 353 wrxcr(0, xcr0); 354 pat_init(&cpu_info_primary); 355 x86_errata(); 356 i8259_reinit(); 357#if NLAPIC > 0 358 lapic_enable(); 359 lapic_set_lvt(); 360 lapic_reset(); 361#endif 362#if NIOAPIC > 0 363 ioapic_reenable(); 364#endif 365 366 initrtclock(TIMER_FREQ); 367 inittodr(time_second); 368 369 /* 370 * The BIOS should always re-enable the SCI upon 371 * resume from the S3 state. The following is a 372 * workaround for systems that fail to do this. 373 */ 374 (void)AcpiWriteBitRegister(ACPI_BITREG_SCI_ENABLE, 1); 375 376 /* 377 * Clear fixed events (see e.g. ACPI 3.0, p. 62). 378 * Also prevent GPEs from misfiring by disabling 379 * all GPEs before interrupts are enabled. The 380 * AcpiLeaveSleepState() function will enable 381 * and handle the general purpose events later. 382 */ 383 (void)AcpiClearEvent(ACPI_EVENT_PMTIMER); 384 (void)AcpiClearEvent(ACPI_EVENT_GLOBAL); 385 (void)AcpiClearEvent(ACPI_EVENT_POWER_BUTTON); 386 (void)AcpiClearEvent(ACPI_EVENT_SLEEP_BUTTON); 387 (void)AcpiClearEvent(ACPI_EVENT_RTC); 388 (void)AcpiHwDisableAllGpes(); 389 390 acpi_pci_link_resume(); 391 392out: 393 394#ifdef MULTIPROCESSOR 395 /* Wake up the secondary CPUs */ 396 for (CPU_INFO_FOREACH(cii, ci)) { 397 if (CPU_IS_PRIMARY(ci)) 398 continue; 399 acpi_md_sleep_patch(ci); 400 401 CPU_STARTUP(ci, acpi_wakeup_paddr); 402 CPU_START_CLEANUP(ci); 403 404 while ((ci->ci_flags & CPUF_RUNNING) == 0) 405 x86_pause(); 406 407 tsc_sync_bp(ci); 408 } 409#endif 410 411 x86_enable_intr(); 412 splx(s); 413 414#ifdef MTRR 415 if (mtrr_funcs != NULL) 416 mtrr_commit(); 417#endif 418 419 return (ret); 420} 421 422void 423acpi_md_sleep_init(void) 424{ 425 /* Map ACPI wakecode */ 426 acpi_wakeup_vaddr = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, 427 UVM_KMF_VAONLY); 428 if (acpi_wakeup_vaddr == 0) 429 panic("acpi: can't allocate address for wakecode.\n"); 430 431 pmap_kenter_pa(acpi_wakeup_vaddr, acpi_wakeup_paddr, 432 VM_PROT_READ | VM_PROT_WRITE, 0); 433 pmap_update(pmap_kernel()); 434} 435 436SYSCTL_SETUP(sysctl_md_acpi_setup, "ACPI x86 sysctl setup") 437{ 438 const struct sysctlnode *rnode; 439 int err; 440 441 err = sysctl_createv(clog, 0, NULL, &rnode, 442 CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", NULL, 443 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); 444 445 if (err != 0) 446 return; 447 448 err = sysctl_createv(clog, 0, &rnode, &rnode, 449 CTLFLAG_PERMANENT, CTLTYPE_NODE, 450 "sleep", SYSCTL_DESCR("ACPI sleep"), 451 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 452 453 if (err != 0) 454 return; 455 456 (void)sysctl_createv(NULL, 0, &rnode, NULL, 457 CTLFLAG_READWRITE, CTLTYPE_BOOL, "beep", 458 NULL, sysctl_md_acpi_beep_on_reset, 459 0, NULL, 0, CTL_CREATE, CTL_EOL); 460 461 (void)sysctl_createv(NULL, 0, &rnode, NULL, 462 CTLFLAG_READWRITE, CTLTYPE_INT, "vbios", 463 NULL, sysctl_md_acpi_vbios_reset, 464 0, NULL, 0, CTL_CREATE, CTL_EOL); 465} 466 467static int 468sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS) 469{ 470 int error, t; 471 struct sysctlnode node; 472 473 node = *rnode; 474 t = acpi_md_vbios_reset; 475 node.sysctl_data = &t; 476 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 477 if (error || newp == NULL) 478 return error; 479 480 if (t < 0 || t > 2) 481 return EINVAL; 482 483#ifndef VGA_POST 484 if (t == 2) { 485 aprint_error("WARNING: hw.acpi.sleep.vbios=2 " 486 "unsupported (no option VGA_POST in kernel config)\n"); 487 return EINVAL; 488 } 489#endif 490 491 acpi_md_vbios_reset = t; 492 493 return 0; 494} 495 496static int 497sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS) 498{ 499 int error, t; 500 struct sysctlnode node; 501 502 node = *rnode; 503 t = acpi_md_beep_on_reset; 504 node.sysctl_data = &t; 505 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 506 if (error || newp == NULL) 507 return error; 508 509 if (t < 0 || t > 1) 510 return EINVAL; 511 512 acpi_md_beep_on_reset = t; 513 514 return 0; 515} 516