acpi_wakeup.c revision 335554
1/*- 2 * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org> 3 * Copyright (c) 2001-2012 Mitsuru IWASAKI <iwasaki@jp.freebsd.org> 4 * Copyright (c) 2003 Peter Wemm 5 * Copyright (c) 2008-2012 Jung-uk Kim <jkim@FreeBSD.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: stable/11/sys/x86/acpica/acpi_wakeup.c 335554 2018-06-22 10:39:22Z avg $"); 32 33#if defined(__amd64__) 34#define DEV_APIC 35#else 36#include "opt_apic.h" 37#endif 38 39#include <sys/param.h> 40#include <sys/bus.h> 41#include <sys/eventhandler.h> 42#include <sys/kernel.h> 43#include <sys/malloc.h> 44#include <sys/memrange.h> 45#include <sys/smp.h> 46#include <sys/systm.h> 47 48#include <vm/vm.h> 49#include <vm/pmap.h> 50 51#include <machine/clock.h> 52#include <machine/cpu.h> 53#include <machine/intr_machdep.h> 54#include <x86/mca.h> 55#include <machine/pcb.h> 56#include <machine/specialreg.h> 57#include <machine/md_var.h> 58 59#ifdef DEV_APIC 60#include <x86/apicreg.h> 61#include <x86/apicvar.h> 62#endif 63#ifdef SMP 64#include <machine/smp.h> 65#include <machine/vmparam.h> 66#endif 67 68#include <contrib/dev/acpica/include/acpi.h> 69 70#include <dev/acpica/acpivar.h> 71 72#include "acpi_wakecode.h" 73#include "acpi_wakedata.h" 74 75/* Make sure the code is less than a page and leave room for the stack. */ 76CTASSERT(sizeof(wakecode) < PAGE_SIZE - 1024); 77 78extern int acpi_resume_beep; 79extern int acpi_reset_video; 80extern int acpi_susp_bounce; 81 82#ifdef SMP 83extern struct susppcb **susppcbs; 84static cpuset_t suspcpus; 85#else 86static struct susppcb **susppcbs; 87#endif 88 89static void *acpi_alloc_wakeup_handler(void **); 90static void acpi_stop_beep(void *); 91 92#ifdef SMP 93static int acpi_wakeup_ap(struct acpi_softc *, int); 94static void acpi_wakeup_cpus(struct acpi_softc *); 95#endif 96 97#ifdef __amd64__ 98#define ACPI_WAKEPAGES 4 99#else 100#define ACPI_WAKEPAGES 1 101#endif 102 103#define WAKECODE_FIXUP(offset, type, val) do { \ 104 type *addr; \ 105 addr = (type *)(sc->acpi_wakeaddr + (offset)); \ 106 *addr = val; \ 107} while (0) 108 109static void 110acpi_stop_beep(void *arg) 111{ 112 113 if (acpi_resume_beep != 0) 114 timer_spkr_release(); 115} 116 117#ifdef SMP 118static int 119acpi_wakeup_ap(struct acpi_softc *sc, int cpu) 120{ 121 struct pcb *pcb; 122 int vector = (sc->acpi_wakephys >> 12) & 0xff; 123 int apic_id = cpu_apic_ids[cpu]; 124 int ms; 125 126 pcb = &susppcbs[cpu]->sp_pcb; 127 WAKECODE_FIXUP(wakeup_pcb, struct pcb *, pcb); 128 WAKECODE_FIXUP(wakeup_gdt, uint16_t, pcb->pcb_gdt.rd_limit); 129 WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t, pcb->pcb_gdt.rd_base); 130 131 ipi_startup(apic_id, vector); 132 133 /* Wait up to 5 seconds for it to resume. */ 134 for (ms = 0; ms < 5000; ms++) { 135 if (!CPU_ISSET(cpu, &suspended_cpus)) 136 return (1); /* return SUCCESS */ 137 DELAY(1000); 138 } 139 return (0); /* return FAILURE */ 140} 141 142#define WARMBOOT_TARGET 0 143#define WARMBOOT_OFF (KERNBASE + 0x0467) 144#define WARMBOOT_SEG (KERNBASE + 0x0469) 145 146#define CMOS_REG (0x70) 147#define CMOS_DATA (0x71) 148#define BIOS_RESET (0x0f) 149#define BIOS_WARM (0x0a) 150 151static void 152acpi_wakeup_cpus(struct acpi_softc *sc) 153{ 154 uint32_t mpbioswarmvec; 155 int cpu; 156 u_char mpbiosreason; 157 158 /* save the current value of the warm-start vector */ 159 mpbioswarmvec = *((uint32_t *)WARMBOOT_OFF); 160 outb(CMOS_REG, BIOS_RESET); 161 mpbiosreason = inb(CMOS_DATA); 162 163 /* setup a vector to our boot code */ 164 *((volatile u_short *)WARMBOOT_OFF) = WARMBOOT_TARGET; 165 *((volatile u_short *)WARMBOOT_SEG) = sc->acpi_wakephys >> 4; 166 outb(CMOS_REG, BIOS_RESET); 167 outb(CMOS_DATA, BIOS_WARM); /* 'warm-start' */ 168 169 /* Wake up each AP. */ 170 for (cpu = 1; cpu < mp_ncpus; cpu++) { 171 if (!CPU_ISSET(cpu, &suspcpus)) 172 continue; 173 if (acpi_wakeup_ap(sc, cpu) == 0) { 174 /* restore the warmstart vector */ 175 *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec; 176 panic("acpi_wakeup: failed to resume AP #%d (PHY #%d)", 177 cpu, cpu_apic_ids[cpu]); 178 } 179 } 180 181 /* restore the warmstart vector */ 182 *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec; 183 184 outb(CMOS_REG, BIOS_RESET); 185 outb(CMOS_DATA, mpbiosreason); 186} 187#endif 188 189int 190acpi_sleep_machdep(struct acpi_softc *sc, int state) 191{ 192 ACPI_STATUS status; 193 struct pcb *pcb; 194#ifdef __amd64__ 195 struct pcpu *pc; 196 int i; 197#endif 198 199 if (sc->acpi_wakeaddr == 0ul) 200 return (-1); /* couldn't alloc wake memory */ 201 202#ifdef SMP 203 suspcpus = all_cpus; 204 CPU_CLR(PCPU_GET(cpuid), &suspcpus); 205#endif 206 207 if (acpi_resume_beep != 0) 208 timer_spkr_acquire(); 209 210 AcpiSetFirmwareWakingVector(sc->acpi_wakephys, 0); 211 212 intr_suspend(); 213 214 pcb = &susppcbs[0]->sp_pcb; 215 if (savectx(pcb)) { 216#ifdef __amd64__ 217 fpususpend(susppcbs[0]->sp_fpususpend); 218#else 219 npxsuspend(susppcbs[0]->sp_fpususpend); 220#endif 221#ifdef SMP 222 if (!CPU_EMPTY(&suspcpus) && suspend_cpus(suspcpus) == 0) { 223 device_printf(sc->acpi_dev, "Failed to suspend APs\n"); 224 return (0); /* couldn't sleep */ 225 } 226#endif 227#ifdef __amd64__ 228 hw_ibrs_active = 0; 229 hw_ssb_active = 0; 230 cpu_stdext_feature3 = 0; 231 CPU_FOREACH(i) { 232 pc = pcpu_find(i); 233 pc->pc_ibpb_set = 0; 234 } 235#endif 236 237 WAKECODE_FIXUP(resume_beep, uint8_t, (acpi_resume_beep != 0)); 238 WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0)); 239 240#ifdef __amd64__ 241 WAKECODE_FIXUP(wakeup_efer, uint64_t, rdmsr(MSR_EFER) & 242 ~(EFER_LMA)); 243#else 244 WAKECODE_FIXUP(wakeup_cr4, register_t, pcb->pcb_cr4); 245#endif 246 WAKECODE_FIXUP(wakeup_pcb, struct pcb *, pcb); 247 WAKECODE_FIXUP(wakeup_gdt, uint16_t, pcb->pcb_gdt.rd_limit); 248 WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t, pcb->pcb_gdt.rd_base); 249 250 /* Call ACPICA to enter the desired sleep state */ 251 if (state == ACPI_STATE_S4 && sc->acpi_s4bios) 252 status = AcpiEnterSleepStateS4bios(); 253 else 254 status = AcpiEnterSleepState(state); 255 if (ACPI_FAILURE(status)) { 256 device_printf(sc->acpi_dev, 257 "AcpiEnterSleepState failed - %s\n", 258 AcpiFormatException(status)); 259 return (0); /* couldn't sleep */ 260 } 261 262 if (acpi_susp_bounce) 263 resumectx(pcb); 264 265 for (;;) 266 ia32_pause(); 267 } else { 268#ifdef __amd64__ 269 fpuresume(susppcbs[0]->sp_fpususpend); 270#else 271 npxresume(susppcbs[0]->sp_fpususpend); 272#endif 273 } 274 275 return (1); /* wakeup successfully */ 276} 277 278int 279acpi_wakeup_machdep(struct acpi_softc *sc, int state, int sleep_result, 280 int intr_enabled) 281{ 282 283 if (sleep_result == -1) 284 return (sleep_result); 285 286 if (!intr_enabled) { 287 /* Wakeup MD procedures in interrupt disabled context */ 288 if (sleep_result == 1) { 289 pmap_init_pat(); 290 initializecpu(); 291 PCPU_SET(switchtime, 0); 292 PCPU_SET(switchticks, ticks); 293#ifdef DEV_APIC 294 lapic_xapic_mode(); 295#endif 296#ifdef SMP 297 if (!CPU_EMPTY(&suspcpus)) 298 acpi_wakeup_cpus(sc); 299#endif 300 } 301 302#ifdef SMP 303 if (!CPU_EMPTY(&suspcpus)) 304 resume_cpus(suspcpus); 305#endif 306 mca_resume(); 307#ifdef __amd64__ 308 if (vmm_resume_p != NULL) 309 vmm_resume_p(); 310#endif 311 intr_resume(/*suspend_cancelled*/false); 312 313 AcpiSetFirmwareWakingVector(0, 0); 314 } else { 315 /* Wakeup MD procedures in interrupt enabled context */ 316 if (sleep_result == 1 && mem_range_softc.mr_op != NULL && 317 mem_range_softc.mr_op->reinit != NULL) 318 mem_range_softc.mr_op->reinit(&mem_range_softc); 319 } 320 321 return (sleep_result); 322} 323 324static void * 325acpi_alloc_wakeup_handler(void *wakepages[ACPI_WAKEPAGES]) 326{ 327 int i; 328 329 memset(wakepages, 0, ACPI_WAKEPAGES * sizeof(*wakepages)); 330 331 /* 332 * Specify the region for our wakeup code. We want it in the low 1 MB 333 * region, excluding real mode IVT (0-0x3ff), BDA (0x400-0x4ff), EBDA 334 * (less than 128KB, below 0xa0000, must be excluded by SMAP and DSDT), 335 * and ROM area (0xa0000 and above). The temporary page tables must be 336 * page-aligned. 337 */ 338 for (i = 0; i < ACPI_WAKEPAGES; i++) { 339 wakepages[i] = contigmalloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT, 340 0x500, 0xa0000, PAGE_SIZE, 0ul); 341 if (wakepages[i] == NULL) { 342 printf("%s: can't alloc wake memory\n", __func__); 343 goto freepages; 344 } 345 } 346 if (EVENTHANDLER_REGISTER(power_resume, acpi_stop_beep, NULL, 347 EVENTHANDLER_PRI_LAST) == NULL) { 348 printf("%s: can't register event handler\n", __func__); 349 goto freepages; 350 } 351 susppcbs = malloc(mp_ncpus * sizeof(*susppcbs), M_DEVBUF, M_WAITOK); 352 for (i = 0; i < mp_ncpus; i++) { 353 susppcbs[i] = malloc(sizeof(**susppcbs), M_DEVBUF, M_WAITOK); 354 susppcbs[i]->sp_fpususpend = alloc_fpusave(M_WAITOK); 355 } 356 357 return (wakepages); 358 359freepages: 360 for (i = 0; i < ACPI_WAKEPAGES; i++) 361 if (wakepages[i] != NULL) 362 contigfree(wakepages[i], PAGE_SIZE, M_DEVBUF); 363 return (NULL); 364} 365 366void 367acpi_install_wakeup_handler(struct acpi_softc *sc) 368{ 369 static void *wakeaddr; 370 void *wakepages[ACPI_WAKEPAGES]; 371#ifdef __amd64__ 372 uint64_t *pt4, *pt3, *pt2; 373 vm_paddr_t pt4pa, pt3pa, pt2pa; 374 int i; 375#endif 376 377 if (wakeaddr != NULL) 378 return; 379 380 if (acpi_alloc_wakeup_handler(wakepages) == NULL) 381 return; 382 383 wakeaddr = wakepages[0]; 384 sc->acpi_wakeaddr = (vm_offset_t)wakeaddr; 385 sc->acpi_wakephys = vtophys(wakeaddr); 386 387#ifdef __amd64__ 388 pt4 = wakepages[1]; 389 pt3 = wakepages[2]; 390 pt2 = wakepages[3]; 391 pt4pa = vtophys(pt4); 392 pt3pa = vtophys(pt3); 393 pt2pa = vtophys(pt2); 394#endif 395 396 bcopy(wakecode, (void *)sc->acpi_wakeaddr, sizeof(wakecode)); 397 398 /* Patch GDT base address, ljmp targets. */ 399 WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t, 400 sc->acpi_wakephys + bootgdt); 401 WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t, 402 sc->acpi_wakephys + wakeup_32); 403#ifdef __amd64__ 404 WAKECODE_FIXUP((wakeup_sw64 + 1), uint32_t, 405 sc->acpi_wakephys + wakeup_64); 406 WAKECODE_FIXUP(wakeup_pagetables, uint32_t, pt4pa); 407#endif 408 409 /* Save pointers to some global data. */ 410 WAKECODE_FIXUP(wakeup_ret, void *, resumectx); 411#ifndef __amd64__ 412#if defined(PAE) || defined(PAE_TABLES) 413 WAKECODE_FIXUP(wakeup_cr3, register_t, vtophys(kernel_pmap->pm_pdpt)); 414#else 415 WAKECODE_FIXUP(wakeup_cr3, register_t, vtophys(kernel_pmap->pm_pdir)); 416#endif 417 418#else /* __amd64__ */ 419 /* Create the initial 1GB replicated page tables */ 420 for (i = 0; i < 512; i++) { 421 /* 422 * Each slot of the level 4 pages points 423 * to the same level 3 page 424 */ 425 pt4[i] = (uint64_t)pt3pa; 426 pt4[i] |= PG_V | PG_RW | PG_U; 427 428 /* 429 * Each slot of the level 3 pages points 430 * to the same level 2 page 431 */ 432 pt3[i] = (uint64_t)pt2pa; 433 pt3[i] |= PG_V | PG_RW | PG_U; 434 435 /* The level 2 page slots are mapped with 2MB pages for 1GB. */ 436 pt2[i] = i * (2 * 1024 * 1024); 437 pt2[i] |= PG_V | PG_RW | PG_PS | PG_U; 438 } 439#endif /* !__amd64__ */ 440 441 if (bootverbose) 442 device_printf(sc->acpi_dev, "wakeup code va %#jx pa %#jx\n", 443 (uintmax_t)sc->acpi_wakeaddr, (uintmax_t)sc->acpi_wakephys); 444} 445