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 */ |
108 | static paddr_t acpi_wakeup_paddr = 3 * PAGE_SIZE; |
109 | static vaddr_t acpi_wakeup_vaddr; |
110 | |
111 | int acpi_md_vbios_reset = 0; /* Referenced by dev/pci/vga_pci.c */ |
112 | int acpi_md_vesa_modenum = 0; /* Referenced by arch/x86/x86/genfb_machdep.c */ |
113 | static int acpi_md_beep_on_reset = 0; |
114 | |
115 | static int acpi_md_s4bios(void); |
116 | static int sysctl_md_acpi_vbios_reset(SYSCTLFN_ARGS); |
117 | static int sysctl_md_acpi_beep_on_reset(SYSCTLFN_ARGS); |
118 | |
119 | /* Implemented in acpi_wakeup_low.S. */ |
120 | int acpi_md_sleep_prepare(int); |
121 | int acpi_md_sleep_exit(int); |
122 | |
123 | /* Referenced by acpi_wakeup_low.S. */ |
124 | void acpi_md_sleep_enter(int); |
125 | |
126 | #ifdef MULTIPROCESSOR |
127 | /* Referenced in ipifuncs.c. */ |
128 | void acpi_cpu_sleep(struct cpu_info *); |
129 | #endif |
130 | |
131 | static void |
132 | acpi_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 | |
170 | static int |
171 | acpi_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 | |
187 | void |
188 | acpi_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 |
242 | void |
243 | acpi_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 | |
288 | out: |
289 | x86_enable_intr(); |
290 | splx(s); |
291 | } |
292 | #endif |
293 | |
294 | int |
295 | acpi_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 | |
387 | out: |
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 | |
417 | void |
418 | acpi_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 | |
431 | SYSCTL_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 | |
462 | static int |
463 | sysctl_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 | |
491 | static int |
492 | sysctl_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 | |