1/* $NetBSD: acpi_wakeup.c,v 1.50 2019/06/17 16:34:02 jmcneill 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.50 2019/06/17 16:34:02 jmcneill 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 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
170static int
171acpi_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
187void
188acpi_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
242void
243acpi_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
288out:
289 x86_enable_intr();
290 splx(s);
291}
292#endif
293
294int
295acpi_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, 0);
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
387out:
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
417void
418acpi_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
431SYSCTL_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
462static int
463sysctl_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
491static int
492sysctl_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