1 |
/* $MidnightBSD$ */ |
2 |
/*- |
3 |
* Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org> |
4 |
* Copyright (c) 2001-2012 Mitsuru IWASAKI <iwasaki@jp.freebsd.org> |
5 |
* Copyright (c) 2003 Peter Wemm |
6 |
* Copyright (c) 2008-2012 Jung-uk Kim <jkim@FreeBSD.org> |
7 |
* All rights reserved. |
8 |
* |
9 |
* Redistribution and use in source and binary forms, with or without |
10 |
* modification, are permitted provided that the following conditions |
11 |
* are met: |
12 |
* 1. Redistributions of source code must retain the above copyright |
13 |
* notice, this list of conditions and the following disclaimer. |
14 |
* 2. Redistributions in binary form must reproduce the above copyright |
15 |
* notice, this list of conditions and the following disclaimer in the |
16 |
* documentation and/or other materials provided with the distribution. |
17 |
* |
18 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
19 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
20 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
21 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
22 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
24 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
25 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
26 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
27 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
28 |
* SUCH DAMAGE. |
29 |
*/ |
30 |
|
31 |
#include <sys/cdefs.h> |
32 |
__FBSDID("$FreeBSD: stable/11/sys/x86/acpica/acpi_wakeup.c 347700 2019-05-16 14:42:16Z markj $"); |
33 |
|
34 |
#if defined(__amd64__) |
35 |
#define DEV_APIC |
36 |
#else |
37 |
#include "opt_apic.h" |
38 |
#endif |
39 |
|
40 |
#include <sys/param.h> |
41 |
#include <sys/bus.h> |
42 |
#include <sys/eventhandler.h> |
43 |
#include <sys/kernel.h> |
44 |
#include <sys/malloc.h> |
45 |
#include <sys/memrange.h> |
46 |
#include <sys/smp.h> |
47 |
#include <sys/systm.h> |
48 |
#include <sys/cons.h> |
49 |
|
50 |
#include <vm/vm.h> |
51 |
#include <vm/pmap.h> |
52 |
|
53 |
#include <machine/clock.h> |
54 |
#include <machine/cpu.h> |
55 |
#include <machine/intr_machdep.h> |
56 |
#include <machine/md_var.h> |
57 |
#include <x86/mca.h> |
58 |
#include <machine/pcb.h> |
59 |
#include <machine/specialreg.h> |
60 |
#include <x86/ucode.h> |
61 |
|
62 |
#ifdef DEV_APIC |
63 |
#include <x86/apicreg.h> |
64 |
#include <x86/apicvar.h> |
65 |
#endif |
66 |
#ifdef SMP |
67 |
#include <machine/smp.h> |
68 |
#include <machine/vmparam.h> |
69 |
#endif |
70 |
|
71 |
#include <contrib/dev/acpica/include/acpi.h> |
72 |
|
73 |
#include <dev/acpica/acpivar.h> |
74 |
|
75 |
#include "acpi_wakecode.h" |
76 |
#include "acpi_wakedata.h" |
77 |
|
78 |
/* Make sure the code is less than a page and leave room for the stack. */ |
79 |
CTASSERT(sizeof(wakecode) < PAGE_SIZE - 1024); |
80 |
|
81 |
extern int acpi_resume_beep; |
82 |
extern int acpi_reset_video; |
83 |
extern int acpi_susp_bounce; |
84 |
|
85 |
#ifdef SMP |
86 |
extern struct susppcb **susppcbs; |
87 |
static cpuset_t suspcpus; |
88 |
#else |
89 |
static struct susppcb **susppcbs; |
90 |
#endif |
91 |
|
92 |
static void *acpi_alloc_wakeup_handler(void **); |
93 |
static void acpi_stop_beep(void *); |
94 |
|
95 |
#ifdef SMP |
96 |
static int acpi_wakeup_ap(struct acpi_softc *, int); |
97 |
static void acpi_wakeup_cpus(struct acpi_softc *); |
98 |
#endif |
99 |
|
100 |
#ifdef __amd64__ |
101 |
#define ACPI_WAKEPAGES 4 |
102 |
#else |
103 |
#define ACPI_WAKEPAGES 1 |
104 |
#endif |
105 |
|
106 |
#define WAKECODE_FIXUP(offset, type, val) do { \ |
107 |
type *addr; \ |
108 |
addr = (type *)(sc->acpi_wakeaddr + (offset)); \ |
109 |
*addr = val; \ |
110 |
} while (0) |
111 |
|
112 |
static void |
113 |
acpi_stop_beep(void *arg) |
114 |
{ |
115 |
|
116 |
if (acpi_resume_beep != 0) |
117 |
timer_spkr_release(); |
118 |
} |
119 |
|
120 |
#ifdef SMP |
121 |
static int |
122 |
acpi_wakeup_ap(struct acpi_softc *sc, int cpu) |
123 |
{ |
124 |
struct pcb *pcb; |
125 |
int vector = (sc->acpi_wakephys >> 12) & 0xff; |
126 |
int apic_id = cpu_apic_ids[cpu]; |
127 |
int ms; |
128 |
|
129 |
pcb = &susppcbs[cpu]->sp_pcb; |
130 |
WAKECODE_FIXUP(wakeup_pcb, struct pcb *, pcb); |
131 |
WAKECODE_FIXUP(wakeup_gdt, uint16_t, pcb->pcb_gdt.rd_limit); |
132 |
WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t, pcb->pcb_gdt.rd_base); |
133 |
|
134 |
ipi_startup(apic_id, vector); |
135 |
|
136 |
/* Wait up to 5 seconds for it to resume. */ |
137 |
for (ms = 0; ms < 5000; ms++) { |
138 |
if (!CPU_ISSET(cpu, &suspended_cpus)) |
139 |
return (1); /* return SUCCESS */ |
140 |
DELAY(1000); |
141 |
} |
142 |
return (0); /* return FAILURE */ |
143 |
} |
144 |
|
145 |
#define WARMBOOT_TARGET 0 |
146 |
#define WARMBOOT_OFF (KERNBASE + 0x0467) |
147 |
#define WARMBOOT_SEG (KERNBASE + 0x0469) |
148 |
|
149 |
#define CMOS_REG (0x70) |
150 |
#define CMOS_DATA (0x71) |
151 |
#define BIOS_RESET (0x0f) |
152 |
#define BIOS_WARM (0x0a) |
153 |
|
154 |
static void |
155 |
acpi_wakeup_cpus(struct acpi_softc *sc) |
156 |
{ |
157 |
uint32_t mpbioswarmvec; |
158 |
int cpu; |
159 |
u_char mpbiosreason; |
160 |
|
161 |
/* save the current value of the warm-start vector */ |
162 |
mpbioswarmvec = *((uint32_t *)WARMBOOT_OFF); |
163 |
outb(CMOS_REG, BIOS_RESET); |
164 |
mpbiosreason = inb(CMOS_DATA); |
165 |
|
166 |
/* setup a vector to our boot code */ |
167 |
*((volatile u_short *)WARMBOOT_OFF) = WARMBOOT_TARGET; |
168 |
*((volatile u_short *)WARMBOOT_SEG) = sc->acpi_wakephys >> 4; |
169 |
outb(CMOS_REG, BIOS_RESET); |
170 |
outb(CMOS_DATA, BIOS_WARM); /* 'warm-start' */ |
171 |
|
172 |
/* Wake up each AP. */ |
173 |
for (cpu = 1; cpu < mp_ncpus; cpu++) { |
174 |
if (!CPU_ISSET(cpu, &suspcpus)) |
175 |
continue; |
176 |
if (acpi_wakeup_ap(sc, cpu) == 0) { |
177 |
/* restore the warmstart vector */ |
178 |
*(uint32_t *)WARMBOOT_OFF = mpbioswarmvec; |
179 |
panic("acpi_wakeup: failed to resume AP #%d (PHY #%d)", |
180 |
cpu, cpu_apic_ids[cpu]); |
181 |
} |
182 |
} |
183 |
|
184 |
/* restore the warmstart vector */ |
185 |
*(uint32_t *)WARMBOOT_OFF = mpbioswarmvec; |
186 |
|
187 |
outb(CMOS_REG, BIOS_RESET); |
188 |
outb(CMOS_DATA, mpbiosreason); |
189 |
} |
190 |
#endif |
191 |
|
192 |
int |
193 |
acpi_sleep_machdep(struct acpi_softc *sc, int state) |
194 |
{ |
195 |
ACPI_STATUS status; |
196 |
struct pcb *pcb; |
197 |
#ifdef __amd64__ |
198 |
struct pcpu *pc; |
199 |
int i; |
200 |
#endif |
201 |
|
202 |
if (sc->acpi_wakeaddr == 0ul) |
203 |
return (-1); /* couldn't alloc wake memory */ |
204 |
|
205 |
#ifdef SMP |
206 |
suspcpus = all_cpus; |
207 |
CPU_CLR(PCPU_GET(cpuid), &suspcpus); |
208 |
#endif |
209 |
|
210 |
if (acpi_resume_beep != 0) |
211 |
timer_spkr_acquire(); |
212 |
|
213 |
AcpiSetFirmwareWakingVector(sc->acpi_wakephys, 0); |
214 |
|
215 |
intr_suspend(); |
216 |
|
217 |
pcb = &susppcbs[0]->sp_pcb; |
218 |
if (savectx(pcb)) { |
219 |
#ifdef __amd64__ |
220 |
fpususpend(susppcbs[0]->sp_fpususpend); |
221 |
#else |
222 |
npxsuspend(susppcbs[0]->sp_fpususpend); |
223 |
#endif |
224 |
#ifdef SMP |
225 |
if (!CPU_EMPTY(&suspcpus) && suspend_cpus(suspcpus) == 0) { |
226 |
device_printf(sc->acpi_dev, "Failed to suspend APs\n"); |
227 |
return (0); /* couldn't sleep */ |
228 |
} |
229 |
#endif |
230 |
#ifdef __amd64__ |
231 |
hw_ibrs_active = 0; |
232 |
hw_ssb_active = 0; |
233 |
cpu_stdext_feature3 = 0; |
234 |
CPU_FOREACH(i) { |
235 |
pc = pcpu_find(i); |
236 |
pc->pc_ibpb_set = 0; |
237 |
} |
238 |
#endif |
239 |
|
240 |
WAKECODE_FIXUP(resume_beep, uint8_t, (acpi_resume_beep != 0)); |
241 |
WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0)); |
242 |
|
243 |
#ifdef __amd64__ |
244 |
WAKECODE_FIXUP(wakeup_efer, uint64_t, rdmsr(MSR_EFER) & |
245 |
~(EFER_LMA)); |
246 |
#else |
247 |
WAKECODE_FIXUP(wakeup_cr4, register_t, pcb->pcb_cr4); |
248 |
#endif |
249 |
WAKECODE_FIXUP(wakeup_pcb, struct pcb *, pcb); |
250 |
WAKECODE_FIXUP(wakeup_gdt, uint16_t, pcb->pcb_gdt.rd_limit); |
251 |
WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t, pcb->pcb_gdt.rd_base); |
252 |
|
253 |
/* Call ACPICA to enter the desired sleep state */ |
254 |
if (state == ACPI_STATE_S4 && sc->acpi_s4bios) |
255 |
status = AcpiEnterSleepStateS4bios(); |
256 |
else |
257 |
status = AcpiEnterSleepState(state); |
258 |
if (ACPI_FAILURE(status)) { |
259 |
device_printf(sc->acpi_dev, |
260 |
"AcpiEnterSleepState failed - %s\n", |
261 |
AcpiFormatException(status)); |
262 |
return (0); /* couldn't sleep */ |
263 |
} |
264 |
|
265 |
if (acpi_susp_bounce) |
266 |
resumectx(pcb); |
267 |
|
268 |
for (;;) |
269 |
ia32_pause(); |
270 |
} else { |
271 |
/* |
272 |
* Re-initialize console hardware as soon as possibe. |
273 |
* No console output (e.g. printf) is allowed before |
274 |
* this point. |
275 |
*/ |
276 |
cnresume(); |
277 |
#ifdef __amd64__ |
278 |
fpuresume(susppcbs[0]->sp_fpususpend); |
279 |
#else |
280 |
npxresume(susppcbs[0]->sp_fpususpend); |
281 |
#endif |
282 |
} |
283 |
|
284 |
return (1); /* wakeup successfully */ |
285 |
} |
286 |
|
287 |
int |
288 |
acpi_wakeup_machdep(struct acpi_softc *sc, int state, int sleep_result, |
289 |
int intr_enabled) |
290 |
{ |
291 |
|
292 |
if (sleep_result == -1) |
293 |
return (sleep_result); |
294 |
|
295 |
if (!intr_enabled) { |
296 |
/* Wakeup MD procedures in interrupt disabled context */ |
297 |
if (sleep_result == 1) { |
298 |
ucode_reload(); |
299 |
pmap_init_pat(); |
300 |
initializecpu(); |
301 |
PCPU_SET(switchtime, 0); |
302 |
PCPU_SET(switchticks, ticks); |
303 |
#ifdef DEV_APIC |
304 |
lapic_xapic_mode(); |
305 |
#endif |
306 |
#ifdef SMP |
307 |
if (!CPU_EMPTY(&suspcpus)) |
308 |
acpi_wakeup_cpus(sc); |
309 |
#endif |
310 |
} |
311 |
|
312 |
#ifdef SMP |
313 |
if (!CPU_EMPTY(&suspcpus)) |
314 |
resume_cpus(suspcpus); |
315 |
#endif |
316 |
mca_resume(); |
317 |
#ifdef __amd64__ |
318 |
if (vmm_resume_p != NULL) |
319 |
vmm_resume_p(); |
320 |
#endif |
321 |
intr_resume(/*suspend_cancelled*/false); |
322 |
|
323 |
AcpiSetFirmwareWakingVector(0, 0); |
324 |
} else { |
325 |
/* Wakeup MD procedures in interrupt enabled context */ |
326 |
if (sleep_result == 1 && mem_range_softc.mr_op != NULL && |
327 |
mem_range_softc.mr_op->reinit != NULL) |
328 |
mem_range_softc.mr_op->reinit(&mem_range_softc); |
329 |
} |
330 |
|
331 |
return (sleep_result); |
332 |
} |
333 |
|
334 |
static void * |
335 |
acpi_alloc_wakeup_handler(void *wakepages[ACPI_WAKEPAGES]) |
336 |
{ |
337 |
int i; |
338 |
|
339 |
memset(wakepages, 0, ACPI_WAKEPAGES * sizeof(*wakepages)); |
340 |
|
341 |
/* |
342 |
* Specify the region for our wakeup code. We want it in the low 1 MB |
343 |
* region, excluding real mode IVT (0-0x3ff), BDA (0x400-0x4ff), EBDA |
344 |
* (less than 128KB, below 0xa0000, must be excluded by SMAP and DSDT), |
345 |
* and ROM area (0xa0000 and above). The temporary page tables must be |
346 |
* page-aligned. |
347 |
*/ |
348 |
for (i = 0; i < ACPI_WAKEPAGES; i++) { |
349 |
wakepages[i] = contigmalloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT, |
350 |
0x500, 0xa0000, PAGE_SIZE, 0ul); |
351 |
if (wakepages[i] == NULL) { |
352 |
printf("%s: can't alloc wake memory\n", __func__); |
353 |
goto freepages; |
354 |
} |
355 |
} |
356 |
if (EVENTHANDLER_REGISTER(power_resume, acpi_stop_beep, NULL, |
357 |
EVENTHANDLER_PRI_LAST) == NULL) { |
358 |
printf("%s: can't register event handler\n", __func__); |
359 |
goto freepages; |
360 |
} |
361 |
susppcbs = malloc(mp_ncpus * sizeof(*susppcbs), M_DEVBUF, M_WAITOK); |
362 |
for (i = 0; i < mp_ncpus; i++) { |
363 |
susppcbs[i] = malloc(sizeof(**susppcbs), M_DEVBUF, M_WAITOK); |
364 |
susppcbs[i]->sp_fpususpend = alloc_fpusave(M_WAITOK); |
365 |
} |
366 |
|
367 |
return (wakepages); |
368 |
|
369 |
freepages: |
370 |
for (i = 0; i < ACPI_WAKEPAGES; i++) |
371 |
if (wakepages[i] != NULL) |
372 |
contigfree(wakepages[i], PAGE_SIZE, M_DEVBUF); |
373 |
return (NULL); |
374 |
} |
375 |
|
376 |
void |
377 |
acpi_install_wakeup_handler(struct acpi_softc *sc) |
378 |
{ |
379 |
static void *wakeaddr; |
380 |
void *wakepages[ACPI_WAKEPAGES]; |
381 |
#ifdef __amd64__ |
382 |
uint64_t *pt4, *pt3, *pt2; |
383 |
vm_paddr_t pt4pa, pt3pa, pt2pa; |
384 |
int i; |
385 |
#endif |
386 |
|
387 |
if (wakeaddr != NULL) |
388 |
return; |
389 |
|
390 |
if (acpi_alloc_wakeup_handler(wakepages) == NULL) |
391 |
return; |
392 |
|
393 |
wakeaddr = wakepages[0]; |
394 |
sc->acpi_wakeaddr = (vm_offset_t)wakeaddr; |
395 |
sc->acpi_wakephys = vtophys(wakeaddr); |
396 |
|
397 |
#ifdef __amd64__ |
398 |
pt4 = wakepages[1]; |
399 |
pt3 = wakepages[2]; |
400 |
pt2 = wakepages[3]; |
401 |
pt4pa = vtophys(pt4); |
402 |
pt3pa = vtophys(pt3); |
403 |
pt2pa = vtophys(pt2); |
404 |
#endif |
405 |
|
406 |
bcopy(wakecode, (void *)sc->acpi_wakeaddr, sizeof(wakecode)); |
407 |
|
408 |
/* Patch GDT base address, ljmp targets. */ |
409 |
WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t, |
410 |
sc->acpi_wakephys + bootgdt); |
411 |
WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t, |
412 |
sc->acpi_wakephys + wakeup_32); |
413 |
#ifdef __amd64__ |
414 |
WAKECODE_FIXUP((wakeup_sw64 + 1), uint32_t, |
415 |
sc->acpi_wakephys + wakeup_64); |
416 |
WAKECODE_FIXUP(wakeup_pagetables, uint32_t, pt4pa); |
417 |
#endif |
418 |
|
419 |
/* Save pointers to some global data. */ |
420 |
WAKECODE_FIXUP(wakeup_ret, void *, resumectx); |
421 |
#ifndef __amd64__ |
422 |
#if defined(PAE) || defined(PAE_TABLES) |
423 |
WAKECODE_FIXUP(wakeup_cr3, register_t, vtophys(kernel_pmap->pm_pdpt)); |
424 |
#else |
425 |
WAKECODE_FIXUP(wakeup_cr3, register_t, vtophys(kernel_pmap->pm_pdir)); |
426 |
#endif |
427 |
|
428 |
#else /* __amd64__ */ |
429 |
/* Create the initial 1GB replicated page tables */ |
430 |
for (i = 0; i < 512; i++) { |
431 |
/* |
432 |
* Each slot of the level 4 pages points |
433 |
* to the same level 3 page |
434 |
*/ |
435 |
pt4[i] = (uint64_t)pt3pa; |
436 |
pt4[i] |= PG_V | PG_RW | PG_U; |
437 |
|
438 |
/* |
439 |
* Each slot of the level 3 pages points |
440 |
* to the same level 2 page |
441 |
*/ |
442 |
pt3[i] = (uint64_t)pt2pa; |
443 |
pt3[i] |= PG_V | PG_RW | PG_U; |
444 |
|
445 |
/* The level 2 page slots are mapped with 2MB pages for 1GB. */ |
446 |
pt2[i] = i * (2 * 1024 * 1024); |
447 |
pt2[i] |= PG_V | PG_RW | PG_PS | PG_U; |
448 |
} |
449 |
#endif /* !__amd64__ */ |
450 |
|
451 |
if (bootverbose) |
452 |
device_printf(sc->acpi_dev, "wakeup code va %#jx pa %#jx\n", |
453 |
(uintmax_t)sc->acpi_wakeaddr, (uintmax_t)sc->acpi_wakephys); |
454 |
} |