1/* $NetBSD: acpi_wakeup.c,v 1.45 2016/10/20 16:05:04 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.45 2016/10/20 16:05:04 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 */
108static paddr_t acpi_wakeup_paddr = 3 * PAGE_SIZE;
109static vaddr_t acpi_wakeup_vaddr;
110
111int acpi_md_vbios_reset = 0; /* Referenced by dev/pci/vga_pci.c */
112int acpi_md_vesa_modenum = 0; /* Referenced by arch/x86/x86/genfb_machdep.c */
113static int acpi_md_beep_on_reset = 0;
114
115static int acpi_md_s4bios(void);
116static int sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS);
117static int sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS);
118
119/* Implemented in acpi_wakeup_low.S. */
120int acpi_md_sleep_prepare(int);
121int acpi_md_sleep_exit(int);
122
123/* Referenced by acpi_wakeup_low.S. */
124void acpi_md_sleep_enter(int);
125
126#ifdef MULTIPROCESSOR
127/* Referenced in ipifuncs.c. */
128void acpi_cpu_sleep(struct cpu_info *);
129#endif
130
131static void
132acpi_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#define WAKECODE_BCOPY(offset, type, val) do { \
141 void **addr; \
142 addr = (void **)(acpi_wakeup_vaddr + offset); \
143 memcpy(addr, &(val), sizeof(type)); \
144} while (0)
145
146 paddr_t tmp_pdir;
147
148 tmp_pdir = pmap_init_tmp_pgtbl(acpi_wakeup_paddr);
149
150 /* Execute Sleep */
151 memcpy((void *)acpi_wakeup_vaddr, wakecode, sizeof(wakecode));
152
153 if (CPU_IS_PRIMARY(ci)) {
154 WAKECODE_FIXUP(WAKEUP_vesa_modenum, uint16_t, acpi_md_vesa_modenum);
155 WAKECODE_FIXUP(WAKEUP_vbios_reset, uint8_t, acpi_md_vbios_reset);
156 WAKECODE_FIXUP(WAKEUP_beep_on_reset, uint8_t, acpi_md_beep_on_reset);
157 } else {
158 WAKECODE_FIXUP(WAKEUP_vesa_modenum, uint16_t, 0);
159 WAKECODE_FIXUP(WAKEUP_vbios_reset, uint8_t, 0);
160 WAKECODE_FIXUP(WAKEUP_beep_on_reset, uint8_t, 0);
161 }
162
163#ifdef __i386__
164 WAKECODE_FIXUP(WAKEUP_r_cr4, uint32_t, ci->ci_suspend_cr4);
165#endif
166 WAKECODE_FIXUP(WAKEUP_efer, uint32_t, ci->ci_suspend_efer);
167 WAKECODE_FIXUP(WAKEUP_curcpu, void *, ci);
168#ifdef __i386__
169 WAKECODE_FIXUP(WAKEUP_r_cr3, uint32_t, tmp_pdir);
170#else
171 WAKECODE_FIXUP(WAKEUP_r_cr3, uint64_t, tmp_pdir);
172#endif
173 WAKECODE_FIXUP(WAKEUP_restorecpu, void *, acpi_md_sleep_exit);
174#undef WAKECODE_FIXUP
175#undef WAKECODE_BCOPY
176}
177
178static int
179acpi_md_s4bios(void)
180{
181 ACPI_TABLE_FACS *facs;
182 ACPI_STATUS rv;
183
184 rv = AcpiGetTable(ACPI_SIG_FACS, 0, (ACPI_TABLE_HEADER **)&facs);
185
186 if (ACPI_FAILURE(rv) || facs == NULL)
187 return 0;
188
189 if ((facs->Flags & ACPI_FACS_S4_BIOS_PRESENT) == 0)
190 return 0;
191
192 return 1;
193}
194
195void
196acpi_md_sleep_enter(int state)
197{
198 static int s4bios = -1;
199 struct cpu_info *ci;
200 ACPI_STATUS rv;
201
202 ci = curcpu();
203
204#ifdef MULTIPROCESSOR
205 if (!CPU_IS_PRIMARY(ci)) {
206 atomic_and_32(&ci->ci_flags, ~CPUF_RUNNING);
207 kcpuset_atomic_clear(kcpuset_running, cpu_index(ci));
208
209 ACPI_FLUSH_CPU_CACHE();
210
211 for (;;)
212 x86_hlt();
213 }
214#endif
215
216 acpi_md_sleep_patch(ci);
217
218 ACPI_FLUSH_CPU_CACHE();
219
220 switch (state) {
221
222 case ACPI_STATE_S4:
223
224 if (s4bios < 0)
225 s4bios = acpi_md_s4bios();
226
227 if (s4bios == 0) {
228 aprint_error("acpi0: S4 not supported\n");
229 return;
230 }
231
232 rv = AcpiEnterSleepStateS4bios();
233 break;
234
235 default:
236 rv = AcpiEnterSleepState(state);
237 break;
238 }
239
240 if (ACPI_FAILURE(rv)) {
241 aprint_error("acpi0: failed to enter S%d\n", state);
242 return;
243 }
244
245 for (;;)
246 x86_hlt();
247}
248
249#ifdef MULTIPROCESSOR
250void
251acpi_cpu_sleep(struct cpu_info *ci)
252{
253 int s;
254
255 KASSERT(!CPU_IS_PRIMARY(ci));
256 KASSERT(ci == curcpu());
257
258 s = splhigh();
259 fpusave_cpu(true);
260 x86_disable_intr();
261
262 if (acpi_md_sleep_prepare(-1))
263 goto out;
264
265 /* Execute Wakeup */
266 cpu_init_msrs(ci, false);
267 fpuinit(ci);
268
269#if NLAPIC > 0
270 lapic_enable();
271 lapic_set_lvt();
272 lapic_initclocks();
273#endif
274
275 atomic_or_32(&ci->ci_flags, CPUF_RUNNING);
276 kcpuset_atomic_set(kcpuset_running, cpu_index(ci));
277 tsc_sync_ap(ci);
278
279out:
280 x86_enable_intr();
281 splx(s);
282}
283#endif
284
285int
286acpi_md_sleep(int state)
287{
288 int s, ret = 0;
289#ifdef MULTIPROCESSOR
290 struct cpu_info *ci;
291 CPU_INFO_ITERATOR cii;
292 cpuid_t cid;
293#endif
294
295 KASSERT(acpi_wakeup_paddr != 0);
296 KASSERT(sizeof(wakecode) <= PAGE_SIZE);
297
298 if (!CPU_IS_PRIMARY(curcpu())) {
299 printf("acpi0: WARNING: ignoring sleep from secondary CPU\n");
300 return -1;
301 }
302
303 AcpiSetFirmwareWakingVector(acpi_wakeup_paddr, acpi_wakeup_paddr);
304
305 s = splhigh();
306 fpusave_cpu(true);
307 x86_disable_intr();
308
309#ifdef MULTIPROCESSOR
310 /* Save and suspend Application Processors. */
311 x86_broadcast_ipi(X86_IPI_ACPI_CPU_SLEEP);
312 cid = cpu_index(curcpu());
313 while (kcpuset_isotherset(kcpuset_running, cid)) {
314 delay(1);
315 }
316#endif
317
318 if (acpi_md_sleep_prepare(state))
319 goto out;
320
321 /* Execute Wakeup */
322 cpu_init_msrs(&cpu_info_primary, false);
323 fpuinit(&cpu_info_primary);
324 i8259_reinit();
325#if NLAPIC > 0
326 lapic_enable();
327 lapic_set_lvt();
328 lapic_initclocks();
329#endif
330#if NIOAPIC > 0
331 ioapic_reenable();
332#endif
333
334 initrtclock(TIMER_FREQ);
335 inittodr(time_second);
336
337 /*
338 * The BIOS should always re-enable the SCI upon
339 * resume from the S3 state. The following is a
340 * workaround for systems that fail to do this.
341 */
342 (void)AcpiWriteBitRegister(ACPI_BITREG_SCI_ENABLE, 1);
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
360out:
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
389void
390acpi_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
403SYSCTL_SETUP(sysctl_md_acpi_setup, "ACPI x86 sysctl setup")
404{
405 const struct sysctlnode *rnode;
406 int err;
407
408 err = sysctl_createv(clog, 0, NULL, &rnode,
409 CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi", NULL,
410 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
411
412 if (err != 0)
413 return;
414
415 err = sysctl_createv(clog, 0, &rnode, &rnode,
416 CTLFLAG_PERMANENT, CTLTYPE_NODE,
417 "sleep", SYSCTL_DESCR("ACPI sleep"),
418 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
419
420 if (err != 0)
421 return;
422
423 (void)sysctl_createv(NULL, 0, &rnode, NULL,
424 CTLFLAG_READWRITE, CTLTYPE_BOOL, "beep",
425 NULL, sysctl_md_acpi_beep_on_reset,
426 0, NULL, 0, CTL_CREATE, CTL_EOL);
427
428 (void)sysctl_createv(NULL, 0, &rnode, NULL,
429 CTLFLAG_READWRITE, CTLTYPE_INT, "vbios",
430 NULL, sysctl_md_acpi_vbios_reset,
431 0, NULL, 0, CTL_CREATE, CTL_EOL);
432}
433
434static int
435sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS)
436{
437 int error, t;
438 struct sysctlnode node;
439
440 node = *rnode;
441 t = acpi_md_vbios_reset;
442 node.sysctl_data = &t;
443 error = sysctl_lookup(SYSCTLFN_CALL(&node));
444 if (error || newp == NULL)
445 return error;
446
447 if (t < 0 || t > 2)
448 return EINVAL;
449
450#ifndef VGA_POST
451 if (t == 2) {
452 aprint_error("WARNING: hw.acpi.sleep.vbios=2 "
453 "unsupported (no option VGA_POST in kernel config)\n");
454 return EINVAL;
455 }
456#endif
457
458 acpi_md_vbios_reset = t;
459
460 return 0;
461}
462
463static int
464sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS)
465{
466 int error, t;
467 struct sysctlnode node;
468
469 node = *rnode;
470 t = acpi_md_beep_on_reset;
471 node.sysctl_data = &t;
472 error = sysctl_lookup(SYSCTLFN_CALL(&node));
473 if (error || newp == NULL)
474 return error;
475
476 if (t < 0 || t > 1)
477 return EINVAL;
478
479 acpi_md_beep_on_reset = t;
480
481 return 0;
482}
483