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