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