acpi_wakeup.c revision 231979
1/*-
2 * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
3 * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
4 * Copyright (c) 2003 Peter Wemm
5 * Copyright (c) 2008-2010 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/9/sys/amd64/acpica/acpi_wakeup.c 231979 2012-02-21 20:56:03Z kib $");
32
33#include <sys/param.h>
34#include <sys/bus.h>
35#include <sys/kernel.h>
36#include <sys/malloc.h>
37#include <sys/memrange.h>
38#include <sys/smp.h>
39
40#include <vm/vm.h>
41#include <vm/pmap.h>
42
43#include <machine/intr_machdep.h>
44#include <x86/mca.h>
45#include <machine/pcb.h>
46#include <machine/pmap.h>
47#include <machine/specialreg.h>
48#include <machine/md_var.h>
49
50#ifdef SMP
51#include <x86/apicreg.h>
52#include <machine/smp.h>
53#include <machine/vmparam.h>
54#endif
55
56#include <contrib/dev/acpica/include/acpi.h>
57
58#include <dev/acpica/acpivar.h>
59
60#include "acpi_wakecode.h"
61#include "acpi_wakedata.h"
62
63/* Make sure the code is less than a page and leave room for the stack. */
64CTASSERT(sizeof(wakecode) < PAGE_SIZE - 1024);
65
66extern int		acpi_resume_beep;
67extern int		acpi_reset_video;
68
69#ifdef SMP
70extern struct pcb	**susppcbs;
71extern void		**suspfpusave;
72#else
73static struct pcb	**susppcbs;
74static void		**suspfpusave;
75#endif
76
77int			acpi_restorecpu(vm_offset_t, struct pcb *);
78
79static void		*acpi_alloc_wakeup_handler(void);
80static void		acpi_stop_beep(void *);
81
82#ifdef SMP
83static int		acpi_wakeup_ap(struct acpi_softc *, int);
84static void		acpi_wakeup_cpus(struct acpi_softc *, const cpuset_t *);
85#endif
86
87#define	WAKECODE_VADDR(sc)	((sc)->acpi_wakeaddr + (3 * PAGE_SIZE))
88#define	WAKECODE_PADDR(sc)	((sc)->acpi_wakephys + (3 * PAGE_SIZE))
89#define	WAKECODE_FIXUP(offset, type, val) do	{	\
90	type	*addr;					\
91	addr = (type *)(WAKECODE_VADDR(sc) + offset);	\
92	*addr = val;					\
93} while (0)
94
95/* Turn off bits 1&2 of the PIT, stopping the beep. */
96static void
97acpi_stop_beep(void *arg)
98{
99	outb(0x61, inb(0x61) & ~0x3);
100}
101
102#ifdef SMP
103static int
104acpi_wakeup_ap(struct acpi_softc *sc, int cpu)
105{
106	int		vector = (WAKECODE_PADDR(sc) >> 12) & 0xff;
107	int		apic_id = cpu_apic_ids[cpu];
108	int		ms;
109
110	WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[cpu]);
111	WAKECODE_FIXUP(wakeup_fpusave, void *, suspfpusave[cpu]);
112	WAKECODE_FIXUP(wakeup_gdt, uint16_t, susppcbs[cpu]->pcb_gdt.rd_limit);
113	WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t,
114	    susppcbs[cpu]->pcb_gdt.rd_base);
115	WAKECODE_FIXUP(wakeup_cpu, int, cpu);
116
117	/* do an INIT IPI: assert RESET */
118	lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
119	    APIC_LEVEL_ASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, apic_id);
120
121	/* wait for pending status end */
122	lapic_ipi_wait(-1);
123
124	/* do an INIT IPI: deassert RESET */
125	lapic_ipi_raw(APIC_DEST_ALLESELF | APIC_TRIGMOD_LEVEL |
126	    APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, 0);
127
128	/* wait for pending status end */
129	DELAY(10000);		/* wait ~10mS */
130	lapic_ipi_wait(-1);
131
132	/*
133	 * next we do a STARTUP IPI: the previous INIT IPI might still be
134	 * latched, (P5 bug) this 1st STARTUP would then terminate
135	 * immediately, and the previously started INIT IPI would continue. OR
136	 * the previous INIT IPI has already run. and this STARTUP IPI will
137	 * run. OR the previous INIT IPI was ignored. and this STARTUP IPI
138	 * will run.
139	 */
140
141	/* do a STARTUP IPI */
142	lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
143	    APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP |
144	    vector, apic_id);
145	lapic_ipi_wait(-1);
146	DELAY(200);		/* wait ~200uS */
147
148	/*
149	 * finally we do a 2nd STARTUP IPI: this 2nd STARTUP IPI should run IF
150	 * the previous STARTUP IPI was cancelled by a latched INIT IPI. OR
151	 * this STARTUP IPI will be ignored, as only ONE STARTUP IPI is
152	 * recognized after hardware RESET or INIT IPI.
153	 */
154
155	lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
156	    APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP |
157	    vector, apic_id);
158	lapic_ipi_wait(-1);
159	DELAY(200);		/* wait ~200uS */
160
161	/* Wait up to 5 seconds for it to start. */
162	for (ms = 0; ms < 5000; ms++) {
163		if (*(int *)(WAKECODE_VADDR(sc) + wakeup_cpu) == 0)
164			return (1);	/* return SUCCESS */
165		DELAY(1000);
166	}
167	return (0);		/* return FAILURE */
168}
169
170#define	WARMBOOT_TARGET		0
171#define	WARMBOOT_OFF		(KERNBASE + 0x0467)
172#define	WARMBOOT_SEG		(KERNBASE + 0x0469)
173
174#define	CMOS_REG		(0x70)
175#define	CMOS_DATA		(0x71)
176#define	BIOS_RESET		(0x0f)
177#define	BIOS_WARM		(0x0a)
178
179static void
180acpi_wakeup_cpus(struct acpi_softc *sc, const cpuset_t *wakeup_cpus)
181{
182	uint32_t	mpbioswarmvec;
183	int		cpu;
184	u_char		mpbiosreason;
185
186	/* save the current value of the warm-start vector */
187	mpbioswarmvec = *((uint32_t *)WARMBOOT_OFF);
188	outb(CMOS_REG, BIOS_RESET);
189	mpbiosreason = inb(CMOS_DATA);
190
191	/* setup a vector to our boot code */
192	*((volatile u_short *)WARMBOOT_OFF) = WARMBOOT_TARGET;
193	*((volatile u_short *)WARMBOOT_SEG) = WAKECODE_PADDR(sc) >> 4;
194	outb(CMOS_REG, BIOS_RESET);
195	outb(CMOS_DATA, BIOS_WARM);	/* 'warm-start' */
196
197	/* Wake up each AP. */
198	for (cpu = 1; cpu < mp_ncpus; cpu++) {
199		if (!CPU_ISSET(cpu, wakeup_cpus))
200			continue;
201		if (acpi_wakeup_ap(sc, cpu) == 0) {
202			/* restore the warmstart vector */
203			*(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
204			panic("acpi_wakeup: failed to resume AP #%d (PHY #%d)",
205			    cpu, cpu_apic_ids[cpu]);
206		}
207	}
208
209	/* restore the warmstart vector */
210	*(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
211
212	outb(CMOS_REG, BIOS_RESET);
213	outb(CMOS_DATA, mpbiosreason);
214}
215#endif
216
217int
218acpi_sleep_machdep(struct acpi_softc *sc, int state)
219{
220#ifdef SMP
221	cpuset_t	wakeup_cpus;
222#endif
223	register_t	cr3, rf;
224	ACPI_STATUS	status;
225	int		ret;
226
227	ret = -1;
228
229	if (sc->acpi_wakeaddr == 0ul)
230		return (ret);
231
232#ifdef SMP
233	wakeup_cpus = all_cpus;
234	CPU_CLR(PCPU_GET(cpuid), &wakeup_cpus);
235#endif
236
237	AcpiSetFirmwareWakingVector(WAKECODE_PADDR(sc));
238
239	rf = intr_disable();
240	intr_suspend();
241
242	/*
243	 * Temporarily switch to the kernel pmap because it provides
244	 * an identity mapping (setup at boot) for the low physical
245	 * memory region containing the wakeup code.
246	 */
247	cr3 = rcr3();
248	load_cr3(KPML4phys);
249
250	if (savectx(susppcbs[0])) {
251		ctx_fpusave(suspfpusave[0]);
252#ifdef SMP
253		if (!CPU_EMPTY(&wakeup_cpus) &&
254		    suspend_cpus(wakeup_cpus) == 0) {
255			device_printf(sc->acpi_dev, "Failed to suspend APs\n");
256			goto out;
257		}
258#endif
259
260		WAKECODE_FIXUP(resume_beep, uint8_t, (acpi_resume_beep != 0));
261		WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0));
262
263		WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[0]);
264		WAKECODE_FIXUP(wakeup_fpusave, void *, suspfpusave[0]);
265		WAKECODE_FIXUP(wakeup_gdt, uint16_t,
266		    susppcbs[0]->pcb_gdt.rd_limit);
267		WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t,
268		    susppcbs[0]->pcb_gdt.rd_base);
269		WAKECODE_FIXUP(wakeup_cpu, int, 0);
270
271		/* Call ACPICA to enter the desired sleep state */
272		if (state == ACPI_STATE_S4 && sc->acpi_s4bios)
273			status = AcpiEnterSleepStateS4bios();
274		else
275			status = AcpiEnterSleepState(state);
276
277		if (status != AE_OK) {
278			device_printf(sc->acpi_dev,
279			    "AcpiEnterSleepState failed - %s\n",
280			    AcpiFormatException(status));
281			goto out;
282		}
283
284		for (;;)
285			ia32_pause();
286	} else {
287		pmap_init_pat();
288		PCPU_SET(switchtime, 0);
289		PCPU_SET(switchticks, ticks);
290#ifdef SMP
291		if (!CPU_EMPTY(&wakeup_cpus))
292			acpi_wakeup_cpus(sc, &wakeup_cpus);
293#endif
294		acpi_resync_clock(sc);
295		ret = 0;
296	}
297
298out:
299#ifdef SMP
300	if (!CPU_EMPTY(&wakeup_cpus))
301		restart_cpus(wakeup_cpus);
302#endif
303
304	load_cr3(cr3);
305	mca_resume();
306	intr_resume();
307	intr_restore(rf);
308
309	AcpiSetFirmwareWakingVector(0);
310
311	if (ret == 0 && mem_range_softc.mr_op != NULL &&
312	    mem_range_softc.mr_op->reinit != NULL)
313		mem_range_softc.mr_op->reinit(&mem_range_softc);
314
315	/* If we beeped, turn it off after a delay. */
316	if (acpi_resume_beep)
317		timeout(acpi_stop_beep, NULL, 3 * hz);
318
319	return (ret);
320}
321
322static void *
323acpi_alloc_wakeup_handler(void)
324{
325	void		*wakeaddr;
326	int		i;
327
328	/*
329	 * Specify the region for our wakeup code.  We want it in the low 1 MB
330	 * region, excluding real mode IVT (0-0x3ff), BDA (0x400-0x4ff), EBDA
331	 * (less than 128KB, below 0xa0000, must be excluded by SMAP and DSDT),
332	 * and ROM area (0xa0000 and above).  The temporary page tables must be
333	 * page-aligned.
334	 */
335	wakeaddr = contigmalloc(4 * PAGE_SIZE, M_DEVBUF, M_NOWAIT, 0x500,
336	    0xa0000, PAGE_SIZE, 0ul);
337	if (wakeaddr == NULL) {
338		printf("%s: can't alloc wake memory\n", __func__);
339		return (NULL);
340	}
341	susppcbs = malloc(mp_ncpus * sizeof(*susppcbs), M_DEVBUF, M_WAITOK);
342	suspfpusave = malloc(mp_ncpus * sizeof(void *), M_DEVBUF, M_WAITOK);
343	for (i = 0; i < mp_ncpus; i++) {
344		susppcbs[i] = malloc(sizeof(**susppcbs), M_DEVBUF, M_WAITOK);
345		suspfpusave[i] = alloc_fpusave(M_WAITOK);
346	}
347
348	return (wakeaddr);
349}
350
351void
352acpi_install_wakeup_handler(struct acpi_softc *sc)
353{
354	static void	*wakeaddr = NULL;
355	uint64_t	*pt4, *pt3, *pt2;
356	int		i;
357
358	if (wakeaddr != NULL)
359		return;
360
361	wakeaddr = acpi_alloc_wakeup_handler();
362	if (wakeaddr == NULL)
363		return;
364
365	sc->acpi_wakeaddr = (vm_offset_t)wakeaddr;
366	sc->acpi_wakephys = vtophys(wakeaddr);
367
368	bcopy(wakecode, (void *)WAKECODE_VADDR(sc), sizeof(wakecode));
369
370	/* Patch GDT base address, ljmp targets and page table base address. */
371	WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t,
372	    WAKECODE_PADDR(sc) + bootgdt);
373	WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t,
374	    WAKECODE_PADDR(sc) + wakeup_32);
375	WAKECODE_FIXUP((wakeup_sw64 + 1), uint32_t,
376	    WAKECODE_PADDR(sc) + wakeup_64);
377	WAKECODE_FIXUP(wakeup_pagetables, uint32_t, sc->acpi_wakephys);
378
379	/* Save pointers to some global data. */
380	WAKECODE_FIXUP(wakeup_retaddr, void *, acpi_restorecpu);
381	WAKECODE_FIXUP(wakeup_kpml4, uint64_t, KPML4phys);
382	WAKECODE_FIXUP(wakeup_ctx, vm_offset_t,
383	    WAKECODE_VADDR(sc) + wakeup_ctx);
384	WAKECODE_FIXUP(wakeup_efer, uint64_t, rdmsr(MSR_EFER));
385	WAKECODE_FIXUP(wakeup_star, uint64_t, rdmsr(MSR_STAR));
386	WAKECODE_FIXUP(wakeup_lstar, uint64_t, rdmsr(MSR_LSTAR));
387	WAKECODE_FIXUP(wakeup_cstar, uint64_t, rdmsr(MSR_CSTAR));
388	WAKECODE_FIXUP(wakeup_sfmask, uint64_t, rdmsr(MSR_SF_MASK));
389
390	/* Build temporary page tables below realmode code. */
391	pt4 = wakeaddr;
392	pt3 = pt4 + (PAGE_SIZE) / sizeof(uint64_t);
393	pt2 = pt3 + (PAGE_SIZE) / sizeof(uint64_t);
394
395	/* Create the initial 1GB replicated page tables */
396	for (i = 0; i < 512; i++) {
397		/*
398		 * Each slot of the level 4 pages points
399		 * to the same level 3 page
400		 */
401		pt4[i] = (uint64_t)(sc->acpi_wakephys + PAGE_SIZE);
402		pt4[i] |= PG_V | PG_RW | PG_U;
403
404		/*
405		 * Each slot of the level 3 pages points
406		 * to the same level 2 page
407		 */
408		pt3[i] = (uint64_t)(sc->acpi_wakephys + (2 * PAGE_SIZE));
409		pt3[i] |= PG_V | PG_RW | PG_U;
410
411		/* The level 2 page slots are mapped with 2MB pages for 1GB. */
412		pt2[i] = i * (2 * 1024 * 1024);
413		pt2[i] |= PG_V | PG_RW | PG_PS | PG_U;
414	}
415
416	if (bootverbose)
417		device_printf(sc->acpi_dev, "wakeup code va %p pa %p\n",
418		    (void *)sc->acpi_wakeaddr, (void *)sc->acpi_wakephys);
419}
420