acpi_wakeup.c revision 236938
1236772Siwasaki/*-
2236772Siwasaki * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
3236772Siwasaki * Copyright (c) 2001-2012 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
4236772Siwasaki * Copyright (c) 2003 Peter Wemm
5236772Siwasaki * Copyright (c) 2008-2012 Jung-uk Kim <jkim@FreeBSD.org>
6236772Siwasaki * All rights reserved.
7236772Siwasaki *
8236772Siwasaki * Redistribution and use in source and binary forms, with or without
9236772Siwasaki * modification, are permitted provided that the following conditions
10236772Siwasaki * are met:
11236772Siwasaki * 1. Redistributions of source code must retain the above copyright
12236772Siwasaki *    notice, this list of conditions and the following disclaimer.
13236772Siwasaki * 2. Redistributions in binary form must reproduce the above copyright
14236772Siwasaki *    notice, this list of conditions and the following disclaimer in the
15236772Siwasaki *    documentation and/or other materials provided with the distribution.
16236772Siwasaki *
17236772Siwasaki * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18236772Siwasaki * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19236772Siwasaki * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20236772Siwasaki * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21236772Siwasaki * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22236772Siwasaki * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23236772Siwasaki * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24236772Siwasaki * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25236772Siwasaki * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26236772Siwasaki * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27236772Siwasaki * SUCH DAMAGE.
28236772Siwasaki */
29236772Siwasaki
30236772Siwasaki#include <sys/cdefs.h>
31236772Siwasaki__FBSDID("$FreeBSD: head/sys/x86/acpica/acpi_wakeup.c 236938 2012-06-12 00:14:54Z iwasaki $");
32236772Siwasaki
33236772Siwasaki#include <sys/param.h>
34236772Siwasaki#include <sys/bus.h>
35236772Siwasaki#include <sys/eventhandler.h>
36236772Siwasaki#include <sys/kernel.h>
37236772Siwasaki#include <sys/malloc.h>
38236772Siwasaki#include <sys/memrange.h>
39236772Siwasaki#include <sys/smp.h>
40236772Siwasaki
41236772Siwasaki#include <vm/vm.h>
42236772Siwasaki#include <vm/pmap.h>
43236772Siwasaki
44236772Siwasaki#include <machine/clock.h>
45236772Siwasaki#include <machine/intr_machdep.h>
46236772Siwasaki#include <x86/mca.h>
47236772Siwasaki#include <machine/pcb.h>
48236772Siwasaki#include <machine/pmap.h>
49236772Siwasaki#include <machine/specialreg.h>
50236772Siwasaki#include <machine/md_var.h>
51236772Siwasaki
52236772Siwasaki#ifdef SMP
53236772Siwasaki#include <x86/apicreg.h>
54236772Siwasaki#include <machine/smp.h>
55236772Siwasaki#include <machine/vmparam.h>
56236772Siwasaki#endif
57236772Siwasaki
58236772Siwasaki#include <contrib/dev/acpica/include/acpi.h>
59236772Siwasaki
60236772Siwasaki#include <dev/acpica/acpivar.h>
61236772Siwasaki
62236772Siwasaki#include "acpi_wakecode.h"
63236772Siwasaki#include "acpi_wakedata.h"
64236772Siwasaki
65236772Siwasaki/* Make sure the code is less than a page and leave room for the stack. */
66236772SiwasakiCTASSERT(sizeof(wakecode) < PAGE_SIZE - 1024);
67236772Siwasaki
68236772Siwasakiextern int		acpi_resume_beep;
69236772Siwasakiextern int		acpi_reset_video;
70236772Siwasaki
71236772Siwasaki#ifdef SMP
72236772Siwasakiextern struct pcb	**susppcbs;
73236772Siwasakistatic cpuset_t		suspcpus;
74236772Siwasaki#else
75236772Siwasakistatic struct pcb	**susppcbs;
76236772Siwasaki#endif
77236772Siwasaki
78236772Siwasakistatic void		*acpi_alloc_wakeup_handler(void);
79236772Siwasakistatic void		acpi_stop_beep(void *);
80236772Siwasaki
81236772Siwasaki#ifdef SMP
82236772Siwasakistatic int		acpi_wakeup_ap(struct acpi_softc *, int);
83236772Siwasakistatic void		acpi_wakeup_cpus(struct acpi_softc *);
84236772Siwasaki#endif
85236772Siwasaki
86236772Siwasaki#ifdef __amd64__
87236772Siwasaki#define ACPI_PAGETABLES	3
88236772Siwasaki#else
89236772Siwasaki#define ACPI_PAGETABLES	0
90236772Siwasaki#endif
91236772Siwasaki
92236772Siwasaki#define	WAKECODE_VADDR(sc)	((sc)->acpi_wakeaddr + (ACPI_PAGETABLES * PAGE_SIZE))
93236772Siwasaki#define	WAKECODE_PADDR(sc)	((sc)->acpi_wakephys + (ACPI_PAGETABLES * PAGE_SIZE))
94236772Siwasaki#define	WAKECODE_FIXUP(offset, type, val) do	{	\
95236772Siwasaki	type	*addr;					\
96236772Siwasaki	addr = (type *)(WAKECODE_VADDR(sc) + offset);	\
97236772Siwasaki	*addr = val;					\
98236772Siwasaki} while (0)
99236772Siwasaki
100236772Siwasakistatic void
101236772Siwasakiacpi_stop_beep(void *arg)
102236772Siwasaki{
103236772Siwasaki
104236772Siwasaki	if (acpi_resume_beep != 0)
105236772Siwasaki		timer_spkr_release();
106236772Siwasaki}
107236772Siwasaki
108236772Siwasaki#ifdef SMP
109236772Siwasakistatic int
110236772Siwasakiacpi_wakeup_ap(struct acpi_softc *sc, int cpu)
111236772Siwasaki{
112236772Siwasaki	int		vector = (WAKECODE_PADDR(sc) >> 12) & 0xff;
113236772Siwasaki	int		apic_id = cpu_apic_ids[cpu];
114236772Siwasaki	int		ms;
115236772Siwasaki
116236772Siwasaki	WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[cpu]);
117236772Siwasaki	WAKECODE_FIXUP(wakeup_gdt, uint16_t, susppcbs[cpu]->pcb_gdt.rd_limit);
118236772Siwasaki	WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t,
119236772Siwasaki	    susppcbs[cpu]->pcb_gdt.rd_base);
120236772Siwasaki
121236938Siwasaki	ipi_startup(apic_id, vector);
122236772Siwasaki
123236772Siwasaki	/* Wait up to 5 seconds for it to resume. */
124236772Siwasaki	for (ms = 0; ms < 5000; ms++) {
125236772Siwasaki		if (!CPU_ISSET(cpu, &suspended_cpus))
126236772Siwasaki			return (1);	/* return SUCCESS */
127236772Siwasaki		DELAY(1000);
128236772Siwasaki	}
129236772Siwasaki	return (0);		/* return FAILURE */
130236772Siwasaki}
131236772Siwasaki
132236772Siwasaki#define	WARMBOOT_TARGET		0
133236772Siwasaki#define	WARMBOOT_OFF		(KERNBASE + 0x0467)
134236772Siwasaki#define	WARMBOOT_SEG		(KERNBASE + 0x0469)
135236772Siwasaki
136236772Siwasaki#define	CMOS_REG		(0x70)
137236772Siwasaki#define	CMOS_DATA		(0x71)
138236772Siwasaki#define	BIOS_RESET		(0x0f)
139236772Siwasaki#define	BIOS_WARM		(0x0a)
140236772Siwasaki
141236772Siwasakistatic void
142236772Siwasakiacpi_wakeup_cpus(struct acpi_softc *sc)
143236772Siwasaki{
144236772Siwasaki	uint32_t	mpbioswarmvec;
145236772Siwasaki	int		cpu;
146236772Siwasaki	u_char		mpbiosreason;
147236772Siwasaki
148236772Siwasaki	/* save the current value of the warm-start vector */
149236772Siwasaki	mpbioswarmvec = *((uint32_t *)WARMBOOT_OFF);
150236772Siwasaki	outb(CMOS_REG, BIOS_RESET);
151236772Siwasaki	mpbiosreason = inb(CMOS_DATA);
152236772Siwasaki
153236772Siwasaki	/* setup a vector to our boot code */
154236772Siwasaki	*((volatile u_short *)WARMBOOT_OFF) = WARMBOOT_TARGET;
155236772Siwasaki	*((volatile u_short *)WARMBOOT_SEG) = WAKECODE_PADDR(sc) >> 4;
156236772Siwasaki	outb(CMOS_REG, BIOS_RESET);
157236772Siwasaki	outb(CMOS_DATA, BIOS_WARM);	/* 'warm-start' */
158236772Siwasaki
159236772Siwasaki	/* Wake up each AP. */
160236772Siwasaki	for (cpu = 1; cpu < mp_ncpus; cpu++) {
161236772Siwasaki		if (!CPU_ISSET(cpu, &suspcpus))
162236772Siwasaki			continue;
163236772Siwasaki		if (acpi_wakeup_ap(sc, cpu) == 0) {
164236772Siwasaki			/* restore the warmstart vector */
165236772Siwasaki			*(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
166236772Siwasaki			panic("acpi_wakeup: failed to resume AP #%d (PHY #%d)",
167236772Siwasaki			    cpu, cpu_apic_ids[cpu]);
168236772Siwasaki		}
169236772Siwasaki	}
170236772Siwasaki
171236772Siwasaki	/* restore the warmstart vector */
172236772Siwasaki	*(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
173236772Siwasaki
174236772Siwasaki	outb(CMOS_REG, BIOS_RESET);
175236772Siwasaki	outb(CMOS_DATA, mpbiosreason);
176236772Siwasaki}
177236772Siwasaki#endif
178236772Siwasaki
179236772Siwasakiint
180236772Siwasakiacpi_sleep_machdep(struct acpi_softc *sc, int state)
181236772Siwasaki{
182236772Siwasaki	ACPI_STATUS	status;
183236772Siwasaki
184236772Siwasaki	if (sc->acpi_wakeaddr == 0ul)
185236772Siwasaki		return (-1);	/* couldn't alloc wake memory */
186236772Siwasaki
187236772Siwasaki#ifdef SMP
188236772Siwasaki	suspcpus = all_cpus;
189236772Siwasaki	CPU_CLR(PCPU_GET(cpuid), &suspcpus);
190236772Siwasaki#endif
191236772Siwasaki
192236772Siwasaki	if (acpi_resume_beep != 0)
193236772Siwasaki		timer_spkr_acquire();
194236772Siwasaki
195236772Siwasaki	AcpiSetFirmwareWakingVector(WAKECODE_PADDR(sc));
196236772Siwasaki
197236772Siwasaki	intr_suspend();
198236772Siwasaki
199236772Siwasaki	if (savectx(susppcbs[0])) {
200236772Siwasaki#ifdef __amd64__
201236772Siwasaki		ctx_fpusave(susppcbs[0]->pcb_fpususpend);
202236772Siwasaki#endif
203236772Siwasaki#ifdef SMP
204236772Siwasaki		if (!CPU_EMPTY(&suspcpus) && suspend_cpus(suspcpus) == 0) {
205236772Siwasaki			device_printf(sc->acpi_dev, "Failed to suspend APs\n");
206236772Siwasaki			return (0);	/* couldn't sleep */
207236772Siwasaki		}
208236772Siwasaki#endif
209236772Siwasaki
210236772Siwasaki		WAKECODE_FIXUP(resume_beep, uint8_t, (acpi_resume_beep != 0));
211236772Siwasaki		WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0));
212236772Siwasaki
213236772Siwasaki		WAKECODE_FIXUP(wakeup_cr4, register_t, susppcbs[0]->pcb_cr4);
214236772Siwasaki		WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[0]);
215236772Siwasaki		WAKECODE_FIXUP(wakeup_gdt, uint16_t,
216236772Siwasaki		    susppcbs[0]->pcb_gdt.rd_limit);
217236772Siwasaki		WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t,
218236772Siwasaki		    susppcbs[0]->pcb_gdt.rd_base);
219236772Siwasaki
220236772Siwasaki		/* Call ACPICA to enter the desired sleep state */
221236772Siwasaki		if (state == ACPI_STATE_S4 && sc->acpi_s4bios)
222236772Siwasaki			status = AcpiEnterSleepStateS4bios();
223236772Siwasaki		else
224236772Siwasaki			status = AcpiEnterSleepState(state, acpi_sleep_flags);
225236772Siwasaki		if (ACPI_FAILURE(status)) {
226236772Siwasaki			device_printf(sc->acpi_dev,
227236772Siwasaki			    "AcpiEnterSleepState failed - %s\n",
228236772Siwasaki			    AcpiFormatException(status));
229236772Siwasaki			return (0);	/* couldn't sleep */
230236772Siwasaki		}
231236772Siwasaki
232236772Siwasaki		for (;;)
233236772Siwasaki			ia32_pause();
234236772Siwasaki	}
235236772Siwasaki
236236772Siwasaki	return (1);	/* wakeup successfully */
237236772Siwasaki}
238236772Siwasaki
239236772Siwasakiint
240236772Siwasakiacpi_wakeup_machdep(struct acpi_softc *sc, int state, int sleep_result,
241236772Siwasaki    int intr_enabled)
242236772Siwasaki{
243236772Siwasaki
244236772Siwasaki	if (sleep_result == -1)
245236772Siwasaki		return (sleep_result);
246236772Siwasaki
247236772Siwasaki	if (!intr_enabled) {
248236772Siwasaki		/* Wakeup MD procedures in interrupt disabled context */
249236772Siwasaki		if (sleep_result == 1) {
250236772Siwasaki			pmap_init_pat();
251236772Siwasaki#if 0
252236772Siwasaki			load_cr3(susppcbs[0]->pcb_cr3);
253236772Siwasaki#endif
254236772Siwasaki			initializecpu();
255236772Siwasaki			PCPU_SET(switchtime, 0);
256236772Siwasaki			PCPU_SET(switchticks, ticks);
257236772Siwasaki#ifdef SMP
258236772Siwasaki			if (!CPU_EMPTY(&suspcpus))
259236772Siwasaki				acpi_wakeup_cpus(sc);
260236772Siwasaki#endif
261236772Siwasaki		}
262236772Siwasaki
263236772Siwasaki#ifdef SMP
264236772Siwasaki		if (!CPU_EMPTY(&suspcpus))
265236772Siwasaki			restart_cpus(suspcpus);
266236772Siwasaki#endif
267236772Siwasaki		mca_resume();
268236772Siwasaki		intr_resume();
269236772Siwasaki
270236772Siwasaki		AcpiSetFirmwareWakingVector(0);
271236772Siwasaki	} else {
272236772Siwasaki		/* Wakeup MD procedures in interrupt enabled context */
273236772Siwasaki		if (sleep_result == 1 && mem_range_softc.mr_op != NULL &&
274236772Siwasaki		    mem_range_softc.mr_op->reinit != NULL)
275236772Siwasaki			mem_range_softc.mr_op->reinit(&mem_range_softc);
276236772Siwasaki	}
277236772Siwasaki
278236772Siwasaki	return (sleep_result);
279236772Siwasaki}
280236772Siwasaki
281236772Siwasakistatic void *
282236772Siwasakiacpi_alloc_wakeup_handler(void)
283236772Siwasaki{
284236772Siwasaki	void		*wakeaddr;
285236772Siwasaki	int		i;
286236772Siwasaki
287236772Siwasaki	/*
288236772Siwasaki	 * Specify the region for our wakeup code.  We want it in the low 1 MB
289236772Siwasaki	 * region, excluding real mode IVT (0-0x3ff), BDA (0x400-0x4ff), EBDA
290236772Siwasaki	 * (less than 128KB, below 0xa0000, must be excluded by SMAP and DSDT),
291236772Siwasaki	 * and ROM area (0xa0000 and above).  The temporary page tables must be
292236772Siwasaki	 * page-aligned.
293236772Siwasaki	 */
294236772Siwasaki	wakeaddr = contigmalloc((ACPI_PAGETABLES + 1) * PAGE_SIZE, M_DEVBUF,
295236772Siwasaki	    M_WAITOK, 0x500, 0xa0000, PAGE_SIZE, 0ul);
296236772Siwasaki	if (wakeaddr == NULL) {
297236772Siwasaki		printf("%s: can't alloc wake memory\n", __func__);
298236772Siwasaki		return (NULL);
299236772Siwasaki	}
300236772Siwasaki	if (EVENTHANDLER_REGISTER(power_resume, acpi_stop_beep, NULL,
301236772Siwasaki	    EVENTHANDLER_PRI_LAST) == NULL) {
302236772Siwasaki		printf("%s: can't register event handler\n", __func__);
303236772Siwasaki		contigfree(wakeaddr, (ACPI_PAGETABLES + 1) * PAGE_SIZE, M_DEVBUF);
304236772Siwasaki		return (NULL);
305236772Siwasaki	}
306236772Siwasaki	susppcbs = malloc(mp_ncpus * sizeof(*susppcbs), M_DEVBUF, M_WAITOK);
307236772Siwasaki	for (i = 0; i < mp_ncpus; i++) {
308236772Siwasaki		susppcbs[i] = malloc(sizeof(**susppcbs), M_DEVBUF, M_WAITOK);
309236772Siwasaki#ifdef __amd64__
310236772Siwasaki		susppcbs[i]->pcb_fpususpend = alloc_fpusave(M_WAITOK);
311236772Siwasaki#endif
312236772Siwasaki	}
313236772Siwasaki
314236772Siwasaki	return (wakeaddr);
315236772Siwasaki}
316236772Siwasaki
317236772Siwasakivoid
318236772Siwasakiacpi_install_wakeup_handler(struct acpi_softc *sc)
319236772Siwasaki{
320236772Siwasaki	static void	*wakeaddr = NULL;
321236772Siwasaki#ifdef __amd64__
322236772Siwasaki	uint64_t	*pt4, *pt3, *pt2;
323236772Siwasaki	int		i;
324236772Siwasaki#endif
325236772Siwasaki
326236772Siwasaki	if (wakeaddr != NULL)
327236772Siwasaki		return;
328236772Siwasaki
329236772Siwasaki	wakeaddr = acpi_alloc_wakeup_handler();
330236772Siwasaki	if (wakeaddr == NULL)
331236772Siwasaki		return;
332236772Siwasaki
333236772Siwasaki	sc->acpi_wakeaddr = (vm_offset_t)wakeaddr;
334236772Siwasaki	sc->acpi_wakephys = vtophys(wakeaddr);
335236772Siwasaki
336236772Siwasaki	bcopy(wakecode, (void *)WAKECODE_VADDR(sc), sizeof(wakecode));
337236772Siwasaki
338236772Siwasaki	/* Patch GDT base address, ljmp targets. */
339236772Siwasaki	WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t,
340236772Siwasaki	    WAKECODE_PADDR(sc) + bootgdt);
341236772Siwasaki	WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t,
342236772Siwasaki	    WAKECODE_PADDR(sc) + wakeup_32);
343236772Siwasaki#ifdef __amd64__
344236772Siwasaki	WAKECODE_FIXUP((wakeup_sw64 + 1), uint32_t,
345236772Siwasaki	    WAKECODE_PADDR(sc) + wakeup_64);
346236772Siwasaki	WAKECODE_FIXUP(wakeup_pagetables, uint32_t, sc->acpi_wakephys);
347236772Siwasaki#endif
348236772Siwasaki
349236772Siwasaki	/* Save pointers to some global data. */
350236772Siwasaki	WAKECODE_FIXUP(wakeup_ret, void *, resumectx);
351236772Siwasaki#ifdef __amd64__
352236772Siwasaki	WAKECODE_FIXUP(wakeup_cr3, uint64_t, KPML4phys);
353236772Siwasaki#else
354236772Siwasaki#ifdef PAE
355236772Siwasaki	WAKECODE_FIXUP(wakeup_cr3, register_t, vtophys(kernel_pmap->pm_pdpt));
356236772Siwasaki#else
357236772Siwasaki	WAKECODE_FIXUP(wakeup_cr3, register_t, vtophys(kernel_pmap->pm_pdir));
358236772Siwasaki#endif
359236772Siwasaki#endif
360236772Siwasaki
361236772Siwasaki#ifdef __amd64__
362236772Siwasaki	/* Build temporary page tables below realmode code. */
363236772Siwasaki	pt4 = wakeaddr;
364236772Siwasaki	pt3 = pt4 + (PAGE_SIZE) / sizeof(uint64_t);
365236772Siwasaki	pt2 = pt3 + (PAGE_SIZE) / sizeof(uint64_t);
366236772Siwasaki
367236772Siwasaki	/* Create the initial 1GB replicated page tables */
368236772Siwasaki	for (i = 0; i < 512; i++) {
369236772Siwasaki		/*
370236772Siwasaki		 * Each slot of the level 4 pages points
371236772Siwasaki		 * to the same level 3 page
372236772Siwasaki		 */
373236772Siwasaki		pt4[i] = (uint64_t)(sc->acpi_wakephys + PAGE_SIZE);
374236772Siwasaki		pt4[i] |= PG_V | PG_RW | PG_U;
375236772Siwasaki
376236772Siwasaki		/*
377236772Siwasaki		 * Each slot of the level 3 pages points
378236772Siwasaki		 * to the same level 2 page
379236772Siwasaki		 */
380236772Siwasaki		pt3[i] = (uint64_t)(sc->acpi_wakephys + (2 * PAGE_SIZE));
381236772Siwasaki		pt3[i] |= PG_V | PG_RW | PG_U;
382236772Siwasaki
383236772Siwasaki		/* The level 2 page slots are mapped with 2MB pages for 1GB. */
384236772Siwasaki		pt2[i] = i * (2 * 1024 * 1024);
385236772Siwasaki		pt2[i] |= PG_V | PG_RW | PG_PS | PG_U;
386236772Siwasaki	}
387236772Siwasaki#endif
388236772Siwasaki
389236772Siwasaki	if (bootverbose)
390236772Siwasaki		device_printf(sc->acpi_dev, "wakeup code va %#jx pa %#jx\n",
391236772Siwasaki		    (uintmax_t)sc->acpi_wakeaddr, (uintmax_t)sc->acpi_wakephys);
392236772Siwasaki}
393