acpi_wakeup.c revision 302147
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: head/sys/x86/acpica/acpi_wakeup.c 302147 2016-06-23 19:24:38Z markj $"); 32 33#if defined(__amd64__) 34#define DEV_APIC 35#else 36#include "opt_apic.h" 37#endif 38#ifdef __i386__ 39#include "opt_npx.h" 40#endif 41 42#include <sys/param.h> 43#include <sys/bus.h> 44#include <sys/eventhandler.h> 45#include <sys/kernel.h> 46#include <sys/malloc.h> 47#include <sys/memrange.h> 48#include <sys/smp.h> 49#include <sys/systm.h> 50 51#include <vm/vm.h> 52#include <vm/pmap.h> 53 54#include <machine/clock.h> 55#include <machine/cpu.h> 56#include <machine/intr_machdep.h> 57#include <x86/mca.h> 58#include <machine/pcb.h> 59#include <machine/specialreg.h> 60#include <machine/md_var.h> 61 62#ifdef DEV_APIC 63#include <x86/apicreg.h> 64#include <x86/apicvar.h> 65#endif 66#ifdef SMP 67#include <machine/smp.h> 68#include <machine/vmparam.h> 69#endif 70 71#include <contrib/dev/acpica/include/acpi.h> 72 73#include <dev/acpica/acpivar.h> 74 75#include "acpi_wakecode.h" 76#include "acpi_wakedata.h" 77 78/* Make sure the code is less than a page and leave room for the stack. */ 79CTASSERT(sizeof(wakecode) < PAGE_SIZE - 1024); 80 81extern int acpi_resume_beep; 82extern int acpi_reset_video; 83 84#ifdef SMP 85extern struct susppcb **susppcbs; 86static cpuset_t suspcpus; 87#else 88static struct susppcb **susppcbs; 89#endif 90 91static void *acpi_alloc_wakeup_handler(void); 92static void acpi_stop_beep(void *); 93 94#ifdef SMP 95static int acpi_wakeup_ap(struct acpi_softc *, int); 96static void acpi_wakeup_cpus(struct acpi_softc *); 97#endif 98 99#ifdef __amd64__ 100#define ACPI_PAGETABLES 3 101#else 102#define ACPI_PAGETABLES 0 103#endif 104 105#define WAKECODE_VADDR(sc) \ 106 ((sc)->acpi_wakeaddr + (ACPI_PAGETABLES * PAGE_SIZE)) 107#define WAKECODE_PADDR(sc) \ 108 ((sc)->acpi_wakephys + (ACPI_PAGETABLES * PAGE_SIZE)) 109#define WAKECODE_FIXUP(offset, type, val) do { \ 110 type *addr; \ 111 addr = (type *)(WAKECODE_VADDR(sc) + offset); \ 112 *addr = val; \ 113} while (0) 114 115static void 116acpi_stop_beep(void *arg) 117{ 118 119 if (acpi_resume_beep != 0) 120 timer_spkr_release(); 121} 122 123#ifdef SMP 124static int 125acpi_wakeup_ap(struct acpi_softc *sc, int cpu) 126{ 127 struct pcb *pcb; 128 int vector = (WAKECODE_PADDR(sc) >> 12) & 0xff; 129 int apic_id = cpu_apic_ids[cpu]; 130 int ms; 131 132 pcb = &susppcbs[cpu]->sp_pcb; 133 WAKECODE_FIXUP(wakeup_pcb, struct pcb *, pcb); 134 WAKECODE_FIXUP(wakeup_gdt, uint16_t, pcb->pcb_gdt.rd_limit); 135 WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t, pcb->pcb_gdt.rd_base); 136 137 ipi_startup(apic_id, vector); 138 139 /* Wait up to 5 seconds for it to resume. */ 140 for (ms = 0; ms < 5000; ms++) { 141 if (!CPU_ISSET(cpu, &suspended_cpus)) 142 return (1); /* return SUCCESS */ 143 DELAY(1000); 144 } 145 return (0); /* return FAILURE */ 146} 147 148#define WARMBOOT_TARGET 0 149#define WARMBOOT_OFF (KERNBASE + 0x0467) 150#define WARMBOOT_SEG (KERNBASE + 0x0469) 151 152#define CMOS_REG (0x70) 153#define CMOS_DATA (0x71) 154#define BIOS_RESET (0x0f) 155#define BIOS_WARM (0x0a) 156 157static void 158acpi_wakeup_cpus(struct acpi_softc *sc) 159{ 160 uint32_t mpbioswarmvec; 161 int cpu; 162 u_char mpbiosreason; 163 164 /* save the current value of the warm-start vector */ 165 mpbioswarmvec = *((uint32_t *)WARMBOOT_OFF); 166 outb(CMOS_REG, BIOS_RESET); 167 mpbiosreason = inb(CMOS_DATA); 168 169 /* setup a vector to our boot code */ 170 *((volatile u_short *)WARMBOOT_OFF) = WARMBOOT_TARGET; 171 *((volatile u_short *)WARMBOOT_SEG) = WAKECODE_PADDR(sc) >> 4; 172 outb(CMOS_REG, BIOS_RESET); 173 outb(CMOS_DATA, BIOS_WARM); /* 'warm-start' */ 174 175 /* Wake up each AP. */ 176 for (cpu = 1; cpu < mp_ncpus; cpu++) { 177 if (!CPU_ISSET(cpu, &suspcpus)) 178 continue; 179 if (acpi_wakeup_ap(sc, cpu) == 0) { 180 /* restore the warmstart vector */ 181 *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec; 182 panic("acpi_wakeup: failed to resume AP #%d (PHY #%d)", 183 cpu, cpu_apic_ids[cpu]); 184 } 185 } 186 187 /* restore the warmstart vector */ 188 *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec; 189 190 outb(CMOS_REG, BIOS_RESET); 191 outb(CMOS_DATA, mpbiosreason); 192} 193#endif 194 195int 196acpi_sleep_machdep(struct acpi_softc *sc, int state) 197{ 198 ACPI_STATUS status; 199 struct pcb *pcb; 200 201 if (sc->acpi_wakeaddr == 0ul) 202 return (-1); /* couldn't alloc wake memory */ 203 204#ifdef SMP 205 suspcpus = all_cpus; 206 CPU_CLR(PCPU_GET(cpuid), &suspcpus); 207#endif 208 209 if (acpi_resume_beep != 0) 210 timer_spkr_acquire(); 211 212 AcpiSetFirmwareWakingVector(WAKECODE_PADDR(sc), 0); 213 214 intr_suspend(); 215 216 pcb = &susppcbs[0]->sp_pcb; 217 if (savectx(pcb)) { 218#ifdef __amd64__ 219 fpususpend(susppcbs[0]->sp_fpususpend); 220#elif defined(DEV_NPX) 221 npxsuspend(susppcbs[0]->sp_fpususpend); 222#endif 223#ifdef SMP 224 if (!CPU_EMPTY(&suspcpus) && suspend_cpus(suspcpus) == 0) { 225 device_printf(sc->acpi_dev, "Failed to suspend APs\n"); 226 return (0); /* couldn't sleep */ 227 } 228#endif 229 230 WAKECODE_FIXUP(resume_beep, uint8_t, (acpi_resume_beep != 0)); 231 WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0)); 232 233#ifndef __amd64__ 234 WAKECODE_FIXUP(wakeup_cr4, register_t, pcb->pcb_cr4); 235#endif 236 WAKECODE_FIXUP(wakeup_pcb, struct pcb *, pcb); 237 WAKECODE_FIXUP(wakeup_gdt, uint16_t, pcb->pcb_gdt.rd_limit); 238 WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t, pcb->pcb_gdt.rd_base); 239 240 /* Call ACPICA to enter the desired sleep state */ 241 if (state == ACPI_STATE_S4 && sc->acpi_s4bios) 242 status = AcpiEnterSleepStateS4bios(); 243 else 244 status = AcpiEnterSleepState(state); 245 if (ACPI_FAILURE(status)) { 246 device_printf(sc->acpi_dev, 247 "AcpiEnterSleepState failed - %s\n", 248 AcpiFormatException(status)); 249 return (0); /* couldn't sleep */ 250 } 251 252 for (;;) 253 ia32_pause(); 254 } else { 255#ifdef __amd64__ 256 fpuresume(susppcbs[0]->sp_fpususpend); 257#elif defined(DEV_NPX) 258 npxresume(susppcbs[0]->sp_fpususpend); 259#endif 260 } 261 262 return (1); /* wakeup successfully */ 263} 264 265int 266acpi_wakeup_machdep(struct acpi_softc *sc, int state, int sleep_result, 267 int intr_enabled) 268{ 269 270 if (sleep_result == -1) 271 return (sleep_result); 272 273 if (!intr_enabled) { 274 /* Wakeup MD procedures in interrupt disabled context */ 275 if (sleep_result == 1) { 276 pmap_init_pat(); 277 initializecpu(); 278 PCPU_SET(switchtime, 0); 279 PCPU_SET(switchticks, ticks); 280#ifdef DEV_APIC 281 lapic_xapic_mode(); 282#endif 283#ifdef SMP 284 if (!CPU_EMPTY(&suspcpus)) 285 acpi_wakeup_cpus(sc); 286#endif 287 } 288 289#ifdef SMP 290 if (!CPU_EMPTY(&suspcpus)) 291 restart_cpus(suspcpus); 292#endif 293 mca_resume(); 294#ifdef __amd64__ 295 if (vmm_resume_p != NULL) 296 vmm_resume_p(); 297#endif 298 intr_resume(/*suspend_cancelled*/false); 299 300 AcpiSetFirmwareWakingVector(0, 0); 301 } else { 302 /* Wakeup MD procedures in interrupt enabled context */ 303 if (sleep_result == 1 && mem_range_softc.mr_op != NULL && 304 mem_range_softc.mr_op->reinit != NULL) 305 mem_range_softc.mr_op->reinit(&mem_range_softc); 306 } 307 308 return (sleep_result); 309} 310 311static void * 312acpi_alloc_wakeup_handler(void) 313{ 314 void *wakeaddr; 315 int i; 316 317 /* 318 * Specify the region for our wakeup code. We want it in the low 1 MB 319 * region, excluding real mode IVT (0-0x3ff), BDA (0x400-0x4ff), EBDA 320 * (less than 128KB, below 0xa0000, must be excluded by SMAP and DSDT), 321 * and ROM area (0xa0000 and above). The temporary page tables must be 322 * page-aligned. 323 */ 324 wakeaddr = contigmalloc((ACPI_PAGETABLES + 1) * PAGE_SIZE, M_DEVBUF, 325 M_NOWAIT, 0x500, 0xa0000, PAGE_SIZE, 0ul); 326 if (wakeaddr == NULL) { 327 printf("%s: can't alloc wake memory\n", __func__); 328 return (NULL); 329 } 330 if (EVENTHANDLER_REGISTER(power_resume, acpi_stop_beep, NULL, 331 EVENTHANDLER_PRI_LAST) == NULL) { 332 printf("%s: can't register event handler\n", __func__); 333 contigfree(wakeaddr, (ACPI_PAGETABLES + 1) * PAGE_SIZE, 334 M_DEVBUF); 335 return (NULL); 336 } 337 susppcbs = malloc(mp_ncpus * sizeof(*susppcbs), M_DEVBUF, M_WAITOK); 338 for (i = 0; i < mp_ncpus; i++) { 339 susppcbs[i] = malloc(sizeof(**susppcbs), M_DEVBUF, M_WAITOK); 340 susppcbs[i]->sp_fpususpend = alloc_fpusave(M_WAITOK); 341 } 342 343 return (wakeaddr); 344} 345 346void 347acpi_install_wakeup_handler(struct acpi_softc *sc) 348{ 349 static void *wakeaddr = NULL; 350#ifdef __amd64__ 351 uint64_t *pt4, *pt3, *pt2; 352 int i; 353#endif 354 355 if (wakeaddr != NULL) 356 return; 357 358 wakeaddr = acpi_alloc_wakeup_handler(); 359 if (wakeaddr == NULL) 360 return; 361 362 sc->acpi_wakeaddr = (vm_offset_t)wakeaddr; 363 sc->acpi_wakephys = vtophys(wakeaddr); 364 365 bcopy(wakecode, (void *)WAKECODE_VADDR(sc), sizeof(wakecode)); 366 367 /* Patch GDT base address, ljmp targets. */ 368 WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t, 369 WAKECODE_PADDR(sc) + bootgdt); 370 WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t, 371 WAKECODE_PADDR(sc) + wakeup_32); 372#ifdef __amd64__ 373 WAKECODE_FIXUP((wakeup_sw64 + 1), uint32_t, 374 WAKECODE_PADDR(sc) + wakeup_64); 375 WAKECODE_FIXUP(wakeup_pagetables, uint32_t, sc->acpi_wakephys); 376#endif 377 378 /* Save pointers to some global data. */ 379 WAKECODE_FIXUP(wakeup_ret, void *, resumectx); 380#ifndef __amd64__ 381#if defined(PAE) || defined(PAE_TABLES) 382 WAKECODE_FIXUP(wakeup_cr3, register_t, vtophys(kernel_pmap->pm_pdpt)); 383#else 384 WAKECODE_FIXUP(wakeup_cr3, register_t, vtophys(kernel_pmap->pm_pdir)); 385#endif 386 387#else 388 /* Build temporary page tables below realmode code. */ 389 pt4 = wakeaddr; 390 pt3 = pt4 + (PAGE_SIZE) / sizeof(uint64_t); 391 pt2 = pt3 + (PAGE_SIZE) / sizeof(uint64_t); 392 393 /* Create the initial 1GB replicated page tables */ 394 for (i = 0; i < 512; i++) { 395 /* 396 * Each slot of the level 4 pages points 397 * to the same level 3 page 398 */ 399 pt4[i] = (uint64_t)(sc->acpi_wakephys + PAGE_SIZE); 400 pt4[i] |= PG_V | PG_RW | PG_U; 401 402 /* 403 * Each slot of the level 3 pages points 404 * to the same level 2 page 405 */ 406 pt3[i] = (uint64_t)(sc->acpi_wakephys + (2 * PAGE_SIZE)); 407 pt3[i] |= PG_V | PG_RW | PG_U; 408 409 /* The level 2 page slots are mapped with 2MB pages for 1GB. */ 410 pt2[i] = i * (2 * 1024 * 1024); 411 pt2[i] |= PG_V | PG_RW | PG_PS | PG_U; 412 } 413#endif 414 415 if (bootverbose) 416 device_printf(sc->acpi_dev, "wakeup code va %#jx pa %#jx\n", 417 (uintmax_t)sc->acpi_wakeaddr, (uintmax_t)sc->acpi_wakephys); 418} 419