Home | History | Annotate | Line # | Download | only in acpi
      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 (at) jp.freebsd.org>
     34  * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki (at) 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
    110 int acpi_md_vesa_modenum = 0;
    111 #endif
    112 
    113 /* Address is also hard-coded in acpi_wakecode.S */
    114 static paddr_t acpi_wakeup_paddr = 3 * PAGE_SIZE;
    115 static vaddr_t acpi_wakeup_vaddr;
    116 
    117 static int acpi_md_beep_on_reset = 0;
    118 
    119 static int	acpi_md_s4bios(void);
    120 static int	sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS);
    121 static int	sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS);
    122 
    123 /* Implemented in acpi_wakeup_low.S. */
    124 int	acpi_md_sleep_prepare(int);
    125 int	acpi_md_sleep_exit(int);
    126 
    127 /* Referenced by acpi_wakeup_low.S. */
    128 void	acpi_md_sleep_enter(int);
    129 
    130 #ifdef MULTIPROCESSOR
    131 /* Referenced in ipifuncs.c. */
    132 void	acpi_cpu_sleep(struct cpu_info *);
    133 #endif
    134 
    135 static void
    136 acpi_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 
    174 static int
    175 acpi_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 
    191 void
    192 acpi_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
    246 void
    247 acpi_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 
    292 out:
    293 	x86_enable_intr();
    294 	splx(s);
    295 }
    296 #endif
    297 
    298 int
    299 acpi_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 
    392 out:
    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 
    422 void
    423 acpi_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 
    436 SYSCTL_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 
    467 static int
    468 sysctl_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 
    496 static int
    497 sysctl_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