xref: /netbsd-src/sys/arch/x86/acpi/acpi_wakeup.c (revision bbde328be4e75ea9ad02e9715ea13ca54b797ada)
1 /*	$NetBSD: acpi_wakeup.c,v 1.23 2010/04/14 19:32:35 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.23 2010/04/14 19:32:35 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 static int acpi_md_node = CTL_EOL;
115 int acpi_md_vbios_reset = 1; /* Referenced by dev/pci/vga_pci.c */
116 int acpi_md_vesa_modenum = 0; /* Referenced by arch/x86/x86/genfb_machdep.c */
117 static int acpi_md_beep_on_reset = 0;
118 
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 /* Referenced by acpi_wakeup_low.S. */
126 void	acpi_md_sleep_enter(int);
127 
128 #ifdef MULTIPROCESSOR
129 /* Referenced in ipifuncs.c. */
130 void	acpi_cpu_sleep(struct cpu_info *);
131 #endif
132 
133 static void
134 acpi_md_sleep_patch(struct cpu_info *ci)
135 {
136 #define WAKECODE_FIXUP(offset, type, val) do	{		\
137 	type	*addr;						\
138 	addr = (type *)(acpi_wakeup_vaddr + offset);		\
139 	*addr = val;						\
140 } while (0)
141 
142 #define WAKECODE_BCOPY(offset, type, val) do	{		\
143 	void	**addr;						\
144 	addr = (void **)(acpi_wakeup_vaddr + offset);		\
145 	memcpy(addr, &(val), sizeof(type));			\
146 } while (0)
147 
148 	paddr_t				tmp_pdir;
149 
150 	tmp_pdir = pmap_init_tmp_pgtbl(acpi_wakeup_paddr);
151 
152 	/* Execute Sleep */
153 	memcpy((void *)acpi_wakeup_vaddr, wakecode, sizeof(wakecode));
154 
155 	if (CPU_IS_PRIMARY(ci)) {
156 		WAKECODE_FIXUP(WAKEUP_vesa_modenum, uint16_t, acpi_md_vesa_modenum);
157 		WAKECODE_FIXUP(WAKEUP_vbios_reset, uint8_t, acpi_md_vbios_reset);
158 		WAKECODE_FIXUP(WAKEUP_beep_on_reset, uint8_t, acpi_md_beep_on_reset);
159 	} else {
160 		WAKECODE_FIXUP(WAKEUP_vesa_modenum, uint16_t, 0);
161 		WAKECODE_FIXUP(WAKEUP_vbios_reset, uint8_t, 0);
162 		WAKECODE_FIXUP(WAKEUP_beep_on_reset, uint8_t, 0);
163 	}
164 
165 #ifdef __i386__
166 	WAKECODE_FIXUP(WAKEUP_r_cr4, uint32_t, ci->ci_suspend_cr4);
167 #else
168 	WAKECODE_FIXUP(WAKEUP_efer, uint32_t, ci->ci_suspend_efer);
169 #endif
170 
171 	WAKECODE_FIXUP(WAKEUP_curcpu, void *, ci);
172 #ifdef __i386__
173 	WAKECODE_FIXUP(WAKEUP_r_cr3, uint32_t, tmp_pdir);
174 #else
175 	WAKECODE_FIXUP(WAKEUP_r_cr3, uint64_t, tmp_pdir);
176 #endif
177 	WAKECODE_FIXUP(WAKEUP_restorecpu, void *, acpi_md_sleep_exit);
178 #undef WAKECODE_FIXUP
179 #undef WAKECODE_BCOPY
180 }
181 
182 /*
183  * S4 sleep using S4BIOS support, from FreeBSD.
184  *
185  * FreeBSD: src/sys/dev/acpica/acpica_support.c,v 1.4 2002/03/12 09:45:17 dfr Exp
186  */
187 
188 static ACPI_STATUS
189 enter_s4_with_bios(void)
190 {
191 	ACPI_OBJECT_LIST	ArgList;
192 	ACPI_OBJECT		Arg;
193 	uint32_t		ret;
194 	ACPI_STATUS		status;
195 
196 	/* run the _PTS and _GTS methods */
197 
198 	(void)memset(&ArgList, 0, sizeof(ArgList));
199 
200 	ArgList.Count = 1;
201 	ArgList.Pointer = &Arg;
202 
203 	(void)memset(&Arg, 0, sizeof(Arg));
204 
205 	Arg.Type = ACPI_TYPE_INTEGER;
206 	Arg.Integer.Value = ACPI_STATE_S4;
207 
208 	AcpiEvaluateObject(NULL, "\\_PTS", &ArgList, NULL);
209 	AcpiEvaluateObject(NULL, "\\_GTS", &ArgList, NULL);
210 
211 	/* clear wake status */
212 
213 	AcpiWriteBitRegister(ACPI_BITREG_WAKE_STATUS, 1);
214 
215 	AcpiHwDisableAllGpes();
216 	AcpiHwEnableAllWakeupGpes();
217 
218 	/* flush caches */
219 
220 	ACPI_FLUSH_CPU_CACHE();
221 
222 	/*
223 	 * write the value to command port and wait until we enter sleep state
224 	 */
225 	do {
226 		AcpiOsStall(1000000);
227 		AcpiOsWritePort(AcpiGbl_FADT.SmiCommand,
228 				AcpiGbl_FADT.S4BiosRequest, 8);
229 		status = AcpiReadBitRegister(ACPI_BITREG_WAKE_STATUS, &ret);
230 		if (ACPI_FAILURE(status))
231 			break;
232 	} while (!ret);
233 
234 	AcpiHwDisableAllGpes();
235 	AcpiHwEnableAllRuntimeGpes();
236 
237 	return (AE_OK);
238 }
239 
240 void
241 acpi_md_sleep_enter(int state)
242 {
243 	ACPI_STATUS			status;
244 	struct cpu_info			*ci;
245 
246 	ci = curcpu();
247 
248 #ifdef MULTIPROCESSOR
249 	if (!CPU_IS_PRIMARY(ci)) {
250 		atomic_and_32(&ci->ci_flags, ~CPUF_RUNNING);
251 		atomic_and_32(&cpus_running, ~ci->ci_cpumask);
252 
253 		ACPI_FLUSH_CPU_CACHE();
254 
255 		for (;;)
256 			x86_hlt();
257 	}
258 #endif
259 
260 	acpi_md_sleep_patch(ci);
261 
262 	ACPI_FLUSH_CPU_CACHE();
263 
264 	if (state == ACPI_STATE_S4) {
265 		ACPI_TABLE_FACS *facs;
266 		status = AcpiGetTable(ACPI_SIG_FACS, 0, (ACPI_TABLE_HEADER **)&facs);
267 		if (ACPI_FAILURE(status)) {
268 			printf("acpi: S4BIOS not supported: cannot load FACS\n");
269 			return;
270 		}
271 		if (facs == NULL ||
272 		    (facs->Flags & ACPI_FACS_S4_BIOS_PRESENT) == 0) {
273 			printf("acpi: S4BIOS not supported: not present");
274 			return;
275 		}
276 		status = enter_s4_with_bios();
277 	} else {
278 		status = AcpiEnterSleepState(state);
279 	}
280 
281 	if (ACPI_FAILURE(status)) {
282 		printf("acpi: AcpiEnterSleepState failed: %s\n",
283 		       AcpiFormatException(status));
284 		return;
285 	}
286 
287 	for (;;)
288 		x86_hlt();
289 }
290 
291 #ifdef MULTIPROCESSOR
292 void
293 acpi_cpu_sleep(struct cpu_info *ci)
294 {
295 	KASSERT(!CPU_IS_PRIMARY(ci));
296 	KASSERT(ci == curcpu());
297 
298 	x86_disable_intr();
299 
300 	if (acpi_md_sleep_prepare(-1))
301 		return;
302 
303 	/* Execute Wakeup */
304 #ifdef __i386__
305 	npxinit(ci);
306 #else
307 	cpu_init_msrs(ci, false);
308 	fpuinit(ci);
309 #endif
310 #if NLAPIC > 0
311 	lapic_enable();
312 	lapic_set_lvt();
313 	lapic_initclocks();
314 #endif
315 
316 	atomic_or_32(&ci->ci_flags, CPUF_RUNNING);
317 	atomic_or_32(&cpus_running, ci->ci_cpumask);
318 	tsc_sync_ap(ci);
319 
320 	x86_enable_intr();
321 }
322 #endif
323 
324 int
325 acpi_md_sleep(int state)
326 {
327 	int s, ret = 0;
328 #ifdef MULTIPROCESSOR
329 	struct cpu_info *ci;
330 	CPU_INFO_ITERATOR cii;
331 #endif
332 
333 	KASSERT(acpi_wakeup_paddr != 0);
334 	KASSERT(sizeof(wakecode) <= PAGE_SIZE);
335 
336 	if (!CPU_IS_PRIMARY(curcpu())) {
337 		printf("acpi0: WARNING: ignoring sleep from secondary CPU\n");
338 		return -1;
339 	}
340 
341 	AcpiSetFirmwareWakingVector(acpi_wakeup_paddr);
342 
343 	s = splhigh();
344 #ifdef __i386__
345 	npxsave_cpu(true);
346 #else
347 	fpusave_cpu(true);
348 #endif
349 	x86_disable_intr();
350 
351 #ifdef MULTIPROCESSOR
352 	/* Save and suspend Application Processors */
353 	x86_broadcast_ipi(X86_IPI_ACPI_CPU_SLEEP);
354 	while (cpus_running != curcpu()->ci_cpumask)
355 		delay(1);
356 #endif
357 
358 	if (acpi_md_sleep_prepare(state))
359 		goto out;
360 
361 	/* Execute Wakeup */
362 #ifdef __i386__
363 	npxinit(&cpu_info_primary);
364 #else
365 	cpu_init_msrs(&cpu_info_primary, false);
366 	fpuinit(&cpu_info_primary);
367 #endif
368 	i8259_reinit();
369 #if NLAPIC > 0
370 	lapic_enable();
371 	lapic_set_lvt();
372 	lapic_initclocks();
373 #endif
374 #if NIOAPIC > 0
375 	ioapic_reenable();
376 #endif
377 
378 	initrtclock(TIMER_FREQ);
379 	inittodr(time_second);
380 
381 	AcpiClearEvent(ACPI_EVENT_PMTIMER);
382 	AcpiClearEvent(ACPI_EVENT_GLOBAL);
383 	AcpiClearEvent(ACPI_EVENT_POWER_BUTTON);
384 	AcpiClearEvent(ACPI_EVENT_SLEEP_BUTTON);
385 	AcpiClearEvent(ACPI_EVENT_RTC);
386 	AcpiHwDisableAllGpes ();
387 
388 	acpi_pci_link_resume();
389 
390 out:
391 
392 #ifdef MULTIPROCESSOR
393 	for (CPU_INFO_FOREACH(cii, ci)) {
394 		if (CPU_IS_PRIMARY(ci))
395 			continue;
396 		acpi_md_sleep_patch(ci);
397 
398 		CPU_STARTUP(ci, acpi_wakeup_paddr);
399 		CPU_START_CLEANUP(ci);
400 
401 		while ((ci->ci_flags & CPUF_RUNNING) == 0)
402 			x86_pause();
403 
404 		tsc_sync_bp(ci);
405 	}
406 #endif
407 
408 	x86_enable_intr();
409 	splx(s);
410 
411 #ifdef MTRR
412 	if (mtrr_funcs != NULL)
413 		mtrr_commit();
414 #endif
415 
416 	return (ret);
417 }
418 
419 void
420 acpi_md_sleep_init(void)
421 {
422 	/* Map ACPI wakecode */
423 	acpi_wakeup_vaddr = uvm_km_alloc(kernel_map, PAGE_SIZE, 0,
424 	    UVM_KMF_VAONLY);
425 	if (acpi_wakeup_vaddr == 0)
426 		panic("acpi: can't allocate address for wakecode.\n");
427 
428 	pmap_kenter_pa(acpi_wakeup_vaddr, acpi_wakeup_paddr,
429 	    VM_PROT_READ | VM_PROT_WRITE, 0);
430 	pmap_update(pmap_kernel());
431 }
432 
433 SYSCTL_SETUP(sysctl_md_acpi_setup, "acpi x86 sysctl setup")
434 {
435 	const struct sysctlnode *node;
436 	const struct sysctlnode *ssnode;
437 
438 	if (sysctl_createv(NULL, 0, NULL, &node, CTLFLAG_PERMANENT,
439 	    CTLTYPE_NODE, "machdep", NULL, NULL, 0, NULL, 0, CTL_MACHDEP,
440 	    CTL_EOL) != 0)
441 		return;
442 	if (sysctl_createv(NULL, 0, &node, &ssnode, CTLFLAG_READWRITE,
443 	    CTLTYPE_INT, "acpi_vbios_reset", NULL, sysctl_md_acpi_vbios_reset,
444 	    0, NULL, 0, CTL_CREATE, CTL_EOL) != 0)
445 		return;
446 	if (sysctl_createv(NULL, 0, &node, &ssnode, CTLFLAG_READWRITE,
447 	    CTLTYPE_BOOL, "acpi_beep_on_reset", NULL, sysctl_md_acpi_beep_on_reset,
448 	    0, NULL, 0, CTL_CREATE, CTL_EOL) != 0)
449 		return;
450 
451 	acpi_md_node = node->sysctl_num;
452 }
453 
454 static int
455 sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS)
456 {
457 	int error, t;
458 	struct sysctlnode node;
459 
460 	node = *rnode;
461 	t = acpi_md_vbios_reset;
462 	node.sysctl_data = &t;
463 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
464 	if (error || newp == NULL)
465 		return error;
466 
467 	if (t < 0 || t > 2)
468 		return EINVAL;
469 
470 #ifndef VGA_POST
471 	if (t == 2) {
472 		aprint_error("WARNING: machdep.acpi_vbios_reset=2 "
473 		    "unsupported (no option VGA_POST in kernel config)\n");
474 		return EINVAL;
475 	}
476 #endif
477 
478 	acpi_md_vbios_reset = t;
479 
480 	return 0;
481 }
482 
483 static int
484 sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS)
485 {
486 	int error, t;
487 	struct sysctlnode node;
488 
489 	node = *rnode;
490 	t = acpi_md_beep_on_reset;
491 	node.sysctl_data = &t;
492 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
493 	if (error || newp == NULL)
494 		return error;
495 
496 	if (t < 0 || t > 1)
497 		return EINVAL;
498 
499 	acpi_md_beep_on_reset = t;
500 
501 	return 0;
502 }
503