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