aboutsummaryrefslogtreecommitdiff
path: root/sys/amd64/acpica
diff options
context:
space:
mode:
Diffstat (limited to 'sys/amd64/acpica')
-rw-r--r--sys/amd64/acpica/Makefile33
-rw-r--r--sys/amd64/acpica/OsdEnvironment.c71
-rw-r--r--sys/amd64/acpica/acpi_machdep.c139
-rw-r--r--sys/amd64/acpica/acpi_switch.S190
-rw-r--r--sys/amd64/acpica/acpi_wakecode.S286
-rw-r--r--sys/amd64/acpica/acpi_wakeup.c420
-rwxr-xr-xsys/amd64/acpica/genwakecode.sh6
-rwxr-xr-xsys/amd64/acpica/genwakedata.sh9
-rw-r--r--sys/amd64/acpica/madt.c786
9 files changed, 1940 insertions, 0 deletions
diff --git a/sys/amd64/acpica/Makefile b/sys/amd64/acpica/Makefile
new file mode 100644
index 000000000000..743728051e97
--- /dev/null
+++ b/sys/amd64/acpica/Makefile
@@ -0,0 +1,33 @@
+# $FreeBSD$
+
+# Correct path for kernel builds
+# Don't rely on the kernel's .depend file
+.ifdef MAKESRCPATH
+.PATH: ${MAKESRCPATH}
+DEPENDFILE=
+.else
+MAKESRCPATH= ${.CURDIR}
+CLEANFILES= acpi_wakecode.h acpi_wakedata.h acpi_wakecode.bin acpi_wakecode.o
+.endif
+.if ${CC} == "icc"
+CFLAGS+= -restrict
+NOSTDINC= -X
+.else
+NOSTDINC= -nostdinc
+.endif
+CFLAGS+= ${NOSTDINC} -include opt_global.h -I. -I${MAKESRCPATH}/../..
+
+all: acpi_wakecode.h acpi_wakedata.h
+
+acpi_wakecode.o: acpi_wakecode.S assym.s
+
+acpi_wakecode.bin: acpi_wakecode.o
+ objcopy -S -O binary acpi_wakecode.o acpi_wakecode.bin
+
+acpi_wakecode.h: acpi_wakecode.bin
+ sh ${MAKESRCPATH}/genwakecode.sh > acpi_wakecode.h
+
+acpi_wakedata.h: acpi_wakecode.bin
+ sh ${MAKESRCPATH}/genwakedata.sh > acpi_wakedata.h
+
+.include <bsd.prog.mk>
diff --git a/sys/amd64/acpica/OsdEnvironment.c b/sys/amd64/acpica/OsdEnvironment.c
new file mode 100644
index 000000000000..6928f2167870
--- /dev/null
+++ b/sys/amd64/acpica/OsdEnvironment.c
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 2000,2001 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * 6.1 : Environmental support
+ */
+#include <sys/types.h>
+#include <sys/bus.h>
+#include <sys/linker_set.h>
+#include <sys/sysctl.h>
+
+#include <contrib/dev/acpica/acpi.h>
+#include <contrib/dev/acpica/actables.h>
+
+static u_long amd64_acpi_root;
+
+SYSCTL_ULONG(_machdep, OID_AUTO, acpi_root, CTLFLAG_RD, &amd64_acpi_root, 0,
+ "The physical address of the RSDP");
+
+ACPI_STATUS
+AcpiOsInitialize(void)
+{
+ return(0);
+}
+
+ACPI_STATUS
+AcpiOsTerminate(void)
+{
+ return(0);
+}
+
+ACPI_PHYSICAL_ADDRESS
+AcpiOsGetRootPointer(void)
+{
+ u_long ptr;
+
+ if (amd64_acpi_root == 0 &&
+ (resource_long_value("acpi", 0, "rsdp", (long *)&ptr) == 0 ||
+ AcpiFindRootPointer((ACPI_NATIVE_UINT *)&ptr) == AE_OK) &&
+ ptr != 0)
+ amd64_acpi_root = ptr;
+
+ return (amd64_acpi_root);
+}
diff --git a/sys/amd64/acpica/acpi_machdep.c b/sys/amd64/acpica/acpi_machdep.c
new file mode 100644
index 000000000000..c69f14ad8bba
--- /dev/null
+++ b/sys/amd64/acpica/acpi_machdep.c
@@ -0,0 +1,139 @@
+/*-
+ * Copyright (c) 2001 Mitsuru IWASAKI
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <contrib/dev/acpica/acpi.h>
+#include <dev/acpica/acpivar.h>
+
+#include <machine/nexusvar.h>
+
+SYSCTL_DECL(_debug_acpi);
+
+int acpi_resume_beep;
+TUNABLE_INT("debug.acpi.resume_beep", &acpi_resume_beep);
+SYSCTL_INT(_debug_acpi, OID_AUTO, resume_beep, CTLFLAG_RW, &acpi_resume_beep,
+ 0, "Beep the PC speaker when resuming");
+
+int acpi_reset_video;
+TUNABLE_INT("hw.acpi.reset_video", &acpi_reset_video);
+
+static int intr_model = ACPI_INTR_PIC;
+static struct apm_clone_data acpi_clone;
+
+int
+acpi_machdep_init(device_t dev)
+{
+ struct acpi_softc *sc;
+
+ sc = devclass_get_softc(devclass_find("acpi"), 0);
+
+ /* Create a fake clone for /dev/acpi. */
+ STAILQ_INIT(&sc->apm_cdevs);
+ acpi_clone.cdev = sc->acpi_dev_t;
+ acpi_clone.acpi_sc = sc;
+ ACPI_LOCK(acpi);
+ STAILQ_INSERT_TAIL(&sc->apm_cdevs, &acpi_clone, entries);
+ ACPI_UNLOCK(acpi);
+ sc->acpi_clone = &acpi_clone;
+ acpi_install_wakeup_handler(sc);
+
+ if (intr_model != ACPI_INTR_PIC)
+ acpi_SetIntrModel(intr_model);
+
+ SYSCTL_ADD_UINT(&sc->acpi_sysctl_ctx,
+ SYSCTL_CHILDREN(sc->acpi_sysctl_tree), OID_AUTO,
+ "reset_video", CTLFLAG_RW, &acpi_reset_video, 0,
+ "Call the VESA reset BIOS vector on the resume path");
+
+ return (0);
+}
+
+void
+acpi_SetDefaultIntrModel(int model)
+{
+
+ intr_model = model;
+}
+
+int
+acpi_machdep_quirks(int *quirks)
+{
+ return (0);
+}
+
+void
+acpi_cpu_c1()
+{
+ __asm __volatile("sti; hlt");
+}
+
+/*
+ * ACPI nexus(4) driver.
+ */
+static int
+nexus_acpi_probe(device_t dev)
+{
+ int error;
+
+ error = acpi_identify();
+ if (error)
+ return (error);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+nexus_acpi_attach(device_t dev)
+{
+
+ nexus_init_resources();
+ bus_generic_probe(dev);
+ if (BUS_ADD_CHILD(dev, 10, "acpi", 0) == NULL)
+ panic("failed to add acpi0 device");
+
+ return (bus_generic_attach(dev));
+}
+
+static device_method_t nexus_acpi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, nexus_acpi_probe),
+ DEVMETHOD(device_attach, nexus_acpi_attach),
+
+ { 0, 0 }
+};
+
+DEFINE_CLASS_1(nexus, nexus_acpi_driver, nexus_acpi_methods, 1, nexus_driver);
+static devclass_t nexus_devclass;
+
+DRIVER_MODULE(nexus_acpi, root, nexus_acpi_driver, nexus_devclass, 0, 0);
diff --git a/sys/amd64/acpica/acpi_switch.S b/sys/amd64/acpica/acpi_switch.S
new file mode 100644
index 000000000000..892dd11c47a5
--- /dev/null
+++ b/sys/amd64/acpica/acpi_switch.S
@@ -0,0 +1,190 @@
+/*-
+ * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
+ * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2008-2009 Jung-uk Kim <jkim@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <machine/asmacros.h>
+#include <machine/specialreg.h>
+
+#include "acpi_wakedata.h"
+#include "assym.s"
+
+#define WAKEUP_DECL(member) \
+ .set WAKEUP_ ## member, wakeup_ ## member - wakeup_ctx
+
+ WAKEUP_DECL(xpcb)
+ WAKEUP_DECL(gdt)
+ WAKEUP_DECL(efer)
+ WAKEUP_DECL(pat)
+ WAKEUP_DECL(star)
+ WAKEUP_DECL(lstar)
+ WAKEUP_DECL(cstar)
+ WAKEUP_DECL(sfmask)
+ WAKEUP_DECL(cpu)
+
+#define WAKEUP_CTX(member) WAKEUP_ ## member (%rdi)
+#define WAKEUP_PCB(member) PCB_ ## member(%r11)
+#define WAKEUP_XPCB(member) XPCB_ ## member(%r11)
+
+ENTRY(acpi_restorecpu)
+ /* Switch to KPML4phys. */
+ movq %rsi, %rax
+ movq %rax, %cr3
+
+ /* Restore GDT. */
+ lgdt WAKEUP_CTX(gdt)
+ jmp 1f
+1:
+
+ /* Fetch PCB. */
+ movq WAKEUP_CTX(xpcb), %r11
+
+ /* Force kernel segment registers. */
+ movl $KDSEL, %eax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %ss
+ movl $KUF32SEL, %eax
+ movw %ax, %fs
+ movl $KUG32SEL, %eax
+ movw %ax, %gs
+
+ movl $MSR_FSBASE, %ecx
+ movl WAKEUP_PCB(FSBASE), %eax
+ movl 4 + WAKEUP_PCB(FSBASE), %edx
+ wrmsr
+ movl $MSR_GSBASE, %ecx
+ movl WAKEUP_PCB(GSBASE), %eax
+ movl 4 + WAKEUP_PCB(GSBASE), %edx
+ wrmsr
+ movl $MSR_KGSBASE, %ecx
+ movl WAKEUP_XPCB(KGSBASE), %eax
+ movl 4 + WAKEUP_XPCB(KGSBASE), %edx
+ wrmsr
+
+ /* Restore EFER. */
+ movl $MSR_EFER, %ecx
+ movl WAKEUP_CTX(efer), %eax
+ wrmsr
+
+ /* Restore PAT. */
+ movl $MSR_PAT, %ecx
+ movl WAKEUP_CTX(pat), %eax
+ movl 4 + WAKEUP_CTX(pat), %edx
+ wrmsr
+
+ /* Restore fast syscall stuff. */
+ movl $MSR_STAR, %ecx
+ movl WAKEUP_CTX(star), %eax
+ movl 4 + WAKEUP_CTX(star), %edx
+ wrmsr
+ movl $MSR_LSTAR, %ecx
+ movl WAKEUP_CTX(lstar), %eax
+ movl 4 + WAKEUP_CTX(lstar), %edx
+ wrmsr
+ movl $MSR_CSTAR, %ecx
+ movl WAKEUP_CTX(cstar), %eax
+ movl 4 + WAKEUP_CTX(cstar), %edx
+ wrmsr
+ movl $MSR_SF_MASK, %ecx
+ movl WAKEUP_CTX(sfmask), %eax
+ wrmsr
+
+ /* Restore CR0, CR2 and CR4. */
+ movq WAKEUP_XPCB(CR0), %rax
+ movq %rax, %cr0
+ movq WAKEUP_XPCB(CR2), %rax
+ movq %rax, %cr2
+ movq WAKEUP_XPCB(CR4), %rax
+ movq %rax, %cr4
+
+ /* Restore descriptor tables. */
+ lidt WAKEUP_XPCB(IDT)
+ lldt WAKEUP_XPCB(LDT)
+
+#define SDT_SYSTSS 9
+#define SDT_SYSBSY 11
+
+ /* Clear "task busy" bit and reload TR. */
+ movq PCPU(TSS), %rax
+ andb $(~SDT_SYSBSY | SDT_SYSTSS), 5(%rax)
+ movw WAKEUP_XPCB(TR), %ax
+ ltr %ax
+
+#undef SDT_SYSTSS
+#undef SDT_SYSBSY
+
+ /* Restore other callee saved registers. */
+ movq WAKEUP_PCB(R15), %r15
+ movq WAKEUP_PCB(R14), %r14
+ movq WAKEUP_PCB(R13), %r13
+ movq WAKEUP_PCB(R12), %r12
+ movq WAKEUP_PCB(RBP), %rbp
+ movq WAKEUP_PCB(RSP), %rsp
+ movq WAKEUP_PCB(RBX), %rbx
+
+ /* Restore debug registers. */
+ movq WAKEUP_PCB(DR0), %rax
+ movq %rax, %dr0
+ movq WAKEUP_PCB(DR1), %rax
+ movq %rax, %dr1
+ movq WAKEUP_PCB(DR2), %rax
+ movq %rax, %dr2
+ movq WAKEUP_PCB(DR3), %rax
+ movq %rax, %dr3
+ movq WAKEUP_PCB(DR6), %rax
+ movq %rax, %dr6
+ movq WAKEUP_PCB(DR7), %rax
+ movq %rax, %dr7
+
+ /* Restore return address. */
+ movq WAKEUP_PCB(RIP), %rax
+ movq %rax, (%rsp)
+
+ /* Indicate the CPU is resumed. */
+ xorl %eax, %eax
+ movl %eax, WAKEUP_CTX(cpu)
+
+ ret
+END(acpi_restorecpu)
+
+ENTRY(acpi_savecpu)
+ /* Fetch XPCB and save CPU context. */
+ movq %rdi, %r10
+ call savectx2
+ movq %r10, %r11
+
+ /* Patch caller's return address and stack pointer. */
+ movq (%rsp), %rax
+ movq %rax, WAKEUP_PCB(RIP)
+ movq %rsp, %rax
+ movq %rax, WAKEUP_PCB(RSP)
+
+ movl $1, %eax
+ ret
+END(acpi_savecpu)
diff --git a/sys/amd64/acpica/acpi_wakecode.S b/sys/amd64/acpica/acpi_wakecode.S
new file mode 100644
index 000000000000..4e82f53196af
--- /dev/null
+++ b/sys/amd64/acpica/acpi_wakecode.S
@@ -0,0 +1,286 @@
+/*-
+ * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
+ * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2003 Peter Wemm
+ * Copyright (c) 2008-2009 Jung-uk Kim <jkim@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define LOCORE
+
+#include <machine/asmacros.h>
+#include <machine/specialreg.h>
+
+#include "assym.s"
+
+/*
+ * Resume entry point for real mode.
+ *
+ * If XFirmwareWakingVector is zero and FirmwareWakingVector is non-zero
+ * in FACS, the BIOS enters here in real mode after POST with CS set to
+ * (FirmwareWakingVector >> 4) and IP set to (FirmwareWakingVector & 0xf).
+ * Depending on the previous sleep state, we may need to initialize more
+ * of the system (i.e., S3 suspend-to-RAM vs. S4 suspend-to-disk).
+ *
+ * Note: If XFirmwareWakingVector is non-zero, it should disable address
+ * translation/paging and interrupts, load all segment registers with
+ * a flat 4 GB address space, and set EFLAGS.IF to zero. Currently
+ * this mode is not supported by this code.
+ */
+
+ .data /* So we can modify it */
+
+ ALIGN_TEXT
+wakeup_start:
+ .code16
+ /*
+ * Set up segment registers for real mode, a small stack for
+ * any calls we make, and clear any flags.
+ */
+ cli /* make sure no interrupts */
+ cld
+ mov %cs, %ax /* copy %cs to %ds. Remember these */
+ mov %ax, %ds /* are offsets rather than selectors */
+ mov %ax, %ss
+ movw $PAGE_SIZE - 8, %sp
+ xorw %ax, %ax
+ pushw %ax
+ popfw
+
+ /* To debug resume hangs, beep the speaker if the user requested. */
+ testb $~0, resume_beep - wakeup_start
+ jz 1f
+ movb $0, resume_beep - wakeup_start
+ movb $0xc0, %al
+ outb %al, $0x42
+ movb $0x04, %al
+ outb %al, $0x42
+ inb $0x61, %al
+ orb $0x3, %al
+ outb %al, $0x61
+1:
+
+ /* Re-initialize video BIOS if the reset_video tunable is set. */
+ testb $~0, reset_video - wakeup_start
+ jz 1f
+ movb $0, reset_video - wakeup_start
+ lcall $0xc000, $3
+
+ /* Re-start in case the previous BIOS call clobbers them. */
+ jmp wakeup_start
+1:
+
+ /*
+ * Find relocation base and patch the gdt descript and ljmp targets
+ */
+ xorl %ebx, %ebx
+ mov %cs, %bx
+ sall $4, %ebx /* %ebx is now our relocation base */
+
+ /*
+ * Load the descriptor table pointer. We'll need it when running
+ * in 16-bit protected mode.
+ */
+ lgdtl bootgdtdesc - wakeup_start
+
+ /* Enable protected mode */
+ movl $CR0_PE, %eax
+ mov %eax, %cr0
+
+ /*
+ * Now execute a far jump to turn on protected mode. This
+ * causes the segment registers to turn into selectors and causes
+ * %cs to be loaded from the gdt.
+ *
+ * The following instruction is:
+ * ljmpl $bootcode32 - bootgdt, $wakeup_32 - wakeup_start
+ * but gas cannot assemble that. And besides, we patch the targets
+ * in early startup and its a little clearer what we are patching.
+ */
+wakeup_sw32:
+ .byte 0x66 /* size override to 32 bits */
+ .byte 0xea /* opcode for far jump */
+ .long wakeup_32 - wakeup_start /* offset in segment */
+ .word bootcode32 - bootgdt /* index in gdt for 32 bit code */
+
+ /*
+ * At this point, we are running in 32 bit legacy protected mode.
+ */
+ .code32
+wakeup_32:
+
+ mov $bootdata32 - bootgdt, %eax
+ mov %ax, %ds
+
+ /* Turn on the PAE and PSE bits for when paging is enabled */
+ mov %cr4, %eax
+ orl $(CR4_PAE | CR4_PSE), %eax
+ mov %eax, %cr4
+
+ /*
+ * Enable EFER.LME so that we get long mode when all the prereqs are
+ * in place. In this case, it turns on when CR0_PG is finally enabled.
+ * Pick up a few other EFER bits that we'll use need we're here.
+ */
+ movl $MSR_EFER, %ecx
+ rdmsr
+ orl $EFER_LME | EFER_SCE, %eax
+ wrmsr
+
+ /*
+ * Point to the embedded page tables for startup. Note that this
+ * only gets accessed after we're actually in 64 bit mode, however
+ * we can only set the bottom 32 bits of %cr3 in this state. This
+ * means we are required to use a temporary page table that is below
+ * the 4GB limit. %ebx is still our relocation base. We could just
+ * subtract 3 * PAGE_SIZE, but that would be too easy.
+ */
+ leal wakeup_pagetables - wakeup_start(%ebx), %eax
+ movl (%eax), %eax
+ mov %eax, %cr3
+
+ /*
+ * Finally, switch to long bit mode by enabling paging. We have
+ * to be very careful here because all the segmentation disappears
+ * out from underneath us. The spec says we can depend on the
+ * subsequent pipelined branch to execute, but *only if* everthing
+ * is still identity mapped. If any mappings change, the pipeline
+ * will flush.
+ */
+ mov %cr0, %eax
+ orl $CR0_PG, %eax
+ mov %eax, %cr0
+
+ /*
+ * At this point paging is enabled, and we are in "compatability" mode.
+ * We do another far jump to reload %cs with the 64 bit selector.
+ * %cr3 points to a 4-level page table page.
+ * We cannot yet jump all the way to the kernel because we can only
+ * specify a 32 bit linear address. So, yet another trampoline.
+ *
+ * The following instruction is:
+ * ljmp $bootcode64 - bootgdt, $wakeup_64 - wakeup_start
+ * but gas cannot assemble that. And besides, we patch the targets
+ * in early startup and its a little clearer what we are patching.
+ */
+wakeup_sw64:
+ .byte 0xea /* opcode for far jump */
+ .long wakeup_64 - wakeup_start /* offset in segment */
+ .word bootcode64 - bootgdt /* index in gdt for 64 bit code */
+
+ /*
+ * Yeehar! We're running in 64-bit mode! We can mostly ignore our
+ * segment registers, and get on with it.
+ * Note that we are running at the correct virtual address, but with
+ * a 1:1 1GB mirrored mapping over entire address space. We had better
+ * switch to a real %cr3 promptly so that we can get to the direct map
+ * space. Remember that jmp is relative and that we've been relocated,
+ * so use an indirect jump.
+ */
+ ALIGN_TEXT
+ .code64
+wakeup_64:
+ mov $bootdata64 - bootgdt, %eax
+ mov %ax, %ds
+
+ /* Restore arguments and return. */
+ movq wakeup_ctx - wakeup_start(%rbx), %rdi
+ movq wakeup_kpml4 - wakeup_start(%rbx), %rsi
+ movq wakeup_retaddr - wakeup_start(%rbx), %rax
+ jmp *%rax
+
+ .data
+
+resume_beep:
+ .byte 0
+reset_video:
+ .byte 0
+
+ ALIGN_DATA
+bootgdt:
+ .long 0x00000000
+ .long 0x00000000
+ .long 0x00000000
+ .long 0x00000000
+ .long 0x00000000
+ .long 0x00000000
+ .long 0x00000000
+ .long 0x00000000
+
+bootcode64:
+ .long 0x0000ffff
+ .long 0x00af9b00
+
+bootdata64:
+ .long 0x0000ffff
+ .long 0x00af9300
+
+bootcode32:
+ .long 0x0000ffff
+ .long 0x00cf9b00
+
+bootdata32:
+ .long 0x0000ffff
+ .long 0x00cf9300
+bootgdtend:
+
+wakeup_pagetables:
+ .long 0
+
+bootgdtdesc:
+ .word bootgdtend - bootgdt /* Length */
+ .long bootgdt - wakeup_start /* Offset plus %ds << 4 */
+
+ ALIGN_DATA
+wakeup_retaddr:
+ .quad 0
+wakeup_kpml4:
+ .quad 0
+
+wakeup_ctx:
+ .quad 0
+wakeup_xpcb:
+ .quad 0
+wakeup_gdt:
+ .word 0
+ .quad 0
+
+ ALIGN_DATA
+wakeup_efer:
+ .quad 0
+wakeup_pat:
+ .quad 0
+wakeup_star:
+ .quad 0
+wakeup_lstar:
+ .quad 0
+wakeup_cstar:
+ .quad 0
+wakeup_sfmask:
+ .quad 0
+wakeup_cpu:
+ .long 0
+dummy:
diff --git a/sys/amd64/acpica/acpi_wakeup.c b/sys/amd64/acpica/acpi_wakeup.c
new file mode 100644
index 000000000000..2f9d8a01b295
--- /dev/null
+++ b/sys/amd64/acpica/acpi_wakeup.c
@@ -0,0 +1,420 @@
+/*-
+ * Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
+ * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2003 Peter Wemm
+ * Copyright (c) 2008-2009 Jung-uk Kim <jkim@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/memrange.h>
+#include <sys/smp.h>
+#include <sys/types.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/intr_machdep.h>
+#include <machine/pcb.h>
+#include <machine/pmap.h>
+#include <machine/specialreg.h>
+#include <machine/vmparam.h>
+
+#ifdef SMP
+#include <machine/apicreg.h>
+#include <machine/smp.h>
+#endif
+
+#include <contrib/dev/acpica/acpi.h>
+#include <dev/acpica/acpivar.h>
+
+#include "acpi_wakecode.h"
+#include "acpi_wakedata.h"
+
+/* Make sure the code is less than a page and leave room for the stack. */
+CTASSERT(sizeof(wakecode) < PAGE_SIZE - 1024);
+
+#ifndef _SYS_CDEFS_H_
+#error this file needs sys/cdefs.h as a prerequisite
+#endif
+
+extern int acpi_resume_beep;
+extern int acpi_reset_video;
+
+#ifdef SMP
+extern struct xpcb *stopxpcbs;
+#else
+static struct xpcb *stopxpcbs;
+#endif
+
+int acpi_restorecpu(struct xpcb *, vm_offset_t);
+int acpi_savecpu(struct xpcb *);
+
+static void acpi_alloc_wakeup_handler(void);
+static void acpi_stop_beep(void *);
+
+#ifdef SMP
+static int acpi_wakeup_ap(struct acpi_softc *, int);
+static void acpi_wakeup_cpus(struct acpi_softc *, cpumask_t);
+#endif
+
+#define WAKECODE_VADDR(sc) ((sc)->acpi_wakeaddr + (3 * PAGE_SIZE))
+#define WAKECODE_PADDR(sc) ((sc)->acpi_wakephys + (3 * PAGE_SIZE))
+#define WAKECODE_FIXUP(offset, type, val) do { \
+ type *addr; \
+ addr = (type *)(WAKECODE_VADDR(sc) + offset); \
+ *addr = val; \
+} while (0)
+
+/* Turn off bits 1&2 of the PIT, stopping the beep. */
+static void
+acpi_stop_beep(void *arg)
+{
+ outb(0x61, inb(0x61) & ~0x3);
+}
+
+#ifdef SMP
+static int
+acpi_wakeup_ap(struct acpi_softc *sc, int cpu)
+{
+ int vector = (WAKECODE_PADDR(sc) >> 12) & 0xff;
+ int apic_id = cpu_apic_ids[cpu];
+ int ms;
+
+ WAKECODE_FIXUP(wakeup_xpcb, struct xpcb *, &stopxpcbs[cpu]);
+ WAKECODE_FIXUP(wakeup_gdt, uint16_t, stopxpcbs[cpu].xpcb_gdt.rd_limit);
+ WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t,
+ stopxpcbs[cpu].xpcb_gdt.rd_base);
+ WAKECODE_FIXUP(wakeup_cpu, int, cpu);
+
+ /* do an INIT IPI: assert RESET */
+ lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
+ APIC_LEVEL_ASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, apic_id);
+
+ /* wait for pending status end */
+ lapic_ipi_wait(-1);
+
+ /* do an INIT IPI: deassert RESET */
+ lapic_ipi_raw(APIC_DEST_ALLESELF | APIC_TRIGMOD_LEVEL |
+ APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_INIT, 0);
+
+ /* wait for pending status end */
+ DELAY(10000); /* wait ~10mS */
+ lapic_ipi_wait(-1);
+
+ /*
+ * next we do a STARTUP IPI: the previous INIT IPI might still be
+ * latched, (P5 bug) this 1st STARTUP would then terminate
+ * immediately, and the previously started INIT IPI would continue. OR
+ * the previous INIT IPI has already run. and this STARTUP IPI will
+ * run. OR the previous INIT IPI was ignored. and this STARTUP IPI
+ * will run.
+ */
+
+ /* do a STARTUP IPI */
+ lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
+ APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP |
+ vector, apic_id);
+ lapic_ipi_wait(-1);
+ DELAY(200); /* wait ~200uS */
+
+ /*
+ * finally we do a 2nd STARTUP IPI: this 2nd STARTUP IPI should run IF
+ * the previous STARTUP IPI was cancelled by a latched INIT IPI. OR
+ * this STARTUP IPI will be ignored, as only ONE STARTUP IPI is
+ * recognized after hardware RESET or INIT IPI.
+ */
+
+ lapic_ipi_raw(APIC_DEST_DESTFLD | APIC_TRIGMOD_EDGE |
+ APIC_LEVEL_DEASSERT | APIC_DESTMODE_PHY | APIC_DELMODE_STARTUP |
+ vector, apic_id);
+ lapic_ipi_wait(-1);
+ DELAY(200); /* wait ~200uS */
+
+ /* Wait up to 5 seconds for it to start. */
+ for (ms = 0; ms < 5000; ms++) {
+ if (*(int *)(WAKECODE_VADDR(sc) + wakeup_cpu) == 0)
+ return (1); /* return SUCCESS */
+ DELAY(1000);
+ }
+ return (0); /* return FAILURE */
+}
+
+#define WARMBOOT_TARGET 0
+#define WARMBOOT_OFF (KERNBASE + 0x0467)
+#define WARMBOOT_SEG (KERNBASE + 0x0469)
+
+#define CMOS_REG (0x70)
+#define CMOS_DATA (0x71)
+#define BIOS_RESET (0x0f)
+#define BIOS_WARM (0x0a)
+
+static void
+acpi_wakeup_cpus(struct acpi_softc *sc, cpumask_t wakeup_cpus)
+{
+ uint32_t mpbioswarmvec;
+ cpumask_t map;
+ int cpu;
+ u_char mpbiosreason;
+
+ /* save the current value of the warm-start vector */
+ mpbioswarmvec = *((uint32_t *)WARMBOOT_OFF);
+ outb(CMOS_REG, BIOS_RESET);
+ mpbiosreason = inb(CMOS_DATA);
+
+ /* setup a vector to our boot code */
+ *((volatile u_short *)WARMBOOT_OFF) = WARMBOOT_TARGET;
+ *((volatile u_short *)WARMBOOT_SEG) = WAKECODE_PADDR(sc) >> 4;
+ outb(CMOS_REG, BIOS_RESET);
+ outb(CMOS_DATA, BIOS_WARM); /* 'warm-start' */
+
+ /* Wake up each AP. */
+ for (cpu = 1; cpu < mp_ncpus; cpu++) {
+ map = 1ul << cpu;
+ if ((wakeup_cpus & map) != map)
+ continue;
+ if (acpi_wakeup_ap(sc, cpu) == 0) {
+ /* restore the warmstart vector */
+ *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
+ panic("acpi_wakeup: failed to resume AP #%d (PHY #%d)",
+ cpu, cpu_apic_ids[cpu]);
+ }
+ }
+
+ /* restore the warmstart vector */
+ *(uint32_t *)WARMBOOT_OFF = mpbioswarmvec;
+
+ outb(CMOS_REG, BIOS_RESET);
+ outb(CMOS_DATA, mpbiosreason);
+}
+#endif
+
+int
+acpi_sleep_machdep(struct acpi_softc *sc, int state)
+{
+ struct savefpu *stopfpu;
+#ifdef SMP
+ cpumask_t wakeup_cpus;
+#endif
+ register_t cr3, rf;
+ ACPI_STATUS status;
+ int ret;
+
+ ret = -1;
+
+ if (sc->acpi_wakeaddr == 0ul)
+ return (ret);
+
+#ifdef SMP
+ wakeup_cpus = PCPU_GET(other_cpus);
+#endif
+
+ AcpiSetFirmwareWakingVector(WAKECODE_PADDR(sc));
+
+ rf = intr_disable();
+ intr_suspend();
+
+ /*
+ * Temporarily switch to the kernel pmap because it provides
+ * an identity mapping (setup at boot) for the low physical
+ * memory region containing the wakeup code.
+ */
+ cr3 = rcr3();
+ load_cr3(KPML4phys);
+
+ stopfpu = &stopxpcbs[0].xpcb_pcb.pcb_save;
+ if (acpi_savecpu(&stopxpcbs[0])) {
+ fpugetregs(curthread, stopfpu);
+
+#ifdef SMP
+ if (wakeup_cpus != 0 && suspend_cpus(wakeup_cpus) == 0) {
+ device_printf(sc->acpi_dev,
+ "Failed to suspend APs: CPU mask = 0x%jx\n",
+ (uintmax_t)(wakeup_cpus & ~stopped_cpus));
+ goto out;
+ }
+#endif
+
+ WAKECODE_FIXUP(resume_beep, uint8_t, (acpi_resume_beep != 0));
+ WAKECODE_FIXUP(reset_video, uint8_t, (acpi_reset_video != 0));
+
+ WAKECODE_FIXUP(wakeup_xpcb, struct xpcb *, &stopxpcbs[0]);
+ WAKECODE_FIXUP(wakeup_gdt, uint16_t,
+ stopxpcbs[0].xpcb_gdt.rd_limit);
+ WAKECODE_FIXUP(wakeup_gdt + 2, uint64_t,
+ stopxpcbs[0].xpcb_gdt.rd_base);
+ WAKECODE_FIXUP(wakeup_cpu, int, 0);
+
+ /* Call ACPICA to enter the desired sleep state */
+ if (state == ACPI_STATE_S4 && sc->acpi_s4bios)
+ status = AcpiEnterSleepStateS4bios();
+ else
+ status = AcpiEnterSleepState(state);
+
+ if (status != AE_OK) {
+ device_printf(sc->acpi_dev,
+ "AcpiEnterSleepState failed - %s\n",
+ AcpiFormatException(status));
+ goto out;
+ }
+
+ for (;;)
+ ia32_pause();
+ } else {
+ fpusetregs(curthread, stopfpu);
+#ifdef SMP
+ if (wakeup_cpus != 0)
+ acpi_wakeup_cpus(sc, wakeup_cpus);
+#endif
+ acpi_resync_clock(sc);
+ ret = 0;
+ }
+
+out:
+#ifdef SMP
+ if (wakeup_cpus != 0)
+ restart_cpus(wakeup_cpus);
+#endif
+
+ load_cr3(cr3);
+ intr_resume();
+ intr_restore(rf);
+
+ AcpiSetFirmwareWakingVector(0);
+
+ if (ret == 0 && mem_range_softc.mr_op != NULL &&
+ mem_range_softc.mr_op->reinit != NULL)
+ mem_range_softc.mr_op->reinit(&mem_range_softc);
+
+ /* If we beeped, turn it off after a delay. */
+ if (acpi_resume_beep)
+ timeout(acpi_stop_beep, NULL, 3 * hz);
+
+ return (ret);
+}
+
+static vm_offset_t acpi_wakeaddr;
+
+static void
+acpi_alloc_wakeup_handler(void)
+{
+ void *wakeaddr;
+
+ if (!cold)
+ return;
+
+ /*
+ * Specify the region for our wakeup code. We want it in the low 1 MB
+ * region, excluding video memory and above (0xa0000). We ask for
+ * it to be page-aligned, just to be safe.
+ */
+ wakeaddr = contigmalloc(4 * PAGE_SIZE, M_DEVBUF, M_NOWAIT, 0, 0x9ffff,
+ PAGE_SIZE, 0ul);
+ if (wakeaddr == NULL) {
+ printf("%s: can't alloc wake memory\n", __func__);
+ return;
+ }
+ stopxpcbs = malloc(mp_ncpus * sizeof(*stopxpcbs), M_DEVBUF, M_NOWAIT);
+ if (stopxpcbs == NULL) {
+ contigfree(wakeaddr, 4 * PAGE_SIZE, M_DEVBUF);
+ printf("%s: can't alloc CPU state memory\n", __func__);
+ return;
+ }
+ acpi_wakeaddr = (vm_offset_t)wakeaddr;
+}
+
+SYSINIT(acpiwakeup, SI_SUB_KMEM, SI_ORDER_ANY, acpi_alloc_wakeup_handler, 0);
+
+void
+acpi_install_wakeup_handler(struct acpi_softc *sc)
+{
+ uint64_t *pt4, *pt3, *pt2;
+ int i;
+
+ if (acpi_wakeaddr == 0ul)
+ return;
+
+ sc->acpi_wakeaddr = acpi_wakeaddr;
+ sc->acpi_wakephys = vtophys(acpi_wakeaddr);
+
+ bcopy(wakecode, (void *)WAKECODE_VADDR(sc), sizeof(wakecode));
+
+ /* Patch GDT base address, ljmp targets and page table base address. */
+ WAKECODE_FIXUP((bootgdtdesc + 2), uint32_t,
+ WAKECODE_PADDR(sc) + bootgdt);
+ WAKECODE_FIXUP((wakeup_sw32 + 2), uint32_t,
+ WAKECODE_PADDR(sc) + wakeup_32);
+ WAKECODE_FIXUP((wakeup_sw64 + 1), uint32_t,
+ WAKECODE_PADDR(sc) + wakeup_64);
+ WAKECODE_FIXUP(wakeup_pagetables, uint32_t, sc->acpi_wakephys);
+
+ /* Save pointers to some global data. */
+ WAKECODE_FIXUP(wakeup_retaddr, void *, acpi_restorecpu);
+ WAKECODE_FIXUP(wakeup_kpml4, uint64_t, KPML4phys);
+ WAKECODE_FIXUP(wakeup_ctx, vm_offset_t,
+ WAKECODE_VADDR(sc) + wakeup_ctx);
+ WAKECODE_FIXUP(wakeup_efer, uint64_t, rdmsr(MSR_EFER));
+ WAKECODE_FIXUP(wakeup_pat, uint64_t, rdmsr(MSR_PAT));
+ WAKECODE_FIXUP(wakeup_star, uint64_t, rdmsr(MSR_STAR));
+ WAKECODE_FIXUP(wakeup_lstar, uint64_t, rdmsr(MSR_LSTAR));
+ WAKECODE_FIXUP(wakeup_cstar, uint64_t, rdmsr(MSR_CSTAR));
+ WAKECODE_FIXUP(wakeup_sfmask, uint64_t, rdmsr(MSR_SF_MASK));
+
+ /* Build temporary page tables below realmode code. */
+ pt4 = (uint64_t *)acpi_wakeaddr;
+ pt3 = pt4 + (PAGE_SIZE) / sizeof(uint64_t);
+ pt2 = pt3 + (PAGE_SIZE) / sizeof(uint64_t);
+
+ /* Create the initial 1GB replicated page tables */
+ for (i = 0; i < 512; i++) {
+ /*
+ * Each slot of the level 4 pages points
+ * to the same level 3 page
+ */
+ pt4[i] = (uint64_t)(sc->acpi_wakephys + PAGE_SIZE);
+ pt4[i] |= PG_V | PG_RW | PG_U;
+
+ /*
+ * Each slot of the level 3 pages points
+ * to the same level 2 page
+ */
+ pt3[i] = (uint64_t)(sc->acpi_wakephys + (2 * PAGE_SIZE));
+ pt3[i] |= PG_V | PG_RW | PG_U;
+
+ /* The level 2 page slots are mapped with 2MB pages for 1GB. */
+ pt2[i] = i * (2 * 1024 * 1024);
+ pt2[i] |= PG_V | PG_RW | PG_PS | PG_U;
+ }
+
+ if (bootverbose)
+ device_printf(sc->acpi_dev, "wakeup code va %p pa %p\n",
+ (void *)sc->acpi_wakeaddr, (void *)sc->acpi_wakephys);
+}
diff --git a/sys/amd64/acpica/genwakecode.sh b/sys/amd64/acpica/genwakecode.sh
new file mode 100755
index 000000000000..c9d0077de56a
--- /dev/null
+++ b/sys/amd64/acpica/genwakecode.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+# $FreeBSD$
+#
+file2c -sx 'static char wakecode[] = {' '};' <acpi_wakecode.bin
+
+exit 0
diff --git a/sys/amd64/acpica/genwakedata.sh b/sys/amd64/acpica/genwakedata.sh
new file mode 100755
index 000000000000..6d4181ecacaf
--- /dev/null
+++ b/sys/amd64/acpica/genwakedata.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+# $FreeBSD$
+#
+nm -n --defined-only acpi_wakecode.o | while read offset dummy what
+do
+ echo "#define ${what} 0x${offset}"
+done
+
+exit 0
diff --git a/sys/amd64/acpica/madt.c b/sys/amd64/acpica/madt.c
new file mode 100644
index 000000000000..a8df55d20190
--- /dev/null
+++ b/sys/amd64/acpica/madt.c
@@ -0,0 +1,786 @@
+/*-
+ * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#include <machine/apicreg.h>
+#include <machine/frame.h>
+#include <machine/intr_machdep.h>
+#include <machine/apicvar.h>
+#include <machine/md_var.h>
+#include <machine/specialreg.h>
+
+#include <contrib/dev/acpica/acpi.h>
+#include <contrib/dev/acpica/actables.h>
+#include <dev/acpica/acpivar.h>
+#include <dev/pci/pcivar.h>
+
+typedef void madt_entry_handler(ACPI_SUBTABLE_HEADER *entry, void *arg);
+
+/* These two arrays are indexed by APIC IDs. */
+struct ioapic_info {
+ void *io_apic;
+ UINT32 io_vector;
+} ioapics[MAX_APIC_ID + 1];
+
+struct lapic_info {
+ u_int la_enabled:1;
+ u_int la_acpi_id:8;
+} lapics[MAX_APIC_ID + 1];
+
+static int madt_found_sci_override;
+static ACPI_TABLE_MADT *madt;
+static vm_paddr_t madt_physaddr;
+static vm_offset_t madt_length;
+
+MALLOC_DEFINE(M_MADT, "madt_table", "ACPI MADT Table Items");
+
+static enum intr_polarity interrupt_polarity(UINT16 IntiFlags, UINT8 Source);
+static enum intr_trigger interrupt_trigger(UINT16 IntiFlags, UINT8 Source);
+static int madt_find_cpu(u_int acpi_id, u_int *apic_id);
+static int madt_find_interrupt(int intr, void **apic, u_int *pin);
+static void *madt_map(vm_paddr_t pa, int offset, vm_offset_t length);
+static void *madt_map_table(vm_paddr_t pa, int offset, const char *sig);
+static void madt_parse_apics(ACPI_SUBTABLE_HEADER *entry, void *arg);
+static void madt_parse_interrupt_override(
+ ACPI_MADT_INTERRUPT_OVERRIDE *intr);
+static void madt_parse_ints(ACPI_SUBTABLE_HEADER *entry,
+ void *arg __unused);
+static void madt_parse_local_nmi(ACPI_MADT_LOCAL_APIC_NMI *nmi);
+static void madt_parse_nmi(ACPI_MADT_NMI_SOURCE *nmi);
+static int madt_probe(void);
+static int madt_probe_cpus(void);
+static void madt_probe_cpus_handler(ACPI_SUBTABLE_HEADER *entry,
+ void *arg __unused);
+static int madt_probe_table(vm_paddr_t address);
+static void madt_register(void *dummy);
+static int madt_setup_local(void);
+static int madt_setup_io(void);
+static void madt_unmap(void *data, vm_offset_t length);
+static void madt_unmap_table(void *table);
+static void madt_walk_table(madt_entry_handler *handler, void *arg);
+
+static struct apic_enumerator madt_enumerator = {
+ "MADT",
+ madt_probe,
+ madt_probe_cpus,
+ madt_setup_local,
+ madt_setup_io
+};
+
+/*
+ * Code to abuse the crashdump map to map in the tables for the early
+ * probe. We cheat and make the following assumptions about how we
+ * use this KVA: pages 0 and 1 are used to map in the header of each
+ * table found via the RSDT or XSDT and pages 2 to n are used to map
+ * in the RSDT or XSDT. We have to use 2 pages for the table headers
+ * in case a header spans a page boundary. The offset is in pages;
+ * the length is in bytes.
+ */
+static void *
+madt_map(vm_paddr_t pa, int offset, vm_offset_t length)
+{
+ vm_offset_t va, off;
+ void *data;
+
+ off = pa & PAGE_MASK;
+ length = roundup(length + off, PAGE_SIZE);
+ pa = pa & PG_FRAME;
+ va = (vm_offset_t)pmap_kenter_temporary(pa, offset) +
+ (offset * PAGE_SIZE);
+ data = (void *)(va + off);
+ length -= PAGE_SIZE;
+ while (length > 0) {
+ va += PAGE_SIZE;
+ pa += PAGE_SIZE;
+ length -= PAGE_SIZE;
+ pmap_kenter(va, pa);
+ invlpg(va);
+ }
+ return (data);
+}
+
+static void
+madt_unmap(void *data, vm_offset_t length)
+{
+ vm_offset_t va, off;
+
+ va = (vm_offset_t)data;
+ off = va & PAGE_MASK;
+ length = roundup(length + off, PAGE_SIZE);
+ va &= ~PAGE_MASK;
+ while (length > 0) {
+ pmap_kremove(va);
+ invlpg(va);
+ va += PAGE_SIZE;
+ length -= PAGE_SIZE;
+ }
+}
+
+static void *
+madt_map_table(vm_paddr_t pa, int offset, const char *sig)
+{
+ ACPI_TABLE_HEADER *header;
+ vm_offset_t length;
+ void *table;
+
+ header = madt_map(pa, offset, sizeof(ACPI_TABLE_HEADER));
+ if (strncmp(header->Signature, sig, ACPI_NAME_SIZE) != 0) {
+ madt_unmap(header, sizeof(ACPI_TABLE_HEADER));
+ return (NULL);
+ }
+ length = header->Length;
+ madt_unmap(header, sizeof(ACPI_TABLE_HEADER));
+ table = madt_map(pa, offset, length);
+ if (ACPI_FAILURE(AcpiTbChecksum(table, length))) {
+ if (bootverbose)
+ printf("MADT: Failed checksum for table %s\n", sig);
+ madt_unmap(table, length);
+ return (NULL);
+ }
+ return (table);
+}
+
+static void
+madt_unmap_table(void *table)
+{
+ ACPI_TABLE_HEADER *header;
+
+ header = (ACPI_TABLE_HEADER *)table;
+ madt_unmap(table, header->Length);
+}
+
+/*
+ * Look for an ACPI Multiple APIC Description Table ("APIC")
+ */
+static int
+madt_probe(void)
+{
+ ACPI_PHYSICAL_ADDRESS rsdp_ptr;
+ ACPI_TABLE_RSDP *rsdp;
+ ACPI_TABLE_RSDT *rsdt;
+ ACPI_TABLE_XSDT *xsdt;
+ int i, count;
+
+ if (resource_disabled("acpi", 0))
+ return (ENXIO);
+
+ /*
+ * Map in the RSDP. Since ACPI uses AcpiOsMapMemory() which in turn
+ * calls pmap_mapbios() to find the RSDP, we assume that we can use
+ * pmap_mapbios() to map the RSDP.
+ */
+ if ((rsdp_ptr = AcpiOsGetRootPointer()) == 0)
+ return (ENXIO);
+ rsdp = pmap_mapbios(rsdp_ptr, sizeof(ACPI_TABLE_RSDP));
+ if (rsdp == NULL) {
+ if (bootverbose)
+ printf("MADT: Failed to map RSDP\n");
+ return (ENXIO);
+ }
+
+ /*
+ * For ACPI >= 2.0, use the XSDT if it is available.
+ * Otherwise, use the RSDT. We map the XSDT or RSDT at page 1
+ * in the crashdump area. Page 0 is used to map in the
+ * headers of candidate ACPI tables.
+ */
+ if (rsdp->Revision >= 2 && rsdp->XsdtPhysicalAddress != 0) {
+ /*
+ * AcpiOsGetRootPointer only verifies the checksum for
+ * the version 1.0 portion of the RSDP. Version 2.0 has
+ * an additional checksum that we verify first.
+ */
+ if (AcpiTbChecksum((UINT8 *)rsdp, ACPI_RSDP_XCHECKSUM_LENGTH)) {
+ if (bootverbose)
+ printf("MADT: RSDP failed extended checksum\n");
+ return (ENXIO);
+ }
+ xsdt = madt_map_table(rsdp->XsdtPhysicalAddress, 2,
+ ACPI_SIG_XSDT);
+ if (xsdt == NULL) {
+ if (bootverbose)
+ printf("MADT: Failed to map XSDT\n");
+ return (ENXIO);
+ }
+ count = (xsdt->Header.Length - sizeof(ACPI_TABLE_HEADER)) /
+ sizeof(UINT64);
+ for (i = 0; i < count; i++)
+ if (madt_probe_table(xsdt->TableOffsetEntry[i]))
+ break;
+ madt_unmap_table(xsdt);
+ } else {
+ rsdt = madt_map_table(rsdp->RsdtPhysicalAddress, 2,
+ ACPI_SIG_RSDT);
+ if (rsdt == NULL) {
+ if (bootverbose)
+ printf("MADT: Failed to map RSDT\n");
+ return (ENXIO);
+ }
+ count = (rsdt->Header.Length - sizeof(ACPI_TABLE_HEADER)) /
+ sizeof(UINT32);
+ for (i = 0; i < count; i++)
+ if (madt_probe_table(rsdt->TableOffsetEntry[i]))
+ break;
+ madt_unmap_table(rsdt);
+ }
+ pmap_unmapbios((vm_offset_t)rsdp, sizeof(ACPI_TABLE_RSDP));
+ if (madt_physaddr == 0) {
+ if (bootverbose)
+ printf("MADT: No MADT table found\n");
+ return (ENXIO);
+ }
+ if (bootverbose)
+ printf("MADT: Found table at 0x%jx\n",
+ (uintmax_t)madt_physaddr);
+
+ /*
+ * Verify that we can map the full table and that its checksum is
+ * correct, etc.
+ */
+ madt = madt_map_table(madt_physaddr, 0, ACPI_SIG_MADT);
+ if (madt == NULL)
+ return (ENXIO);
+ madt_unmap_table(madt);
+ madt = NULL;
+
+ return (0);
+}
+
+/*
+ * See if a given ACPI table is the MADT.
+ */
+static int
+madt_probe_table(vm_paddr_t address)
+{
+ ACPI_TABLE_HEADER *table;
+
+ table = madt_map(address, 0, sizeof(ACPI_TABLE_HEADER));
+ if (table == NULL) {
+ if (bootverbose)
+ printf("MADT: Failed to map table at 0x%jx\n",
+ (uintmax_t)address);
+ return (0);
+ }
+ if (bootverbose)
+ printf("Table '%.4s' at 0x%jx\n", table->Signature,
+ (uintmax_t)address);
+
+ if (strncmp(table->Signature, ACPI_SIG_MADT, ACPI_NAME_SIZE) != 0) {
+ madt_unmap(table, sizeof(ACPI_TABLE_HEADER));
+ return (0);
+ }
+ madt_physaddr = address;
+ madt_length = table->Length;
+ madt_unmap(table, sizeof(ACPI_TABLE_HEADER));
+ return (1);
+}
+
+/*
+ * Run through the MP table enumerating CPUs.
+ */
+static int
+madt_probe_cpus(void)
+{
+
+ madt = madt_map_table(madt_physaddr, 0, ACPI_SIG_MADT);
+ KASSERT(madt != NULL, ("Unable to re-map MADT"));
+ madt_walk_table(madt_probe_cpus_handler, NULL);
+ madt_unmap_table(madt);
+ madt = NULL;
+ return (0);
+}
+
+/*
+ * Initialize the local APIC on the BSP.
+ */
+static int
+madt_setup_local(void)
+{
+
+ madt = pmap_mapbios(madt_physaddr, madt_length);
+ lapic_init(madt->Address);
+ printf("ACPI APIC Table: <%.*s %.*s>\n",
+ (int)sizeof(madt->Header.OemId), madt->Header.OemId,
+ (int)sizeof(madt->Header.OemTableId), madt->Header.OemTableId);
+
+ /*
+ * We ignore 64-bit local APIC override entries. Should we
+ * perhaps emit a warning here if we find one?
+ */
+ return (0);
+}
+
+/*
+ * Enumerate I/O APICs and setup interrupt sources.
+ */
+static int
+madt_setup_io(void)
+{
+ void *ioapic;
+ u_int pin;
+ int i;
+
+ /* Try to initialize ACPI so that we can access the FADT. */
+ i = acpi_Startup();
+ if (ACPI_FAILURE(i)) {
+ printf("MADT: ACPI Startup failed with %s\n",
+ AcpiFormatException(i));
+ printf("Try disabling either ACPI or apic support.\n");
+ panic("Using MADT but ACPI doesn't work");
+ }
+
+ /* First, we run through adding I/O APIC's. */
+ madt_walk_table(madt_parse_apics, NULL);
+
+ /* Second, we run through the table tweaking interrupt sources. */
+ madt_walk_table(madt_parse_ints, NULL);
+
+ /*
+ * If there was not an explicit override entry for the SCI,
+ * force it to use level trigger and active-low polarity.
+ */
+ if (!madt_found_sci_override) {
+ if (madt_find_interrupt(AcpiGbl_FADT.SciInterrupt, &ioapic,
+ &pin) != 0)
+ printf("MADT: Could not find APIC for SCI IRQ %u\n",
+ AcpiGbl_FADT.SciInterrupt);
+ else {
+ printf(
+ "MADT: Forcing active-low polarity and level trigger for SCI\n");
+ ioapic_set_polarity(ioapic, pin, INTR_POLARITY_LOW);
+ ioapic_set_triggermode(ioapic, pin, INTR_TRIGGER_LEVEL);
+ }
+ }
+
+ /* Third, we register all the I/O APIC's. */
+ for (i = 0; i <= MAX_APIC_ID; i++)
+ if (ioapics[i].io_apic != NULL)
+ ioapic_register(ioapics[i].io_apic);
+
+ /* Finally, we throw the switch to enable the I/O APIC's. */
+ acpi_SetDefaultIntrModel(ACPI_INTR_APIC);
+
+ return (0);
+}
+
+static void
+madt_register(void *dummy __unused)
+{
+
+ apic_register_enumerator(&madt_enumerator);
+}
+SYSINIT(madt_register, SI_SUB_TUNABLES - 1, SI_ORDER_FIRST,
+ madt_register, NULL);
+
+/*
+ * Call the handler routine for each entry in the MADT table.
+ */
+static void
+madt_walk_table(madt_entry_handler *handler, void *arg)
+{
+ ACPI_SUBTABLE_HEADER *entry;
+ u_char *p, *end;
+
+ end = (u_char *)(madt) + madt->Header.Length;
+ for (p = (u_char *)(madt + 1); p < end; ) {
+ entry = (ACPI_SUBTABLE_HEADER *)p;
+ handler(entry, arg);
+ p += entry->Length;
+ }
+}
+
+static void
+madt_probe_cpus_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
+{
+ ACPI_MADT_LOCAL_APIC *proc;
+ struct lapic_info *la;
+
+ switch (entry->Type) {
+ case ACPI_MADT_TYPE_LOCAL_APIC:
+ /*
+ * The MADT does not include a BSP flag, so we have to
+ * let the MP code figure out which CPU is the BSP on
+ * its own.
+ */
+ proc = (ACPI_MADT_LOCAL_APIC *)entry;
+ if (bootverbose)
+ printf("MADT: Found CPU APIC ID %u ACPI ID %u: %s\n",
+ proc->Id, proc->ProcessorId,
+ (proc->LapicFlags & ACPI_MADT_ENABLED) ?
+ "enabled" : "disabled");
+ if (!(proc->LapicFlags & ACPI_MADT_ENABLED))
+ break;
+ if (proc->Id > MAX_APIC_ID)
+ panic("%s: CPU ID %u too high", __func__, proc->Id);
+ la = &lapics[proc->Id];
+ KASSERT(la->la_enabled == 0,
+ ("Duplicate local APIC ID %u", proc->Id));
+ la->la_enabled = 1;
+ la->la_acpi_id = proc->ProcessorId;
+ lapic_create(proc->Id, 0);
+ break;
+ }
+}
+
+
+/*
+ * Add an I/O APIC from an entry in the table.
+ */
+static void
+madt_parse_apics(ACPI_SUBTABLE_HEADER *entry, void *arg __unused)
+{
+ ACPI_MADT_IO_APIC *apic;
+
+ switch (entry->Type) {
+ case ACPI_MADT_TYPE_IO_APIC:
+ apic = (ACPI_MADT_IO_APIC *)entry;
+ if (bootverbose)
+ printf(
+ "MADT: Found IO APIC ID %u, Interrupt %u at %p\n",
+ apic->Id, apic->GlobalIrqBase,
+ (void *)(uintptr_t)apic->Address);
+ if (apic->Id > MAX_APIC_ID)
+ panic("%s: I/O APIC ID %u too high", __func__,
+ apic->Id);
+ if (ioapics[apic->Id].io_apic != NULL)
+ panic("%s: Double APIC ID %u", __func__, apic->Id);
+ if (apic->GlobalIrqBase >= FIRST_MSI_INT) {
+ printf("MADT: Ignoring bogus I/O APIC ID %u", apic->Id);
+ break;
+ }
+ ioapics[apic->Id].io_apic = ioapic_create(apic->Address,
+ apic->Id, apic->GlobalIrqBase);
+ ioapics[apic->Id].io_vector = apic->GlobalIrqBase;
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Determine properties of an interrupt source. Note that for ACPI these
+ * functions are only used for ISA interrupts, so we assume ISA bus values
+ * (Active Hi, Edge Triggered) for conforming values except for the ACPI
+ * SCI for which we use Active Lo, Level Triggered.
+ */
+static enum intr_polarity
+interrupt_polarity(UINT16 IntiFlags, UINT8 Source)
+{
+
+ switch (IntiFlags & ACPI_MADT_POLARITY_MASK) {
+ case ACPI_MADT_POLARITY_CONFORMS:
+ if (Source == AcpiGbl_FADT.SciInterrupt)
+ return (INTR_POLARITY_LOW);
+ else
+ return (INTR_POLARITY_HIGH);
+ case ACPI_MADT_POLARITY_ACTIVE_HIGH:
+ return (INTR_POLARITY_HIGH);
+ case ACPI_MADT_POLARITY_ACTIVE_LOW:
+ return (INTR_POLARITY_LOW);
+ default:
+ panic("Bogus Interrupt Polarity");
+ }
+}
+
+static enum intr_trigger
+interrupt_trigger(UINT16 IntiFlags, UINT8 Source)
+{
+
+ switch (IntiFlags & ACPI_MADT_TRIGGER_MASK) {
+ case ACPI_MADT_TRIGGER_CONFORMS:
+ if (Source == AcpiGbl_FADT.SciInterrupt)
+ return (INTR_TRIGGER_LEVEL);
+ else
+ return (INTR_TRIGGER_EDGE);
+ case ACPI_MADT_TRIGGER_EDGE:
+ return (INTR_TRIGGER_EDGE);
+ case ACPI_MADT_TRIGGER_LEVEL:
+ return (INTR_TRIGGER_LEVEL);
+ default:
+ panic("Bogus Interrupt Trigger Mode");
+ }
+}
+
+/*
+ * Find the local APIC ID associated with a given ACPI Processor ID.
+ */
+static int
+madt_find_cpu(u_int acpi_id, u_int *apic_id)
+{
+ int i;
+
+ for (i = 0; i <= MAX_APIC_ID; i++) {
+ if (!lapics[i].la_enabled)
+ continue;
+ if (lapics[i].la_acpi_id != acpi_id)
+ continue;
+ *apic_id = i;
+ return (0);
+ }
+ return (ENOENT);
+}
+
+/*
+ * Find the IO APIC and pin on that APIC associated with a given global
+ * interrupt.
+ */
+static int
+madt_find_interrupt(int intr, void **apic, u_int *pin)
+{
+ int i, best;
+
+ best = -1;
+ for (i = 0; i <= MAX_APIC_ID; i++) {
+ if (ioapics[i].io_apic == NULL ||
+ ioapics[i].io_vector > intr)
+ continue;
+ if (best == -1 ||
+ ioapics[best].io_vector < ioapics[i].io_vector)
+ best = i;
+ }
+ if (best == -1)
+ return (ENOENT);
+ *apic = ioapics[best].io_apic;
+ *pin = intr - ioapics[best].io_vector;
+ if (*pin > 32)
+ printf("WARNING: Found intpin of %u for vector %d\n", *pin,
+ intr);
+ return (0);
+}
+
+/*
+ * Parse an interrupt source override for an ISA interrupt.
+ */
+static void
+madt_parse_interrupt_override(ACPI_MADT_INTERRUPT_OVERRIDE *intr)
+{
+ void *new_ioapic, *old_ioapic;
+ u_int new_pin, old_pin;
+ enum intr_trigger trig;
+ enum intr_polarity pol;
+ char buf[64];
+
+ if (acpi_quirks & ACPI_Q_MADT_IRQ0 && intr->SourceIrq == 0 &&
+ intr->GlobalIrq == 2) {
+ if (bootverbose)
+ printf("MADT: Skipping timer override\n");
+ return;
+ }
+ if (bootverbose)
+ printf("MADT: Interrupt override: source %u, irq %u\n",
+ intr->SourceIrq, intr->GlobalIrq);
+ KASSERT(intr->Bus == 0, ("bus for interrupt overrides must be zero"));
+ if (madt_find_interrupt(intr->GlobalIrq, &new_ioapic, &new_pin) != 0) {
+ printf("MADT: Could not find APIC for vector %u (IRQ %u)\n",
+ intr->GlobalIrq, intr->SourceIrq);
+ return;
+ }
+
+ /*
+ * Lookup the appropriate trigger and polarity modes for this
+ * entry.
+ */
+ trig = interrupt_trigger(intr->IntiFlags, intr->SourceIrq);
+ pol = interrupt_polarity(intr->IntiFlags, intr->SourceIrq);
+
+ /*
+ * If the SCI is identity mapped but has edge trigger and
+ * active-hi polarity or the force_sci_lo tunable is set,
+ * force it to use level/lo.
+ */
+ if (intr->SourceIrq == AcpiGbl_FADT.SciInterrupt) {
+ madt_found_sci_override = 1;
+ if (getenv_string("hw.acpi.sci.trigger", buf, sizeof(buf))) {
+ if (tolower(buf[0]) == 'e')
+ trig = INTR_TRIGGER_EDGE;
+ else if (tolower(buf[0]) == 'l')
+ trig = INTR_TRIGGER_LEVEL;
+ else
+ panic(
+ "Invalid trigger %s: must be 'edge' or 'level'",
+ buf);
+ printf("MADT: Forcing SCI to %s trigger\n",
+ trig == INTR_TRIGGER_EDGE ? "edge" : "level");
+ }
+ if (getenv_string("hw.acpi.sci.polarity", buf, sizeof(buf))) {
+ if (tolower(buf[0]) == 'h')
+ pol = INTR_POLARITY_HIGH;
+ else if (tolower(buf[0]) == 'l')
+ pol = INTR_POLARITY_LOW;
+ else
+ panic(
+ "Invalid polarity %s: must be 'high' or 'low'",
+ buf);
+ printf("MADT: Forcing SCI to active %s polarity\n",
+ pol == INTR_POLARITY_HIGH ? "high" : "low");
+ }
+ }
+
+ /* Remap the IRQ if it is mapped to a different interrupt vector. */
+ if (intr->SourceIrq != intr->GlobalIrq) {
+ /*
+ * If the SCI is remapped to a non-ISA global interrupt,
+ * then override the vector we use to setup and allocate
+ * the interrupt.
+ */
+ if (intr->GlobalIrq > 15 &&
+ intr->SourceIrq == AcpiGbl_FADT.SciInterrupt)
+ acpi_OverrideInterruptLevel(intr->GlobalIrq);
+ else
+ ioapic_remap_vector(new_ioapic, new_pin,
+ intr->SourceIrq);
+ if (madt_find_interrupt(intr->SourceIrq, &old_ioapic,
+ &old_pin) != 0)
+ printf("MADT: Could not find APIC for source IRQ %u\n",
+ intr->SourceIrq);
+ else if (ioapic_get_vector(old_ioapic, old_pin) ==
+ intr->SourceIrq)
+ ioapic_disable_pin(old_ioapic, old_pin);
+ }
+
+ /* Program the polarity and trigger mode. */
+ ioapic_set_triggermode(new_ioapic, new_pin, trig);
+ ioapic_set_polarity(new_ioapic, new_pin, pol);
+}
+
+/*
+ * Parse an entry for an NMI routed to an IO APIC.
+ */
+static void
+madt_parse_nmi(ACPI_MADT_NMI_SOURCE *nmi)
+{
+ void *ioapic;
+ u_int pin;
+
+ if (madt_find_interrupt(nmi->GlobalIrq, &ioapic, &pin) != 0) {
+ printf("MADT: Could not find APIC for vector %u\n",
+ nmi->GlobalIrq);
+ return;
+ }
+
+ ioapic_set_nmi(ioapic, pin);
+ if (!(nmi->IntiFlags & ACPI_MADT_TRIGGER_CONFORMS))
+ ioapic_set_triggermode(ioapic, pin,
+ interrupt_trigger(nmi->IntiFlags, 0));
+ if (!(nmi->IntiFlags & ACPI_MADT_TRIGGER_CONFORMS))
+ ioapic_set_polarity(ioapic, pin,
+ interrupt_polarity(nmi->IntiFlags, 0));
+}
+
+/*
+ * Parse an entry for an NMI routed to a local APIC LVT pin.
+ */
+static void
+madt_parse_local_nmi(ACPI_MADT_LOCAL_APIC_NMI *nmi)
+{
+ u_int apic_id, pin;
+
+ if (nmi->ProcessorId == 0xff)
+ apic_id = APIC_ID_ALL;
+ else if (madt_find_cpu(nmi->ProcessorId, &apic_id) != 0) {
+ if (bootverbose)
+ printf("MADT: Ignoring local NMI routed to "
+ "ACPI CPU %u\n", nmi->ProcessorId);
+ return;
+ }
+ if (nmi->Lint == 0)
+ pin = LVT_LINT0;
+ else
+ pin = LVT_LINT1;
+ lapic_set_lvt_mode(apic_id, pin, APIC_LVT_DM_NMI);
+ if (!(nmi->IntiFlags & ACPI_MADT_TRIGGER_CONFORMS))
+ lapic_set_lvt_triggermode(apic_id, pin,
+ interrupt_trigger(nmi->IntiFlags, 0));
+ if (!(nmi->IntiFlags & ACPI_MADT_POLARITY_CONFORMS))
+ lapic_set_lvt_polarity(apic_id, pin,
+ interrupt_polarity(nmi->IntiFlags, 0));
+}
+
+/*
+ * Parse interrupt entries.
+ */
+static void
+madt_parse_ints(ACPI_SUBTABLE_HEADER *entry, void *arg __unused)
+{
+
+ switch (entry->Type) {
+ case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE:
+ madt_parse_interrupt_override(
+ (ACPI_MADT_INTERRUPT_OVERRIDE *)entry);
+ break;
+ case ACPI_MADT_TYPE_NMI_SOURCE:
+ madt_parse_nmi((ACPI_MADT_NMI_SOURCE *)entry);
+ break;
+ case ACPI_MADT_TYPE_LOCAL_APIC_NMI:
+ madt_parse_local_nmi((ACPI_MADT_LOCAL_APIC_NMI *)entry);
+ break;
+ }
+}
+
+/*
+ * Setup per-CPU ACPI IDs.
+ */
+static void
+madt_set_ids(void *dummy)
+{
+ struct lapic_info *la;
+ struct pcpu *pc;
+ u_int i;
+
+ if (madt == NULL)
+ return;
+ for (i = 0; i < MAXCPU; i++) {
+ if (CPU_ABSENT(i))
+ continue;
+ pc = pcpu_find(i);
+ KASSERT(pc != NULL, ("no pcpu data for CPU %u", i));
+ la = &lapics[pc->pc_apic_id];
+ if (!la->la_enabled)
+ panic("APIC: CPU with APIC ID %u is not enabled",
+ pc->pc_apic_id);
+ pc->pc_acpi_id = la->la_acpi_id;
+ if (bootverbose)
+ printf("APIC: CPU %u has ACPI ID %u\n", i,
+ la->la_acpi_id);
+ }
+}
+SYSINIT(madt_set_ids, SI_SUB_CPU, SI_ORDER_ANY, madt_set_ids, NULL);