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