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$");
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
92237037Sjkim#define	WAKECODE_VADDR(sc)				\
93237037Sjkim    ((sc)->acpi_wakeaddr + (ACPI_PAGETABLES * PAGE_SIZE))
94237037Sjkim#define	WAKECODE_PADDR(sc)				\
95237037Sjkim    ((sc)->acpi_wakephys + (ACPI_PAGETABLES * PAGE_SIZE))
96237037Sjkim#define	WAKECODE_FIXUP(offset, type, val)	do {	\
97236772Siwasaki	type	*addr;					\
98236772Siwasaki	addr = (type *)(WAKECODE_VADDR(sc) + offset);	\
99236772Siwasaki	*addr = val;					\
100236772Siwasaki} while (0)
101236772Siwasaki
102236772Siwasakistatic void
103236772Siwasakiacpi_stop_beep(void *arg)
104236772Siwasaki{
105236772Siwasaki
106236772Siwasaki	if (acpi_resume_beep != 0)
107236772Siwasaki		timer_spkr_release();
108236772Siwasaki}
109236772Siwasaki
110236772Siwasaki#ifdef SMP
111236772Siwasakistatic int
112236772Siwasakiacpi_wakeup_ap(struct acpi_softc *sc, int cpu)
113236772Siwasaki{
114236772Siwasaki	int		vector = (WAKECODE_PADDR(sc) >> 12) & 0xff;
115236772Siwasaki	int		apic_id = cpu_apic_ids[cpu];
116236772Siwasaki	int		ms;
117236772Siwasaki
118236772Siwasaki	WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[cpu]);
119236772Siwasaki	WAKECODE_FIXUP(wakeup_gdt, uint16_t, susppcbs[cpu]->pcb_gdt.rd_limit);
120236772Siwasaki	WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t,
121236772Siwasaki	    susppcbs[cpu]->pcb_gdt.rd_base);
122236772Siwasaki
123236938Siwasaki	ipi_startup(apic_id, vector);
124236772Siwasaki
125236772Siwasaki	/* Wait up to 5 seconds for it to resume. */
126236772Siwasaki	for (ms = 0; ms < 5000; ms++) {
127236772Siwasaki		if (!CPU_ISSET(cpu, &suspended_cpus))
128236772Siwasaki			return (1);	/* return SUCCESS */
129236772Siwasaki		DELAY(1000);
130236772Siwasaki	}
131236772Siwasaki	return (0);		/* return FAILURE */
132236772Siwasaki}
133236772Siwasaki
134236772Siwasaki#define	WARMBOOT_TARGET		0
135236772Siwasaki#define	WARMBOOT_OFF		(KERNBASE + 0x0467)
136236772Siwasaki#define	WARMBOOT_SEG		(KERNBASE + 0x0469)
137236772Siwasaki
138236772Siwasaki#define	CMOS_REG		(0x70)
139236772Siwasaki#define	CMOS_DATA		(0x71)
140236772Siwasaki#define	BIOS_RESET		(0x0f)
141236772Siwasaki#define	BIOS_WARM		(0x0a)
142236772Siwasaki
143236772Siwasakistatic void
144236772Siwasakiacpi_wakeup_cpus(struct acpi_softc *sc)
145236772Siwasaki{
146236772Siwasaki	uint32_t	mpbioswarmvec;
147236772Siwasaki	int		cpu;
148236772Siwasaki	u_char		mpbiosreason;
149236772Siwasaki
150236772Siwasaki	/* save the current value of the warm-start vector */
151236772Siwasaki	mpbioswarmvec = *((uint32_t *)WARMBOOT_OFF);
152236772Siwasaki	outb(CMOS_REG, BIOS_RESET);
153236772Siwasaki	mpbiosreason = inb(CMOS_DATA);
154236772Siwasaki
155236772Siwasaki	/* setup a vector to our boot code */
156236772Siwasaki	*((volatile u_short *)WARMBOOT_OFF) = WARMBOOT_TARGET;
157236772Siwasaki	*((volatile u_short *)WARMBOOT_SEG) = WAKECODE_PADDR(sc) >> 4;
158236772Siwasaki	outb(CMOS_REG, BIOS_RESET);
159236772Siwasaki	outb(CMOS_DATA, BIOS_WARM);	/* 'warm-start' */
160236772Siwasaki
161236772Siwasaki	/* Wake up each AP. */
162236772Siwasaki	for (cpu = 1; cpu < mp_ncpus; cpu++) {
163236772Siwasaki		if (!CPU_ISSET(cpu, &suspcpus))
164236772Siwasaki			continue;
165236772Siwasaki		if (acpi_wakeup_ap(sc, cpu) == 0) {
166236772Siwasaki			/* restore the warmstart vector */
167236772Siwasaki			*(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
168236772Siwasaki			panic("acpi_wakeup: failed to resume AP #%d (PHY #%d)",
169236772Siwasaki			    cpu, cpu_apic_ids[cpu]);
170236772Siwasaki		}
171236772Siwasaki	}
172236772Siwasaki
173236772Siwasaki	/* restore the warmstart vector */
174236772Siwasaki	*(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
175236772Siwasaki
176236772Siwasaki	outb(CMOS_REG, BIOS_RESET);
177236772Siwasaki	outb(CMOS_DATA, mpbiosreason);
178236772Siwasaki}
179236772Siwasaki#endif
180236772Siwasaki
181236772Siwasakiint
182236772Siwasakiacpi_sleep_machdep(struct acpi_softc *sc, int state)
183236772Siwasaki{
184236772Siwasaki	ACPI_STATUS	status;
185236772Siwasaki
186236772Siwasaki	if (sc->acpi_wakeaddr == 0ul)
187236772Siwasaki		return (-1);	/* couldn't alloc wake memory */
188236772Siwasaki
189236772Siwasaki#ifdef SMP
190236772Siwasaki	suspcpus = all_cpus;
191236772Siwasaki	CPU_CLR(PCPU_GET(cpuid), &suspcpus);
192236772Siwasaki#endif
193236772Siwasaki
194236772Siwasaki	if (acpi_resume_beep != 0)
195236772Siwasaki		timer_spkr_acquire();
196236772Siwasaki
197236772Siwasaki	AcpiSetFirmwareWakingVector(WAKECODE_PADDR(sc));
198236772Siwasaki
199236772Siwasaki	intr_suspend();
200236772Siwasaki
201236772Siwasaki	if (savectx(susppcbs[0])) {
202236772Siwasaki#ifdef __amd64__
203236772Siwasaki		ctx_fpusave(susppcbs[0]->pcb_fpususpend);
204236772Siwasaki#endif
205236772Siwasaki#ifdef SMP
206236772Siwasaki		if (!CPU_EMPTY(&suspcpus) && suspend_cpus(suspcpus) == 0) {
207236772Siwasaki			device_printf(sc->acpi_dev, "Failed to suspend APs\n");
208236772Siwasaki			return (0);	/* couldn't sleep */
209236772Siwasaki		}
210236772Siwasaki#endif
211236772Siwasaki
212236772Siwasaki		WAKECODE_FIXUP(resume_beep, uint8_t, (acpi_resume_beep != 0));
213236772Siwasaki		WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0));
214236772Siwasaki
215237037Sjkim#ifndef __amd64__
216236772Siwasaki		WAKECODE_FIXUP(wakeup_cr4, register_t, susppcbs[0]->pcb_cr4);
217237037Sjkim#endif
218236772Siwasaki		WAKECODE_FIXUP(wakeup_pcb, struct pcb *, susppcbs[0]);
219236772Siwasaki		WAKECODE_FIXUP(wakeup_gdt, uint16_t,
220236772Siwasaki		    susppcbs[0]->pcb_gdt.rd_limit);
221236772Siwasaki		WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t,
222236772Siwasaki		    susppcbs[0]->pcb_gdt.rd_base);
223236772Siwasaki
224236772Siwasaki		/* Call ACPICA to enter the desired sleep state */
225236772Siwasaki		if (state == ACPI_STATE_S4 && sc->acpi_s4bios)
226236772Siwasaki			status = AcpiEnterSleepStateS4bios();
227236772Siwasaki		else
228239340Sjkim			status = AcpiEnterSleepState(state);
229236772Siwasaki		if (ACPI_FAILURE(status)) {
230236772Siwasaki			device_printf(sc->acpi_dev,
231236772Siwasaki			    "AcpiEnterSleepState failed - %s\n",
232236772Siwasaki			    AcpiFormatException(status));
233236772Siwasaki			return (0);	/* couldn't sleep */
234236772Siwasaki		}
235236772Siwasaki
236236772Siwasaki		for (;;)
237236772Siwasaki			ia32_pause();
238236772Siwasaki	}
239236772Siwasaki
240236772Siwasaki	return (1);	/* wakeup successfully */
241236772Siwasaki}
242236772Siwasaki
243236772Siwasakiint
244236772Siwasakiacpi_wakeup_machdep(struct acpi_softc *sc, int state, int sleep_result,
245236772Siwasaki    int intr_enabled)
246236772Siwasaki{
247236772Siwasaki
248236772Siwasaki	if (sleep_result == -1)
249236772Siwasaki		return (sleep_result);
250236772Siwasaki
251236772Siwasaki	if (!intr_enabled) {
252236772Siwasaki		/* Wakeup MD procedures in interrupt disabled context */
253236772Siwasaki		if (sleep_result == 1) {
254236772Siwasaki			pmap_init_pat();
255236772Siwasaki			initializecpu();
256236772Siwasaki			PCPU_SET(switchtime, 0);
257236772Siwasaki			PCPU_SET(switchticks, ticks);
258236772Siwasaki#ifdef SMP
259236772Siwasaki			if (!CPU_EMPTY(&suspcpus))
260236772Siwasaki				acpi_wakeup_cpus(sc);
261236772Siwasaki#endif
262236772Siwasaki		}
263236772Siwasaki
264236772Siwasaki#ifdef SMP
265236772Siwasaki		if (!CPU_EMPTY(&suspcpus))
266236772Siwasaki			restart_cpus(suspcpus);
267236772Siwasaki#endif
268236772Siwasaki		mca_resume();
269255726Sgibbs		intr_resume(/*suspend_cancelled*/false);
270236772Siwasaki
271236772Siwasaki		AcpiSetFirmwareWakingVector(0);
272236772Siwasaki	} else {
273236772Siwasaki		/* Wakeup MD procedures in interrupt enabled context */
274236772Siwasaki		if (sleep_result == 1 && mem_range_softc.mr_op != NULL &&
275236772Siwasaki		    mem_range_softc.mr_op->reinit != NULL)
276236772Siwasaki			mem_range_softc.mr_op->reinit(&mem_range_softc);
277236772Siwasaki	}
278236772Siwasaki
279236772Siwasaki	return (sleep_result);
280236772Siwasaki}
281236772Siwasaki
282236772Siwasakistatic void *
283236772Siwasakiacpi_alloc_wakeup_handler(void)
284236772Siwasaki{
285236772Siwasaki	void		*wakeaddr;
286236772Siwasaki	int		i;
287236772Siwasaki
288236772Siwasaki	/*
289236772Siwasaki	 * Specify the region for our wakeup code.  We want it in the low 1 MB
290236772Siwasaki	 * region, excluding real mode IVT (0-0x3ff), BDA (0x400-0x4ff), EBDA
291236772Siwasaki	 * (less than 128KB, below 0xa0000, must be excluded by SMAP and DSDT),
292236772Siwasaki	 * and ROM area (0xa0000 and above).  The temporary page tables must be
293236772Siwasaki	 * page-aligned.
294236772Siwasaki	 */
295236772Siwasaki	wakeaddr = contigmalloc((ACPI_PAGETABLES + 1) * PAGE_SIZE, M_DEVBUF,
296236772Siwasaki	    M_WAITOK, 0x500, 0xa0000, PAGE_SIZE, 0ul);
297236772Siwasaki	if (wakeaddr == NULL) {
298236772Siwasaki		printf("%s: can't alloc wake memory\n", __func__);
299236772Siwasaki		return (NULL);
300236772Siwasaki	}
301236772Siwasaki	if (EVENTHANDLER_REGISTER(power_resume, acpi_stop_beep, NULL,
302236772Siwasaki	    EVENTHANDLER_PRI_LAST) == NULL) {
303236772Siwasaki		printf("%s: can't register event handler\n", __func__);
304237037Sjkim		contigfree(wakeaddr, (ACPI_PAGETABLES + 1) * PAGE_SIZE,
305237037Sjkim		    M_DEVBUF);
306236772Siwasaki		return (NULL);
307236772Siwasaki	}
308236772Siwasaki	susppcbs = malloc(mp_ncpus * sizeof(*susppcbs), M_DEVBUF, M_WAITOK);
309236772Siwasaki	for (i = 0; i < mp_ncpus; i++) {
310236772Siwasaki		susppcbs[i] = malloc(sizeof(**susppcbs), M_DEVBUF, M_WAITOK);
311236772Siwasaki#ifdef __amd64__
312236772Siwasaki		susppcbs[i]->pcb_fpususpend = alloc_fpusave(M_WAITOK);
313236772Siwasaki#endif
314236772Siwasaki	}
315236772Siwasaki
316236772Siwasaki	return (wakeaddr);
317236772Siwasaki}
318236772Siwasaki
319236772Siwasakivoid
320236772Siwasakiacpi_install_wakeup_handler(struct acpi_softc *sc)
321236772Siwasaki{
322236772Siwasaki	static void	*wakeaddr = NULL;
323236772Siwasaki#ifdef __amd64__
324236772Siwasaki	uint64_t	*pt4, *pt3, *pt2;
325236772Siwasaki	int		i;
326236772Siwasaki#endif
327236772Siwasaki
328236772Siwasaki	if (wakeaddr != NULL)
329236772Siwasaki		return;
330236772Siwasaki
331236772Siwasaki	wakeaddr = acpi_alloc_wakeup_handler();
332236772Siwasaki	if (wakeaddr == NULL)
333236772Siwasaki		return;
334236772Siwasaki
335236772Siwasaki	sc->acpi_wakeaddr = (vm_offset_t)wakeaddr;
336236772Siwasaki	sc->acpi_wakephys = vtophys(wakeaddr);
337236772Siwasaki
338236772Siwasaki	bcopy(wakecode, (void *)WAKECODE_VADDR(sc), sizeof(wakecode));
339236772Siwasaki
340236772Siwasaki	/* Patch GDT base address, ljmp targets. */
341236772Siwasaki	WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t,
342236772Siwasaki	    WAKECODE_PADDR(sc) + bootgdt);
343236772Siwasaki	WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t,
344236772Siwasaki	    WAKECODE_PADDR(sc) + wakeup_32);
345236772Siwasaki#ifdef __amd64__
346236772Siwasaki	WAKECODE_FIXUP((wakeup_sw64 + 1), uint32_t,
347236772Siwasaki	    WAKECODE_PADDR(sc) + wakeup_64);
348236772Siwasaki	WAKECODE_FIXUP(wakeup_pagetables, uint32_t, sc->acpi_wakephys);
349236772Siwasaki#endif
350236772Siwasaki
351236772Siwasaki	/* Save pointers to some global data. */
352236772Siwasaki	WAKECODE_FIXUP(wakeup_ret, void *, resumectx);
353237037Sjkim#ifndef __amd64__
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
360237037Sjkim#else
361236772Siwasaki	/* Build temporary page tables below realmode code. */
362236772Siwasaki	pt4 = wakeaddr;
363236772Siwasaki	pt3 = pt4 + (PAGE_SIZE) / sizeof(uint64_t);
364236772Siwasaki	pt2 = pt3 + (PAGE_SIZE) / sizeof(uint64_t);
365236772Siwasaki
366236772Siwasaki	/* Create the initial 1GB replicated page tables */
367236772Siwasaki	for (i = 0; i < 512; i++) {
368236772Siwasaki		/*
369236772Siwasaki		 * Each slot of the level 4 pages points
370236772Siwasaki		 * to the same level 3 page
371236772Siwasaki		 */
372236772Siwasaki		pt4[i] = (uint64_t)(sc->acpi_wakephys + PAGE_SIZE);
373236772Siwasaki		pt4[i] |= PG_V | PG_RW | PG_U;
374236772Siwasaki
375236772Siwasaki		/*
376236772Siwasaki		 * Each slot of the level 3 pages points
377236772Siwasaki		 * to the same level 2 page
378236772Siwasaki		 */
379236772Siwasaki		pt3[i] = (uint64_t)(sc->acpi_wakephys + (2 * PAGE_SIZE));
380236772Siwasaki		pt3[i] |= PG_V | PG_RW | PG_U;
381236772Siwasaki
382236772Siwasaki		/* The level 2 page slots are mapped with 2MB pages for 1GB. */
383236772Siwasaki		pt2[i] = i * (2 * 1024 * 1024);
384236772Siwasaki		pt2[i] |= PG_V | PG_RW | PG_PS | PG_U;
385236772Siwasaki	}
386236772Siwasaki#endif
387236772Siwasaki
388236772Siwasaki	if (bootverbose)
389236772Siwasaki		device_printf(sc->acpi_dev, "wakeup code va %#jx pa %#jx\n",
390236772Siwasaki		    (uintmax_t)sc->acpi_wakeaddr, (uintmax_t)sc->acpi_wakephys);
391236772Siwasaki}
392