acpi_wakeup.c revision 262981
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: stable/10/sys/x86/acpica/acpi_wakeup.c 262981 2014-03-10 20:47:24Z jkim $");
32
33#include <sys/param.h>
34#include <sys/bus.h>
35#include <sys/eventhandler.h>
36#include <sys/kernel.h>
37#include <sys/malloc.h>
38#include <sys/memrange.h>
39#include <sys/smp.h>
40#include <sys/systm.h>
41
42#include <vm/vm.h>
43#include <vm/pmap.h>
44
45#include <machine/clock.h>
46#include <machine/cpu.h>
47#include <machine/intr_machdep.h>
48#include <x86/mca.h>
49#include <machine/pcb.h>
50#include <machine/pmap.h>
51#include <machine/specialreg.h>
52#include <machine/md_var.h>
53
54#ifdef SMP
55#include <x86/apicreg.h>
56#include <machine/smp.h>
57#include <machine/vmparam.h>
58#endif
59
60#include <contrib/dev/acpica/include/acpi.h>
61
62#include <dev/acpica/acpivar.h>
63
64#include "acpi_wakecode.h"
65#include "acpi_wakedata.h"
66
67/* Make sure the code is less than a page and leave room for the stack. */
68CTASSERT(sizeof(wakecode) < PAGE_SIZE - 1024);
69
70extern int		acpi_resume_beep;
71extern int		acpi_reset_video;
72
73#ifdef SMP
74extern struct pcb	**susppcbs;
75static cpuset_t		suspcpus;
76#else
77static struct pcb	**susppcbs;
78#endif
79
80static void		*acpi_alloc_wakeup_handler(void);
81static void		acpi_stop_beep(void *);
82
83#ifdef SMP
84static int		acpi_wakeup_ap(struct acpi_softc *, int);
85static void		acpi_wakeup_cpus(struct acpi_softc *);
86#endif
87
88#ifdef __amd64__
89#define ACPI_PAGETABLES	3
90#else
91#define ACPI_PAGETABLES	0
92#endif
93
94#define	WAKECODE_VADDR(sc)				\
95    ((sc)->acpi_wakeaddr + (ACPI_PAGETABLES * PAGE_SIZE))
96#define	WAKECODE_PADDR(sc)				\
97    ((sc)->acpi_wakephys + (ACPI_PAGETABLES * PAGE_SIZE))
98#define	WAKECODE_FIXUP(offset, type, val)	do {	\
99	type	*addr;					\
100	addr = (type *)(WAKECODE_VADDR(sc) + offset);	\
101	*addr = val;					\
102} while (0)
103
104static void
105acpi_stop_beep(void *arg)
106{
107
108	if (acpi_resume_beep != 0)
109		timer_spkr_release();
110}
111
112#ifdef SMP
113static int
114acpi_wakeup_ap(struct acpi_softc *sc, int cpu)
115{
116	int		vector = (WAKECODE_PADDR(sc) >> 12) & 0xff;
117	int		apic_id = cpu_apic_ids[cpu];
118	int		ms;
119
120	WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[cpu]);
121	WAKECODE_FIXUP(wakeup_gdt, uint16_t, susppcbs[cpu]->pcb_gdt.rd_limit);
122	WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t,
123	    susppcbs[cpu]->pcb_gdt.rd_base);
124
125	ipi_startup(apic_id, vector);
126
127	/* Wait up to 5 seconds for it to resume. */
128	for (ms = 0; ms < 5000; ms++) {
129		if (!CPU_ISSET(cpu, &suspended_cpus))
130			return (1);	/* return SUCCESS */
131		DELAY(1000);
132	}
133	return (0);		/* return FAILURE */
134}
135
136#define	WARMBOOT_TARGET		0
137#define	WARMBOOT_OFF		(KERNBASE + 0x0467)
138#define	WARMBOOT_SEG		(KERNBASE + 0x0469)
139
140#define	CMOS_REG		(0x70)
141#define	CMOS_DATA		(0x71)
142#define	BIOS_RESET		(0x0f)
143#define	BIOS_WARM		(0x0a)
144
145static void
146acpi_wakeup_cpus(struct acpi_softc *sc)
147{
148	uint32_t	mpbioswarmvec;
149	int		cpu;
150	u_char		mpbiosreason;
151
152	/* save the current value of the warm-start vector */
153	mpbioswarmvec = *((uint32_t *)WARMBOOT_OFF);
154	outb(CMOS_REG, BIOS_RESET);
155	mpbiosreason = inb(CMOS_DATA);
156
157	/* setup a vector to our boot code */
158	*((volatile u_short *)WARMBOOT_OFF) = WARMBOOT_TARGET;
159	*((volatile u_short *)WARMBOOT_SEG) = WAKECODE_PADDR(sc) >> 4;
160	outb(CMOS_REG, BIOS_RESET);
161	outb(CMOS_DATA, BIOS_WARM);	/* 'warm-start' */
162
163	/* Wake up each AP. */
164	for (cpu = 1; cpu < mp_ncpus; cpu++) {
165		if (!CPU_ISSET(cpu, &suspcpus))
166			continue;
167		if (acpi_wakeup_ap(sc, cpu) == 0) {
168			/* restore the warmstart vector */
169			*(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
170			panic("acpi_wakeup: failed to resume AP #%d (PHY #%d)",
171			    cpu, cpu_apic_ids[cpu]);
172		}
173	}
174
175	/* restore the warmstart vector */
176	*(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
177
178	outb(CMOS_REG, BIOS_RESET);
179	outb(CMOS_DATA, mpbiosreason);
180}
181#endif
182
183int
184acpi_sleep_machdep(struct acpi_softc *sc, int state)
185{
186	ACPI_STATUS	status;
187
188	if (sc->acpi_wakeaddr == 0ul)
189		return (-1);	/* couldn't alloc wake memory */
190
191#ifdef SMP
192	suspcpus = all_cpus;
193	CPU_CLR(PCPU_GET(cpuid), &suspcpus);
194#endif
195
196	if (acpi_resume_beep != 0)
197		timer_spkr_acquire();
198
199	AcpiSetFirmwareWakingVector(WAKECODE_PADDR(sc));
200
201	intr_suspend();
202
203	if (savectx(susppcbs[0])) {
204#ifdef __amd64__
205		fpususpend(susppcbs[0]->pcb_fpususpend);
206#endif
207#ifdef SMP
208		if (!CPU_EMPTY(&suspcpus) && suspend_cpus(suspcpus) == 0) {
209			device_printf(sc->acpi_dev, "Failed to suspend APs\n");
210			return (0);	/* couldn't sleep */
211		}
212#endif
213
214		WAKECODE_FIXUP(resume_beep, uint8_t, (acpi_resume_beep != 0));
215		WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0));
216
217#ifndef __amd64__
218		WAKECODE_FIXUP(wakeup_cr4, register_t, susppcbs[0]->pcb_cr4);
219#endif
220		WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[0]);
221		WAKECODE_FIXUP(wakeup_gdt, uint16_t,
222		    susppcbs[0]->pcb_gdt.rd_limit);
223		WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t,
224		    susppcbs[0]->pcb_gdt.rd_base);
225
226		/* Call ACPICA to enter the desired sleep state */
227		if (state == ACPI_STATE_S4 && sc->acpi_s4bios)
228			status = AcpiEnterSleepStateS4bios();
229		else
230			status = AcpiEnterSleepState(state);
231		if (ACPI_FAILURE(status)) {
232			device_printf(sc->acpi_dev,
233			    "AcpiEnterSleepState failed - %s\n",
234			    AcpiFormatException(status));
235			return (0);	/* couldn't sleep */
236		}
237
238		for (;;)
239			ia32_pause();
240	}
241
242	return (1);	/* wakeup successfully */
243}
244
245int
246acpi_wakeup_machdep(struct acpi_softc *sc, int state, int sleep_result,
247    int intr_enabled)
248{
249
250	if (sleep_result == -1)
251		return (sleep_result);
252
253	if (!intr_enabled) {
254		/* Wakeup MD procedures in interrupt disabled context */
255		if (sleep_result == 1) {
256			pmap_init_pat();
257			initializecpu();
258			PCPU_SET(switchtime, 0);
259			PCPU_SET(switchticks, ticks);
260#ifdef SMP
261			if (!CPU_EMPTY(&suspcpus))
262				acpi_wakeup_cpus(sc);
263#endif
264		}
265
266#ifdef SMP
267		if (!CPU_EMPTY(&suspcpus))
268			restart_cpus(suspcpus);
269#endif
270		mca_resume();
271#ifdef __amd64__
272		if (vmm_resume_p != NULL)
273			vmm_resume_p();
274#endif
275		intr_resume(/*suspend_cancelled*/false);
276
277		AcpiSetFirmwareWakingVector(0);
278	} else {
279		/* Wakeup MD procedures in interrupt enabled context */
280		if (sleep_result == 1 && mem_range_softc.mr_op != NULL &&
281		    mem_range_softc.mr_op->reinit != NULL)
282			mem_range_softc.mr_op->reinit(&mem_range_softc);
283	}
284
285	return (sleep_result);
286}
287
288static void *
289acpi_alloc_wakeup_handler(void)
290{
291	void		*wakeaddr;
292	int		i;
293
294	/*
295	 * Specify the region for our wakeup code.  We want it in the low 1 MB
296	 * region, excluding real mode IVT (0-0x3ff), BDA (0x400-0x4ff), EBDA
297	 * (less than 128KB, below 0xa0000, must be excluded by SMAP and DSDT),
298	 * and ROM area (0xa0000 and above).  The temporary page tables must be
299	 * page-aligned.
300	 */
301	wakeaddr = contigmalloc((ACPI_PAGETABLES + 1) * PAGE_SIZE, M_DEVBUF,
302	    M_WAITOK, 0x500, 0xa0000, PAGE_SIZE, 0ul);
303	if (wakeaddr == NULL) {
304		printf("%s: can't alloc wake memory\n", __func__);
305		return (NULL);
306	}
307	if (EVENTHANDLER_REGISTER(power_resume, acpi_stop_beep, NULL,
308	    EVENTHANDLER_PRI_LAST) == NULL) {
309		printf("%s: can't register event handler\n", __func__);
310		contigfree(wakeaddr, (ACPI_PAGETABLES + 1) * PAGE_SIZE,
311		    M_DEVBUF);
312		return (NULL);
313	}
314	susppcbs = malloc(mp_ncpus * sizeof(*susppcbs), M_DEVBUF, M_WAITOK);
315	for (i = 0; i < mp_ncpus; i++) {
316		susppcbs[i] = malloc(sizeof(**susppcbs), M_DEVBUF, M_WAITOK);
317#ifdef __amd64__
318		susppcbs[i]->pcb_fpususpend = alloc_fpusave(M_WAITOK);
319#endif
320	}
321
322	return (wakeaddr);
323}
324
325void
326acpi_install_wakeup_handler(struct acpi_softc *sc)
327{
328	static void	*wakeaddr = NULL;
329#ifdef __amd64__
330	uint64_t	*pt4, *pt3, *pt2;
331	int		i;
332#endif
333
334	if (wakeaddr != NULL)
335		return;
336
337	wakeaddr = acpi_alloc_wakeup_handler();
338	if (wakeaddr == NULL)
339		return;
340
341	sc->acpi_wakeaddr = (vm_offset_t)wakeaddr;
342	sc->acpi_wakephys = vtophys(wakeaddr);
343
344	bcopy(wakecode, (void *)WAKECODE_VADDR(sc), sizeof(wakecode));
345
346	/* Patch GDT base address, ljmp targets. */
347	WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t,
348	    WAKECODE_PADDR(sc) + bootgdt);
349	WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t,
350	    WAKECODE_PADDR(sc) + wakeup_32);
351#ifdef __amd64__
352	WAKECODE_FIXUP((wakeup_sw64 + 1), uint32_t,
353	    WAKECODE_PADDR(sc) + wakeup_64);
354	WAKECODE_FIXUP(wakeup_pagetables, uint32_t, sc->acpi_wakephys);
355#endif
356
357	/* Save pointers to some global data. */
358	WAKECODE_FIXUP(wakeup_ret, void *, resumectx);
359#ifndef __amd64__
360#ifdef PAE
361	WAKECODE_FIXUP(wakeup_cr3, register_t, vtophys(kernel_pmap->pm_pdpt));
362#else
363	WAKECODE_FIXUP(wakeup_cr3, register_t, vtophys(kernel_pmap->pm_pdir));
364#endif
365
366#else
367	/* Build temporary page tables below realmode code. */
368	pt4 = wakeaddr;
369	pt3 = pt4 + (PAGE_SIZE) / sizeof(uint64_t);
370	pt2 = pt3 + (PAGE_SIZE) / sizeof(uint64_t);
371
372	/* Create the initial 1GB replicated page tables */
373	for (i = 0; i < 512; i++) {
374		/*
375		 * Each slot of the level 4 pages points
376		 * to the same level 3 page
377		 */
378		pt4[i] = (uint64_t)(sc->acpi_wakephys + PAGE_SIZE);
379		pt4[i] |= PG_V | PG_RW | PG_U;
380
381		/*
382		 * Each slot of the level 3 pages points
383		 * to the same level 2 page
384		 */
385		pt3[i] = (uint64_t)(sc->acpi_wakephys + (2 * PAGE_SIZE));
386		pt3[i] |= PG_V | PG_RW | PG_U;
387
388		/* The level 2 page slots are mapped with 2MB pages for 1GB. */
389		pt2[i] = i * (2 * 1024 * 1024);
390		pt2[i] |= PG_V | PG_RW | PG_PS | PG_U;
391	}
392#endif
393
394	if (bootverbose)
395		device_printf(sc->acpi_dev, "wakeup code va %#jx pa %#jx\n",
396		    (uintmax_t)sc->acpi_wakeaddr, (uintmax_t)sc->acpi_wakephys);
397}
398