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 */ |
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 | #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 | |
178 | static int |
179 | acpi_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 | |
195 | void |
196 | acpi_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 |
250 | void |
251 | acpi_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 | |
279 | out: |
280 | x86_enable_intr(); |
281 | splx(s); |
282 | } |
283 | #endif |
284 | |
285 | int |
286 | acpi_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 | |
360 | out: |
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 | |
389 | void |
390 | acpi_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 | |
403 | SYSCTL_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 | |
434 | static int |
435 | sysctl_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 | |
463 | static int |
464 | sysctl_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 | |