xref: /netbsd-src/sys/arch/x86/acpi/acpi_wakeup.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: acpi_wakeup.c,v 1.49 2017/09/23 10:38:59 maxv 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.49 2017/09/23 10:38:59 maxv 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 #include <uvm/uvm_page.h>
74 
75 #ifdef __i386__
76 #include "opt_mtrr.h"
77 #endif
78 #include "ioapic.h"
79 #include "lapic.h"
80 
81 #if NLAPIC > 0
82 #include <machine/i82489var.h>
83 #endif
84 #if NIOAPIC > 0
85 #include <machine/i82093var.h>
86 #endif
87 #include <machine/i8259.h>
88 
89 #include "acpica.h"
90 
91 #include <dev/ic/i8253reg.h>
92 #include <dev/acpi/acpica.h>
93 #include <dev/acpi/acpivar.h>
94 #define ACPI_MACHDEP_PRIVATE
95 #include <machine/acpi_machdep.h>
96 #include <machine/cpu.h>
97 #include <machine/mtrr.h>
98 
99 #include <x86/cpuvar.h>
100 #include <x86/x86/tsc.h>
101 #include <x86/fpu.h>
102 
103 #include "opt_vga.h"
104 
105 #include "acpi_wakecode.h"
106 
107 /* Address is also hard-coded in acpi_wakecode.S */
108 static paddr_t acpi_wakeup_paddr = 3 * PAGE_SIZE;
109 static vaddr_t acpi_wakeup_vaddr;
110 
111 int acpi_md_vbios_reset = 0; /* Referenced by dev/pci/vga_pci.c */
112 int acpi_md_vesa_modenum = 0; /* Referenced by arch/x86/x86/genfb_machdep.c */
113 static int acpi_md_beep_on_reset = 0;
114 
115 static int	acpi_md_s4bios(void);
116 static int	sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS);
117 static int	sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS);
118 
119 /* Implemented in acpi_wakeup_low.S. */
120 int	acpi_md_sleep_prepare(int);
121 int	acpi_md_sleep_exit(int);
122 
123 /* Referenced by acpi_wakeup_low.S. */
124 void	acpi_md_sleep_enter(int);
125 
126 #ifdef MULTIPROCESSOR
127 /* Referenced in ipifuncs.c. */
128 void	acpi_cpu_sleep(struct cpu_info *);
129 #endif
130 
131 static void
132 acpi_md_sleep_patch(struct cpu_info *ci)
133 {
134 #define WAKECODE_FIXUP(offset, type, val) do	{		\
135 	type	*addr;						\
136 	addr = (type *)(acpi_wakeup_vaddr + offset);		\
137 	*addr = val;						\
138 } while (0)
139 
140 	paddr_t				tmp_pdir;
141 
142 	tmp_pdir = pmap_init_tmp_pgtbl(acpi_wakeup_paddr);
143 
144 	memcpy((void *)acpi_wakeup_vaddr, wakecode, sizeof(wakecode));
145 
146 	if (CPU_IS_PRIMARY(ci)) {
147 		WAKECODE_FIXUP(WAKEUP_vesa_modenum, uint16_t, acpi_md_vesa_modenum);
148 		WAKECODE_FIXUP(WAKEUP_vbios_reset, uint8_t, acpi_md_vbios_reset);
149 		WAKECODE_FIXUP(WAKEUP_beep_on_reset, uint8_t, acpi_md_beep_on_reset);
150 	} else {
151 		WAKECODE_FIXUP(WAKEUP_vesa_modenum, uint16_t, 0);
152 		WAKECODE_FIXUP(WAKEUP_vbios_reset, uint8_t, 0);
153 		WAKECODE_FIXUP(WAKEUP_beep_on_reset, uint8_t, 0);
154 	}
155 
156 #ifdef __i386__
157 	WAKECODE_FIXUP(WAKEUP_r_cr4, uint32_t, ci->ci_suspend_cr4);
158 #endif
159 	WAKECODE_FIXUP(WAKEUP_efer, uint32_t, ci->ci_suspend_efer);
160 	WAKECODE_FIXUP(WAKEUP_curcpu, void *, ci);
161 #ifdef __i386__
162 	WAKECODE_FIXUP(WAKEUP_r_cr3, uint32_t, tmp_pdir);
163 #else
164 	WAKECODE_FIXUP(WAKEUP_r_cr3, uint64_t, tmp_pdir);
165 #endif
166 	WAKECODE_FIXUP(WAKEUP_restorecpu, void *, acpi_md_sleep_exit);
167 #undef WAKECODE_FIXUP
168 }
169 
170 static int
171 acpi_md_s4bios(void)
172 {
173 	ACPI_TABLE_FACS *facs;
174 	ACPI_STATUS rv;
175 
176 	rv = AcpiGetTable(ACPI_SIG_FACS, 0, (ACPI_TABLE_HEADER **)&facs);
177 
178 	if (ACPI_FAILURE(rv) || facs == NULL)
179 		return 0;
180 
181 	if ((facs->Flags & ACPI_FACS_S4_BIOS_PRESENT) == 0)
182 		return 0;
183 
184 	return 1;
185 }
186 
187 void
188 acpi_md_sleep_enter(int state)
189 {
190 	static int s4bios = -1;
191 	struct cpu_info *ci;
192 	ACPI_STATUS rv;
193 
194 	ci = curcpu();
195 
196 #ifdef MULTIPROCESSOR
197 	if (!CPU_IS_PRIMARY(ci)) {
198 		atomic_and_32(&ci->ci_flags, ~CPUF_RUNNING);
199 		kcpuset_atomic_clear(kcpuset_running, cpu_index(ci));
200 
201 		ACPI_FLUSH_CPU_CACHE();
202 
203 		for (;;)
204 			x86_hlt();
205 	}
206 #endif
207 
208 	acpi_md_sleep_patch(ci);
209 
210 	ACPI_FLUSH_CPU_CACHE();
211 
212 	switch (state) {
213 
214 	case ACPI_STATE_S4:
215 
216 		if (s4bios < 0)
217 			s4bios = acpi_md_s4bios();
218 
219 		if (s4bios == 0) {
220 			aprint_error("acpi0: S4 not supported\n");
221 			return;
222 		}
223 
224 		rv = AcpiEnterSleepStateS4bios();
225 		break;
226 
227 	default:
228 		rv = AcpiEnterSleepState(state);
229 		break;
230 	}
231 
232 	if (ACPI_FAILURE(rv)) {
233 		aprint_error("acpi0: failed to enter S%d\n", state);
234 		return;
235 	}
236 
237 	for (;;)
238 		x86_hlt();
239 }
240 
241 #ifdef MULTIPROCESSOR
242 void
243 acpi_cpu_sleep(struct cpu_info *ci)
244 {
245 	uint64_t xcr0 = 0;
246 	int s;
247 
248 	KASSERT(!CPU_IS_PRIMARY(ci));
249 	KASSERT(ci == curcpu());
250 
251 	s = splhigh();
252 	fpusave_cpu(true);
253 	x86_disable_intr();
254 
255 	/*
256 	 * XXX also need to save the PMCs, the dbregs, and probably a few
257 	 * MSRs too.
258 	 */
259 	if (rcr4() & CR4_OSXSAVE)
260 		xcr0 = rdxcr(0);
261 
262 	/* Go get some sleep */
263 	if (acpi_md_sleep_prepare(-1))
264 		goto out;
265 
266 	/*
267 	 * Sleeping and having bad nightmares about what could go wrong
268 	 * when waking up.
269 	 */
270 
271 	/* We just woke up (cpuN), execution is resumed here */
272 	cpu_init_msrs(ci, false);
273 	fpuinit(ci);
274 	if (rcr4() & CR4_OSXSAVE)
275 		wrxcr(0, xcr0);
276 	pat_init(ci);
277 	x86_errata();
278 #if NLAPIC > 0
279 	lapic_enable();
280 	lapic_set_lvt();
281 	lapic_initclocks();
282 #endif
283 
284 	atomic_or_32(&ci->ci_flags, CPUF_RUNNING);
285 	kcpuset_atomic_set(kcpuset_running, cpu_index(ci));
286 	tsc_sync_ap(ci);
287 
288 out:
289 	x86_enable_intr();
290 	splx(s);
291 }
292 #endif
293 
294 int
295 acpi_md_sleep(int state)
296 {
297 	uint64_t xcr0 = 0;
298 	int s, ret = 0;
299 #ifdef MULTIPROCESSOR
300 	struct cpu_info *ci;
301 	CPU_INFO_ITERATOR cii;
302 	cpuid_t cid;
303 #endif
304 
305 	KASSERT(acpi_wakeup_paddr != 0);
306 	KASSERT(sizeof(wakecode) <= PAGE_SIZE);
307 
308 	if (!CPU_IS_PRIMARY(curcpu())) {
309 		printf("acpi0: WARNING: ignoring sleep from secondary CPU\n");
310 		return -1;
311 	}
312 
313 	AcpiSetFirmwareWakingVector(acpi_wakeup_paddr, acpi_wakeup_paddr);
314 
315 	s = splhigh();
316 	fpusave_cpu(true);
317 	x86_disable_intr();
318 
319 #ifdef MULTIPROCESSOR
320 	/* Save and suspend Application Processors. */
321 	x86_broadcast_ipi(X86_IPI_ACPI_CPU_SLEEP);
322 	cid = cpu_index(curcpu());
323 	while (kcpuset_isotherset(kcpuset_running, cid)) {
324 		delay(1);
325 	}
326 #endif
327 
328 	/*
329 	 * XXX also need to save the PMCs, the dbregs, and probably a few
330 	 * MSRs too.
331 	 */
332 	if (rcr4() & CR4_OSXSAVE)
333 		xcr0 = rdxcr(0);
334 
335 	/* Go get some sleep */
336 	if (acpi_md_sleep_prepare(state))
337 		goto out;
338 
339 	/*
340 	 * Sleeping and having bad nightmares about what could go wrong
341 	 * when waking up.
342 	 */
343 
344 	/* We just woke up (cpu0), execution is resumed here */
345 	cpu_init_msrs(&cpu_info_primary, false);
346 	fpuinit(&cpu_info_primary);
347 	if (rcr4() & CR4_OSXSAVE)
348 		wrxcr(0, xcr0);
349 	pat_init(&cpu_info_primary);
350 	x86_errata();
351 	i8259_reinit();
352 #if NLAPIC > 0
353 	lapic_enable();
354 	lapic_set_lvt();
355 	lapic_initclocks();
356 #endif
357 #if NIOAPIC > 0
358 	ioapic_reenable();
359 #endif
360 
361 	initrtclock(TIMER_FREQ);
362 	inittodr(time_second);
363 
364 	/*
365 	 * The BIOS should always re-enable the SCI upon
366 	 * resume from the S3 state. The following is a
367 	 * workaround for systems that fail to do this.
368 	 */
369 	(void)AcpiWriteBitRegister(ACPI_BITREG_SCI_ENABLE, 1);
370 
371 	/*
372 	 * Clear fixed events (see e.g. ACPI 3.0, p. 62).
373 	 * Also prevent GPEs from misfiring by disabling
374 	 * all GPEs before interrupts are enabled. The
375 	 * AcpiLeaveSleepState() function will enable
376 	 * and handle the general purpose events later.
377 	 */
378 	(void)AcpiClearEvent(ACPI_EVENT_PMTIMER);
379 	(void)AcpiClearEvent(ACPI_EVENT_GLOBAL);
380 	(void)AcpiClearEvent(ACPI_EVENT_POWER_BUTTON);
381 	(void)AcpiClearEvent(ACPI_EVENT_SLEEP_BUTTON);
382 	(void)AcpiClearEvent(ACPI_EVENT_RTC);
383 	(void)AcpiHwDisableAllGpes();
384 
385 	acpi_pci_link_resume();
386 
387 out:
388 
389 #ifdef MULTIPROCESSOR
390 	/* Wake up the secondary CPUs */
391 	for (CPU_INFO_FOREACH(cii, ci)) {
392 		if (CPU_IS_PRIMARY(ci))
393 			continue;
394 		acpi_md_sleep_patch(ci);
395 
396 		CPU_STARTUP(ci, acpi_wakeup_paddr);
397 		CPU_START_CLEANUP(ci);
398 
399 		while ((ci->ci_flags & CPUF_RUNNING) == 0)
400 			x86_pause();
401 
402 		tsc_sync_bp(ci);
403 	}
404 #endif
405 
406 	x86_enable_intr();
407 	splx(s);
408 
409 #ifdef MTRR
410 	if (mtrr_funcs != NULL)
411 		mtrr_commit();
412 #endif
413 
414 	return (ret);
415 }
416 
417 void
418 acpi_md_sleep_init(void)
419 {
420 	/* Map ACPI wakecode */
421 	acpi_wakeup_vaddr = uvm_km_alloc(kernel_map, PAGE_SIZE, 0,
422 	    UVM_KMF_VAONLY);
423 	if (acpi_wakeup_vaddr == 0)
424 		panic("acpi: can't allocate address for wakecode.\n");
425 
426 	pmap_kenter_pa(acpi_wakeup_vaddr, acpi_wakeup_paddr,
427 	    VM_PROT_READ | VM_PROT_WRITE, 0);
428 	pmap_update(pmap_kernel());
429 }
430 
431 SYSCTL_SETUP(sysctl_md_acpi_setup, "ACPI x86 sysctl setup")
432 {
433 	const struct sysctlnode *rnode;
434 	int err;
435 
436 	err = sysctl_createv(clog, 0, NULL, &rnode,
437 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", NULL,
438 	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
439 
440 	if (err != 0)
441 		return;
442 
443 	err = sysctl_createv(clog, 0, &rnode, &rnode,
444 	    CTLFLAG_PERMANENT, CTLTYPE_NODE,
445 	    "sleep", SYSCTL_DESCR("ACPI sleep"),
446 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
447 
448 	if (err != 0)
449 		return;
450 
451 	(void)sysctl_createv(NULL, 0, &rnode, NULL,
452 	    CTLFLAG_READWRITE, CTLTYPE_BOOL, "beep",
453 	    NULL, sysctl_md_acpi_beep_on_reset,
454 	    0, NULL, 0, CTL_CREATE, CTL_EOL);
455 
456 	(void)sysctl_createv(NULL, 0, &rnode, NULL,
457 	    CTLFLAG_READWRITE, CTLTYPE_INT, "vbios",
458 	    NULL, sysctl_md_acpi_vbios_reset,
459 	    0, NULL, 0, CTL_CREATE, CTL_EOL);
460 }
461 
462 static int
463 sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS)
464 {
465 	int error, t;
466 	struct sysctlnode node;
467 
468 	node = *rnode;
469 	t = acpi_md_vbios_reset;
470 	node.sysctl_data = &t;
471 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
472 	if (error || newp == NULL)
473 		return error;
474 
475 	if (t < 0 || t > 2)
476 		return EINVAL;
477 
478 #ifndef VGA_POST
479 	if (t == 2) {
480 		aprint_error("WARNING: hw.acpi.sleep.vbios=2 "
481 		    "unsupported (no option VGA_POST in kernel config)\n");
482 		return EINVAL;
483 	}
484 #endif
485 
486 	acpi_md_vbios_reset = t;
487 
488 	return 0;
489 }
490 
491 static int
492 sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS)
493 {
494 	int error, t;
495 	struct sysctlnode node;
496 
497 	node = *rnode;
498 	t = acpi_md_beep_on_reset;
499 	node.sysctl_data = &t;
500 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
501 	if (error || newp == NULL)
502 		return error;
503 
504 	if (t < 0 || t > 1)
505 		return EINVAL;
506 
507 	acpi_md_beep_on_reset = t;
508 
509 	return 0;
510 }
511