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