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