xref: /netbsd-src/sys/arch/x86/acpi/acpi_wakeup.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: acpi_wakeup.c,v 1.34 2013/12/01 01:05:16 christos 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 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: acpi_wakeup.c,v 1.34 2013/12/01 01:05:16 christos Exp $");
34 
35 /*-
36  * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
37  * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
38  * All rights reserved.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions
42  * are met:
43  * 1. Redistributions of source code must retain the above copyright
44  *    notice, this list of conditions and the following disclaimer.
45  * 2. Redistributions in binary form must reproduce the above copyright
46  *    notice, this list of conditions and the following disclaimer in the
47  *    documentation and/or other materials provided with the distribution.
48  *
49  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
50  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
53  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59  * SUCH DAMAGE.
60  *
61  *      FreeBSD: src/sys/i386/acpica/acpi_wakeup.c,v 1.9 2002/01/10 03:26:46 wes Exp
62  */
63 
64 #include <sys/cdefs.h>
65 __KERNEL_RCSID(0, "$NetBSD: acpi_wakeup.c,v 1.34 2013/12/01 01:05:16 christos Exp $");
66 
67 #include <sys/param.h>
68 #include <sys/systm.h>
69 #include <sys/kernel.h>
70 #include <sys/bus.h>
71 #include <sys/cpu.h>
72 #include <sys/kcpuset.h>
73 #include <sys/sysctl.h>
74 
75 #include <uvm/uvm_extern.h>
76 #include <uvm/uvm_page.h>
77 
78 #ifdef __i386__
79 #include "opt_mtrr.h"
80 #endif
81 #include "ioapic.h"
82 #include "lapic.h"
83 
84 #if NLAPIC > 0
85 #include <machine/i82489var.h>
86 #endif
87 #if NIOAPIC > 0
88 #include <machine/i82093var.h>
89 #endif
90 #include <machine/i8259.h>
91 
92 #include "acpica.h"
93 
94 #include <dev/ic/i8253reg.h>
95 #include <dev/acpi/acpica.h>
96 #include <dev/acpi/acpivar.h>
97 #define ACPI_MACHDEP_PRIVATE
98 #include <machine/acpi_machdep.h>
99 #include <machine/cpu.h>
100 #ifdef __i386__
101 #  include <machine/npx.h>
102 #else
103 #  include <machine/fpu.h>
104 #endif
105 #include <machine/mtrr.h>
106 
107 #include <x86/cpuvar.h>
108 #include <x86/x86/tsc.h>
109 
110 #include "opt_vga.h"
111 
112 #include "acpi_wakecode.h"
113 
114 /* Address is also hard-coded in acpi_wakecode.S */
115 static paddr_t acpi_wakeup_paddr = 3 * PAGE_SIZE;
116 static vaddr_t acpi_wakeup_vaddr;
117 
118 int acpi_md_vbios_reset = 1; /* Referenced by dev/pci/vga_pci.c */
119 int acpi_md_vesa_modenum = 0; /* Referenced by arch/x86/x86/genfb_machdep.c */
120 static int acpi_md_beep_on_reset = 0;
121 
122 static int	acpi_md_s4bios(void);
123 static int	sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS);
124 static int	sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS);
125 
126 /* Implemented in acpi_wakeup_low.S. */
127 int	acpi_md_sleep_prepare(int);
128 int	acpi_md_sleep_exit(int);
129 
130 /* Referenced by acpi_wakeup_low.S. */
131 void	acpi_md_sleep_enter(int);
132 
133 #ifdef MULTIPROCESSOR
134 /* Referenced in ipifuncs.c. */
135 void	acpi_cpu_sleep(struct cpu_info *);
136 #endif
137 
138 static void
139 acpi_md_sleep_patch(struct cpu_info *ci)
140 {
141 #define WAKECODE_FIXUP(offset, type, val) do	{		\
142 	type	*addr;						\
143 	addr = (type *)(acpi_wakeup_vaddr + offset);		\
144 	*addr = val;						\
145 } while (0)
146 
147 #define WAKECODE_BCOPY(offset, type, val) do	{		\
148 	void	**addr;						\
149 	addr = (void **)(acpi_wakeup_vaddr + offset);		\
150 	memcpy(addr, &(val), sizeof(type));			\
151 } while (0)
152 
153 	paddr_t				tmp_pdir;
154 
155 	tmp_pdir = pmap_init_tmp_pgtbl(acpi_wakeup_paddr);
156 
157 	/* Execute Sleep */
158 	memcpy((void *)acpi_wakeup_vaddr, wakecode, sizeof(wakecode));
159 
160 	if (CPU_IS_PRIMARY(ci)) {
161 		WAKECODE_FIXUP(WAKEUP_vesa_modenum, uint16_t, acpi_md_vesa_modenum);
162 		WAKECODE_FIXUP(WAKEUP_vbios_reset, uint8_t, acpi_md_vbios_reset);
163 		WAKECODE_FIXUP(WAKEUP_beep_on_reset, uint8_t, acpi_md_beep_on_reset);
164 	} else {
165 		WAKECODE_FIXUP(WAKEUP_vesa_modenum, uint16_t, 0);
166 		WAKECODE_FIXUP(WAKEUP_vbios_reset, uint8_t, 0);
167 		WAKECODE_FIXUP(WAKEUP_beep_on_reset, uint8_t, 0);
168 	}
169 
170 #ifdef __i386__
171 	WAKECODE_FIXUP(WAKEUP_r_cr4, uint32_t, ci->ci_suspend_cr4);
172 #else
173 	WAKECODE_FIXUP(WAKEUP_efer, uint32_t, ci->ci_suspend_efer);
174 #endif
175 
176 	WAKECODE_FIXUP(WAKEUP_curcpu, void *, ci);
177 #ifdef __i386__
178 	WAKECODE_FIXUP(WAKEUP_r_cr3, uint32_t, tmp_pdir);
179 #else
180 	WAKECODE_FIXUP(WAKEUP_r_cr3, uint64_t, tmp_pdir);
181 #endif
182 	WAKECODE_FIXUP(WAKEUP_restorecpu, void *, acpi_md_sleep_exit);
183 #undef WAKECODE_FIXUP
184 #undef WAKECODE_BCOPY
185 }
186 
187 static int
188 acpi_md_s4bios(void)
189 {
190 	ACPI_TABLE_FACS *facs;
191 	ACPI_STATUS rv;
192 
193 	rv = AcpiGetTable(ACPI_SIG_FACS, 0, (ACPI_TABLE_HEADER **)&facs);
194 
195 	if (ACPI_FAILURE(rv) || facs == NULL)
196 		return 0;
197 
198 	if ((facs->Flags & ACPI_FACS_S4_BIOS_PRESENT) == 0)
199 		return 0;
200 
201 	return 1;
202 }
203 
204 void
205 acpi_md_sleep_enter(int state)
206 {
207 	static int s4bios = -1;
208 	struct cpu_info *ci;
209 	ACPI_STATUS rv;
210 
211 	ci = curcpu();
212 
213 #ifdef MULTIPROCESSOR
214 	if (!CPU_IS_PRIMARY(ci)) {
215 		atomic_and_32(&ci->ci_flags, ~CPUF_RUNNING);
216 		kcpuset_atomic_clear(kcpuset_running, cpu_index(ci));
217 
218 		ACPI_FLUSH_CPU_CACHE();
219 
220 		for (;;)
221 			x86_hlt();
222 	}
223 #endif
224 
225 	acpi_md_sleep_patch(ci);
226 
227 	ACPI_FLUSH_CPU_CACHE();
228 
229 	switch (state) {
230 
231 	case ACPI_STATE_S4:
232 
233 		if (s4bios < 0)
234 			s4bios = acpi_md_s4bios();
235 
236 		if (s4bios == 0) {
237 			aprint_error("acpi0: S4 not supported\n");
238 			return;
239 		}
240 
241 		rv = AcpiEnterSleepStateS4bios();
242 		break;
243 
244 	default:
245 		rv = AcpiEnterSleepState(state);
246 		break;
247 	}
248 
249 	if (ACPI_FAILURE(rv)) {
250 		aprint_error("acpi0: failed to enter S%d\n", state);
251 		return;
252 	}
253 
254 	for (;;)
255 		x86_hlt();
256 }
257 
258 #ifdef MULTIPROCESSOR
259 void
260 acpi_cpu_sleep(struct cpu_info *ci)
261 {
262 	KASSERT(!CPU_IS_PRIMARY(ci));
263 	KASSERT(ci == curcpu());
264 
265 	x86_disable_intr();
266 
267 	if (acpi_md_sleep_prepare(-1))
268 		return;
269 
270 	/* Execute Wakeup */
271 #ifdef __i386__
272 	npxinit(ci);
273 #else
274 	cpu_init_msrs(ci, false);
275 	fpuinit(ci);
276 #endif
277 #if NLAPIC > 0
278 	lapic_enable();
279 	lapic_set_lvt();
280 	lapic_initclocks();
281 #endif
282 
283 	atomic_or_32(&ci->ci_flags, CPUF_RUNNING);
284 	kcpuset_atomic_set(kcpuset_running, cpu_index(ci));
285 	tsc_sync_ap(ci);
286 
287 	x86_enable_intr();
288 }
289 #endif
290 
291 int
292 acpi_md_sleep(int state)
293 {
294 	int s, ret = 0;
295 #ifdef MULTIPROCESSOR
296 	struct cpu_info *ci;
297 	CPU_INFO_ITERATOR cii;
298 	cpuid_t cid;
299 #endif
300 
301 	KASSERT(acpi_wakeup_paddr != 0);
302 	KASSERT(sizeof(wakecode) <= PAGE_SIZE);
303 
304 	if (!CPU_IS_PRIMARY(curcpu())) {
305 		printf("acpi0: WARNING: ignoring sleep from secondary CPU\n");
306 		return -1;
307 	}
308 
309 	AcpiSetFirmwareWakingVector(acpi_wakeup_paddr);
310 
311 	s = splhigh();
312 #ifdef __i386__
313 	npxsave_cpu(true);
314 #else
315 	fpusave_cpu(true);
316 #endif
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 	if (acpi_md_sleep_prepare(state))
329 		goto out;
330 
331 	/* Execute Wakeup */
332 #ifdef __i386__
333 	npxinit(&cpu_info_primary);
334 #else
335 	cpu_init_msrs(&cpu_info_primary, false);
336 	fpuinit(&cpu_info_primary);
337 #endif
338 	i8259_reinit();
339 #if NLAPIC > 0
340 	lapic_enable();
341 	lapic_set_lvt();
342 	lapic_initclocks();
343 #endif
344 #if NIOAPIC > 0
345 	ioapic_reenable();
346 #endif
347 
348 	initrtclock(TIMER_FREQ);
349 	inittodr(time_second);
350 
351 	/*
352 	 * The BIOS should always re-enable the SCI upon
353 	 * resume from the S3 state. The following is a
354 	 * workaround for systems that fail to do this.
355 	 */
356 	(void)AcpiWriteBitRegister(ACPI_BITREG_SCI_ENABLE, 1);
357 
358 	/*
359 	 * Clear fixed events (see e.g. ACPI 3.0, p. 62).
360 	 * Also prevent GPEs from misfiring by disabling
361 	 * all GPEs before interrupts are enabled. The
362 	 * AcpiLeaveSleepState() function will enable
363 	 * and handle the general purpose events later.
364 	 */
365 	(void)AcpiClearEvent(ACPI_EVENT_PMTIMER);
366 	(void)AcpiClearEvent(ACPI_EVENT_GLOBAL);
367 	(void)AcpiClearEvent(ACPI_EVENT_POWER_BUTTON);
368 	(void)AcpiClearEvent(ACPI_EVENT_SLEEP_BUTTON);
369 	(void)AcpiClearEvent(ACPI_EVENT_RTC);
370 	(void)AcpiHwDisableAllGpes();
371 
372 	acpi_pci_link_resume();
373 
374 out:
375 
376 #ifdef MULTIPROCESSOR
377 	for (CPU_INFO_FOREACH(cii, ci)) {
378 		if (CPU_IS_PRIMARY(ci))
379 			continue;
380 		acpi_md_sleep_patch(ci);
381 
382 		CPU_STARTUP(ci, acpi_wakeup_paddr);
383 		CPU_START_CLEANUP(ci);
384 
385 		while ((ci->ci_flags & CPUF_RUNNING) == 0)
386 			x86_pause();
387 
388 		tsc_sync_bp(ci);
389 	}
390 #endif
391 
392 	x86_enable_intr();
393 	splx(s);
394 
395 #ifdef MTRR
396 	if (mtrr_funcs != NULL)
397 		mtrr_commit();
398 #endif
399 
400 	return (ret);
401 }
402 
403 void
404 acpi_md_sleep_init(void)
405 {
406 	/* Map ACPI wakecode */
407 	acpi_wakeup_vaddr = uvm_km_alloc(kernel_map, PAGE_SIZE, 0,
408 	    UVM_KMF_VAONLY);
409 	if (acpi_wakeup_vaddr == 0)
410 		panic("acpi: can't allocate address for wakecode.\n");
411 
412 	pmap_kenter_pa(acpi_wakeup_vaddr, acpi_wakeup_paddr,
413 	    VM_PROT_READ | VM_PROT_WRITE, 0);
414 	pmap_update(pmap_kernel());
415 }
416 
417 SYSCTL_SETUP(sysctl_md_acpi_setup, "ACPI x86 sysctl setup")
418 {
419 	const struct sysctlnode *rnode;
420 	int err;
421 
422 	err = sysctl_createv(clog, 0, NULL, &rnode,
423 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw",
424 	    NULL, NULL, 0, NULL, 0, CTL_HW, CTL_EOL);
425 
426 	if (err != 0)
427 		return;
428 
429 	err = sysctl_createv(clog, 0, &rnode, &rnode,
430 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", NULL,
431 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
432 
433 	if (err != 0)
434 		return;
435 
436 	err = sysctl_createv(clog, 0, &rnode, &rnode,
437 	    CTLFLAG_PERMANENT, CTLTYPE_NODE,
438 	    "sleep", SYSCTL_DESCR("ACPI sleep"),
439 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
440 
441 	if (err != 0)
442 		return;
443 
444 	(void)sysctl_createv(NULL, 0, &rnode, NULL,
445 	    CTLFLAG_READWRITE, CTLTYPE_BOOL, "beep",
446 	    NULL, sysctl_md_acpi_beep_on_reset,
447 	    0, NULL, 0, CTL_CREATE, CTL_EOL);
448 
449 	(void)sysctl_createv(NULL, 0, &rnode, NULL,
450 	    CTLFLAG_READWRITE, CTLTYPE_INT, "vbios",
451 	    NULL, sysctl_md_acpi_vbios_reset,
452 	    0, NULL, 0, CTL_CREATE, CTL_EOL);
453 }
454 
455 static int
456 sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS)
457 {
458 	int error, t;
459 	struct sysctlnode node;
460 
461 	node = *rnode;
462 	t = acpi_md_vbios_reset;
463 	node.sysctl_data = &t;
464 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
465 	if (error || newp == NULL)
466 		return error;
467 
468 	if (t < 0 || t > 2)
469 		return EINVAL;
470 
471 #ifndef VGA_POST
472 	if (t == 2) {
473 		aprint_error("WARNING: hw.acpi.sleep.vbios=2 "
474 		    "unsupported (no option VGA_POST in kernel config)\n");
475 		return EINVAL;
476 	}
477 #endif
478 
479 	acpi_md_vbios_reset = t;
480 
481 	return 0;
482 }
483 
484 static int
485 sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS)
486 {
487 	int error, t;
488 	struct sysctlnode node;
489 
490 	node = *rnode;
491 	t = acpi_md_beep_on_reset;
492 	node.sysctl_data = &t;
493 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
494 	if (error || newp == NULL)
495 		return error;
496 
497 	if (t < 0 || t > 1)
498 		return EINVAL;
499 
500 	acpi_md_beep_on_reset = t;
501 
502 	return 0;
503 }
504