1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
5 * Copyright (c) 2001-2012 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
6 * Copyright (c) 2003 Peter Wemm
7 * Copyright (c) 2008-2012 Jung-uk Kim <jkim@FreeBSD.org>
8 * All rights reserved.
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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #include "opt_apic.h"
34
35 #include <sys/param.h>
36 #include <sys/bus.h>
37 #include <sys/eventhandler.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/memrange.h>
41 #include <sys/smp.h>
42 #include <sys/systm.h>
43 #include <sys/cons.h>
44
45 #include <vm/vm.h>
46 #include <vm/pmap.h>
47
48 #include <machine/clock.h>
49 #include <machine/cpu.h>
50 #include <machine/intr_machdep.h>
51 #include <machine/md_var.h>
52 #include <x86/mca.h>
53 #include <machine/pcb.h>
54 #include <machine/specialreg.h>
55 #include <x86/ucode.h>
56
57 #ifdef DEV_APIC
58 #include <x86/apicreg.h>
59 #include <x86/apicvar.h>
60 #endif
61 #ifdef SMP
62 #include <machine/smp.h>
63 #include <machine/vmparam.h>
64 #endif
65
66 #include <contrib/dev/acpica/include/acpi.h>
67
68 #include <dev/acpica/acpivar.h>
69
70 #include "acpi_wakecode.h"
71 #include "acpi_wakedata.h"
72
73 /* Make sure the code is less than a page and leave room for the stack. */
74 CTASSERT(sizeof(wakecode) < PAGE_SIZE - 1024);
75
76 extern int acpi_resume_beep;
77 extern int acpi_reset_video;
78 extern int acpi_susp_bounce;
79
80 #ifdef SMP
81 extern struct susppcb **susppcbs;
82 static cpuset_t suspcpus;
83 #else
84 static struct susppcb **susppcbs;
85 #endif
86
87 static void acpi_stop_beep(void *);
88
89 #ifdef SMP
90 static int acpi_wakeup_ap(struct acpi_softc *, int);
91 static void acpi_wakeup_cpus(struct acpi_softc *);
92 #endif
93
94 #define ACPI_WAKEPAGES 1
95
96 #define WAKECODE_FIXUP(offset, type, val) do { \
97 type *addr; \
98 addr = (type *)(sc->acpi_wakeaddr + (offset)); \
99 *addr = val; \
100 } while (0)
101
102 static void
acpi_stop_beep(void * arg)103 acpi_stop_beep(void *arg)
104 {
105
106 if (acpi_resume_beep != 0)
107 timer_spkr_release();
108 }
109
110 #ifdef SMP
111 static int
acpi_wakeup_ap(struct acpi_softc * sc,int cpu)112 acpi_wakeup_ap(struct acpi_softc *sc, int cpu)
113 {
114 struct pcb *pcb;
115 int vector = (sc->acpi_wakephys >> 12) & 0xff;
116 int apic_id = cpu_apic_ids[cpu];
117 int ms;
118
119 pcb = &susppcbs[cpu]->sp_pcb;
120 WAKECODE_FIXUP(wakeup_pcb, struct pcb *, pcb);
121 WAKECODE_FIXUP(wakeup_gdt, uint16_t, pcb->pcb_gdt.rd_limit);
122 WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t, pcb->pcb_gdt.rd_base);
123
124 ipi_startup(apic_id, vector);
125
126 /* Wait up to 5 seconds for it to resume. */
127 for (ms = 0; ms < 5000; ms++) {
128 if (!CPU_ISSET(cpu, &suspended_cpus))
129 return (1); /* return SUCCESS */
130 DELAY(1000);
131 }
132 return (0); /* return FAILURE */
133 }
134
135 #define WARMBOOT_TARGET 0
136 #define WARMBOOT_OFF (PMAP_MAP_LOW + 0x0467)
137 #define WARMBOOT_SEG (PMAP_MAP_LOW + 0x0469)
138
139 #define CMOS_REG (0x70)
140 #define CMOS_DATA (0x71)
141 #define BIOS_RESET (0x0f)
142 #define BIOS_WARM (0x0a)
143
144 static void
acpi_wakeup_cpus(struct acpi_softc * sc)145 acpi_wakeup_cpus(struct acpi_softc *sc)
146 {
147 uint32_t mpbioswarmvec;
148 int cpu;
149 u_char mpbiosreason;
150
151 /* save the current value of the warm-start vector */
152 mpbioswarmvec = *((uint32_t *)WARMBOOT_OFF);
153 outb(CMOS_REG, BIOS_RESET);
154 mpbiosreason = inb(CMOS_DATA);
155
156 /* setup a vector to our boot code */
157 *((volatile u_short *)WARMBOOT_OFF) = WARMBOOT_TARGET;
158 *((volatile u_short *)WARMBOOT_SEG) = sc->acpi_wakephys >> 4;
159 outb(CMOS_REG, BIOS_RESET);
160 outb(CMOS_DATA, BIOS_WARM); /* 'warm-start' */
161
162 /* Wake up each AP. */
163 for (cpu = 1; cpu < mp_ncpus; cpu++) {
164 if (!CPU_ISSET(cpu, &suspcpus))
165 continue;
166 if (acpi_wakeup_ap(sc, cpu) == 0) {
167 /* restore the warmstart vector */
168 *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
169 panic("acpi_wakeup: failed to resume AP #%d (PHY #%d)",
170 cpu, cpu_apic_ids[cpu]);
171 }
172 }
173
174 /*
175 * Remove the identity mapping of low memory for all CPUs and sync
176 * the TLB for the BSP. The APs are now spinning in
177 * cpususpend_handler() and we will release them soon. Then each
178 * will invalidate its TLB.
179 */
180 pmap_remap_lowptdi(false);
181
182 /* restore the warmstart vector */
183 *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
184
185 outb(CMOS_REG, BIOS_RESET);
186 outb(CMOS_DATA, mpbiosreason);
187 }
188 #endif
189
190 int
acpi_sleep_machdep(struct acpi_softc * sc,int state)191 acpi_sleep_machdep(struct acpi_softc *sc, int state)
192 {
193 ACPI_STATUS status;
194 struct pcb *pcb;
195
196 if (sc->acpi_wakeaddr == 0ul)
197 return (-1); /* couldn't alloc wake memory */
198
199 #ifdef SMP
200 suspcpus = all_cpus;
201 CPU_CLR(PCPU_GET(cpuid), &suspcpus);
202 #endif
203
204 if (acpi_resume_beep != 0)
205 timer_spkr_acquire();
206
207 AcpiSetFirmwareWakingVector(sc->acpi_wakephys, 0);
208
209 intr_suspend();
210
211 pcb = &susppcbs[0]->sp_pcb;
212 if (savectx(pcb)) {
213 npxsuspend(susppcbs[0]->sp_fpususpend);
214 #ifdef SMP
215 if (!CPU_EMPTY(&suspcpus) && suspend_cpus(suspcpus) == 0) {
216 device_printf(sc->acpi_dev, "Failed to suspend APs\n");
217 return (0); /* couldn't sleep */
218 }
219 #endif
220
221 WAKECODE_FIXUP(resume_beep, uint8_t, (acpi_resume_beep != 0));
222 WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0));
223
224 if ((amd_feature & AMDID_NX) != 0)
225 WAKECODE_FIXUP(wakeup_efer, uint64_t, rdmsr(MSR_EFER));
226 WAKECODE_FIXUP(wakeup_cr4, register_t, pcb->pcb_cr4);
227 WAKECODE_FIXUP(wakeup_pcb, struct pcb *, pcb);
228 WAKECODE_FIXUP(wakeup_gdt, uint16_t, pcb->pcb_gdt.rd_limit);
229 WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t, pcb->pcb_gdt.rd_base);
230
231 /*
232 * Map some low memory with virt == phys for ACPI wakecode
233 * to use to jump to high memory after enabling paging. This
234 * is the same as for similar jump in locore, except the
235 * jump is a single instruction, and we know its address
236 * more precisely so only need a single PTD, and we have to
237 * be careful to use the kernel map (PTD[0] is for curthread
238 * which may be a user thread in deprecated APIs).
239 */
240 pmap_remap_lowptdi(true);
241
242 /* Call ACPICA to enter the desired sleep state */
243 if (state == ACPI_STATE_S4 && sc->acpi_s4bios)
244 status = AcpiEnterSleepStateS4bios();
245 else
246 status = AcpiEnterSleepState(state);
247 if (ACPI_FAILURE(status)) {
248 device_printf(sc->acpi_dev,
249 "AcpiEnterSleepState failed - %s\n",
250 AcpiFormatException(status));
251 return (0); /* couldn't sleep */
252 }
253
254 if (acpi_susp_bounce)
255 resumectx(pcb);
256
257 for (;;)
258 ia32_pause();
259 } else {
260 /*
261 * Re-initialize console hardware as soon as possibe.
262 * No console output (e.g. printf) is allowed before
263 * this point.
264 */
265 cnresume();
266 npxresume(susppcbs[0]->sp_fpususpend);
267 }
268
269 return (1); /* wakeup successfully */
270 }
271
272 int
acpi_wakeup_machdep(struct acpi_softc * sc,int state,int sleep_result,int intr_enabled)273 acpi_wakeup_machdep(struct acpi_softc *sc, int state, int sleep_result,
274 int intr_enabled)
275 {
276
277 if (sleep_result == -1)
278 return (sleep_result);
279
280 if (!intr_enabled) {
281 /* Wakeup MD procedures in interrupt disabled context */
282 if (sleep_result == 1) {
283 ucode_reload();
284 pmap_init_pat();
285 initializecpu();
286 PCPU_SET(switchtime, 0);
287 PCPU_SET(switchticks, ticks);
288 #ifdef DEV_APIC
289 lapic_xapic_mode();
290 #endif
291 #ifdef SMP
292 if (!CPU_EMPTY(&suspcpus))
293 acpi_wakeup_cpus(sc);
294 #endif
295 }
296
297 #ifdef SMP
298 if (!CPU_EMPTY(&suspcpus))
299 resume_cpus(suspcpus);
300 #endif
301 mca_resume();
302 intr_resume(/*suspend_cancelled*/false);
303
304 AcpiSetFirmwareWakingVector(0, 0);
305 } else {
306 /* Wakeup MD procedures in interrupt enabled context */
307 if (sleep_result == 1 && mem_range_softc.mr_op != NULL &&
308 mem_range_softc.mr_op->reinit != NULL)
309 mem_range_softc.mr_op->reinit(&mem_range_softc);
310 }
311
312 return (sleep_result);
313 }
314
315 static void *
acpi_alloc_wakeup_handler(void * wakepages[ACPI_WAKEPAGES])316 acpi_alloc_wakeup_handler(void *wakepages[ACPI_WAKEPAGES])
317 {
318 int i;
319
320 memset(wakepages, 0, ACPI_WAKEPAGES * sizeof(*wakepages));
321
322 /*
323 * Specify the region for our wakeup code. We want it in the low 1 MB
324 * region, excluding real mode IVT (0-0x3ff), BDA (0x400-0x4ff), EBDA
325 * (less than 128KB, below 0xa0000, must be excluded by SMAP and DSDT),
326 * and ROM area (0xa0000 and above). The temporary page tables must be
327 * page-aligned.
328 */
329 for (i = 0; i < ACPI_WAKEPAGES; i++) {
330 wakepages[i] = contigmalloc(PAGE_SIZE, M_DEVBUF,
331 M_NOWAIT | M_EXEC, 0x500, 0xa0000, PAGE_SIZE, 0ul);
332 if (wakepages[i] == NULL) {
333 printf("%s: can't alloc wake memory\n", __func__);
334 goto freepages;
335 }
336 }
337 if (EVENTHANDLER_REGISTER(power_resume, acpi_stop_beep, NULL,
338 EVENTHANDLER_PRI_LAST) == NULL) {
339 printf("%s: can't register event handler\n", __func__);
340 goto freepages;
341 }
342 susppcbs = malloc(mp_ncpus * sizeof(*susppcbs), M_DEVBUF, M_WAITOK);
343 for (i = 0; i < mp_ncpus; i++) {
344 susppcbs[i] = malloc(sizeof(**susppcbs), M_DEVBUF, M_WAITOK);
345 susppcbs[i]->sp_fpususpend = alloc_fpusave(M_WAITOK);
346 }
347
348 return (wakepages);
349
350 freepages:
351 for (i = 0; i < ACPI_WAKEPAGES; i++)
352 if (wakepages[i] != NULL)
353 contigfree(wakepages[i], PAGE_SIZE, M_DEVBUF);
354 return (NULL);
355 }
356
357 void
acpi_install_wakeup_handler(struct acpi_softc * sc)358 acpi_install_wakeup_handler(struct acpi_softc *sc)
359 {
360 static void *wakeaddr;
361 void *wakepages[ACPI_WAKEPAGES];
362
363 if (wakeaddr != NULL)
364 return;
365
366 if (acpi_alloc_wakeup_handler(wakepages) == NULL)
367 return;
368
369 wakeaddr = wakepages[0];
370 sc->acpi_wakeaddr = (vm_offset_t)wakeaddr;
371 sc->acpi_wakephys = vtophys(wakeaddr);
372
373 bcopy(wakecode, (void *)sc->acpi_wakeaddr, sizeof(wakecode));
374
375 /* Patch GDT base address, ljmp targets. */
376 WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t,
377 sc->acpi_wakephys + bootgdt);
378 WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t,
379 sc->acpi_wakephys + wakeup_32);
380
381 /* Save pointers to some global data. */
382 WAKECODE_FIXUP(wakeup_ret, void *, resumectx);
383 WAKECODE_FIXUP(wakeup_cr3, register_t, pmap_get_kcr3());
384
385 if (bootverbose)
386 device_printf(sc->acpi_dev, "wakeup code va %#jx pa %#jx\n",
387 (uintmax_t)sc->acpi_wakeaddr, (uintmax_t)sc->acpi_wakephys);
388 }
389