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