acpi_wakeup.c revision 281495
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: head/sys/x86/acpica/acpi_wakeup.c 281495 2015-04-13 15:22:45Z kib $");
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. */
80CTASSERT(sizeof(wakecode) < PAGE_SIZE - 1024);
81
82extern int		acpi_resume_beep;
83extern int		acpi_reset_video;
84
85#ifdef SMP
86extern struct susppcb	**susppcbs;
87static cpuset_t		suspcpus;
88#else
89static struct susppcb	**susppcbs;
90#endif
91
92static void		*acpi_alloc_wakeup_handler(void);
93static void		acpi_stop_beep(void *);
94
95#ifdef SMP
96static int		acpi_wakeup_ap(struct acpi_softc *, int);
97static 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
116static void
117acpi_stop_beep(void *arg)
118{
119
120	if (acpi_resume_beep != 0)
121		timer_spkr_release();
122}
123
124#ifdef SMP
125static int
126acpi_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
158static void
159acpi_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
196int
197acpi_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));
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
266int
267acpi_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);
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
312static void *
313acpi_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
347void
348acpi_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