madt.c revision 286994
183098Smp/*-
259243Sobrien * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
359243Sobrien * All rights reserved.
459243Sobrien *
559243Sobrien * Redistribution and use in source and binary forms, with or without
659243Sobrien * modification, are permitted provided that the following conditions
759243Sobrien * are met:
859243Sobrien * 1. Redistributions of source code must retain the above copyright
959243Sobrien *    notice, this list of conditions and the following disclaimer.
1059243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1159243Sobrien *    notice, this list of conditions and the following disclaimer in the
1259243Sobrien *    documentation and/or other materials provided with the distribution.
1359243Sobrien *
1459243Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1559243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1659243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1759243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1859243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1959243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2059243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2159243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2259243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2359243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2459243Sobrien * SUCH DAMAGE.
2559243Sobrien */
2659243Sobrien
2759243Sobrien#include <sys/cdefs.h>
2859243Sobrien__FBSDID("$FreeBSD: head/sys/x86/acpica/madt.c 286994 2015-08-21 15:13:25Z kib $");
2959243Sobrien
3059243Sobrien#include <sys/param.h>
3159243Sobrien#include <sys/systm.h>
3259243Sobrien#include <sys/bus.h>
3359243Sobrien#include <sys/kernel.h>
3459243Sobrien#include <sys/limits.h>
3559243Sobrien#include <sys/malloc.h>
3659243Sobrien#include <sys/smp.h>
3759243Sobrien#include <vm/vm.h>
3869408Sache#include <vm/pmap.h>
3959243Sobrien
4083098Smp#include <x86/apicreg.h>
4159243Sobrien#include <machine/intr_machdep.h>
4259243Sobrien#include <x86/apicvar.h>
4359243Sobrien#include <machine/md_var.h>
4459243Sobrien#include <x86/vmware.h>
4559243Sobrien
4659243Sobrien#include <contrib/dev/acpica/include/acpi.h>
4759243Sobrien#include <contrib/dev/acpica/include/actables.h>
4859243Sobrien
4959243Sobrien#include <dev/acpica/acpivar.h>
5059243Sobrien#include <dev/pci/pcivar.h>
5159243Sobrien
5259243Sobrien/* These two arrays are indexed by APIC IDs. */
5359243Sobrienstatic struct {
5459243Sobrien	void *io_apic;
5559243Sobrien	UINT32 io_vector;
5659243Sobrien} *ioapics;
5759243Sobrien
5859243Sobrienstatic struct lapic_info {
5959243Sobrien	u_int la_enabled;
6059243Sobrien	u_int la_acpi_id;
6159243Sobrien} lapics[MAX_APIC_ID + 1];
6259243Sobrien
6359243Sobrienint madt_found_sci_override;
6459243Sobrienstatic ACPI_TABLE_MADT *madt;
6559243Sobrienstatic vm_paddr_t madt_physaddr;
6659243Sobrienstatic vm_offset_t madt_length;
6759243Sobrien
6859243Sobrienstatic MALLOC_DEFINE(M_MADT, "madt_table", "ACPI MADT Table Items");
6959243Sobrien
7059243Sobrienstatic enum intr_polarity interrupt_polarity(UINT16 IntiFlags, UINT8 Source);
7159243Sobrienstatic enum intr_trigger interrupt_trigger(UINT16 IntiFlags, UINT8 Source);
7259243Sobrienstatic int	madt_find_cpu(u_int acpi_id, u_int *apic_id);
7359243Sobrienstatic int	madt_find_interrupt(int intr, void **apic, u_int *pin);
7459243Sobrienstatic void	madt_parse_apics(ACPI_SUBTABLE_HEADER *entry, void *arg);
7559243Sobrienstatic void	madt_parse_interrupt_override(
7659243Sobrien		    ACPI_MADT_INTERRUPT_OVERRIDE *intr);
7759243Sobrienstatic void	madt_parse_ints(ACPI_SUBTABLE_HEADER *entry,
7859243Sobrien		    void *arg __unused);
7959243Sobrienstatic void	madt_parse_local_nmi(ACPI_MADT_LOCAL_APIC_NMI *nmi);
8059243Sobrienstatic void	madt_parse_nmi(ACPI_MADT_NMI_SOURCE *nmi);
8159243Sobrienstatic int	madt_probe(void);
8259243Sobrienstatic int	madt_probe_cpus(void);
8359243Sobrienstatic void	madt_probe_cpus_handler(ACPI_SUBTABLE_HEADER *entry,
8459243Sobrien		    void *arg __unused);
8559243Sobrienstatic void	madt_register(void *dummy);
8659243Sobrienstatic int	madt_setup_local(void);
8759243Sobrienstatic int	madt_setup_io(void);
8859243Sobrienstatic void	madt_walk_table(acpi_subtable_handler *handler, void *arg);
8959243Sobrien
9059243Sobrienstatic struct apic_enumerator madt_enumerator = {
9159243Sobrien	"MADT",
9259243Sobrien	madt_probe,
9359243Sobrien	madt_probe_cpus,
9459243Sobrien	madt_setup_local,
9559243Sobrien	madt_setup_io
9659243Sobrien};
9759243Sobrien
9859243Sobrien/*
9959243Sobrien * Look for an ACPI Multiple APIC Description Table ("APIC")
10059243Sobrien */
10159243Sobrienstatic int
10259243Sobrienmadt_probe(void)
10359243Sobrien{
10459243Sobrien
10559243Sobrien	madt_physaddr = acpi_find_table(ACPI_SIG_MADT);
10659243Sobrien	if (madt_physaddr == 0)
10759243Sobrien		return (ENXIO);
10859243Sobrien	return (-50);
10959243Sobrien}
11059243Sobrien
11159243Sobrien/*
11259243Sobrien * Run through the MP table enumerating CPUs.
11359243Sobrien */
11459243Sobrienstatic int
11559243Sobrienmadt_probe_cpus(void)
11659243Sobrien{
11759243Sobrien
11859243Sobrien	madt = acpi_map_table(madt_physaddr, ACPI_SIG_MADT);
11959243Sobrien	madt_length = madt->Header.Length;
12059243Sobrien	KASSERT(madt != NULL, ("Unable to re-map MADT"));
12159243Sobrien	madt_walk_table(madt_probe_cpus_handler, NULL);
12259243Sobrien	acpi_unmap_table(madt);
12359243Sobrien	madt = NULL;
12459243Sobrien	return (0);
12559243Sobrien}
12659243Sobrien
12759243Sobrien/*
12859243Sobrien * Initialize the local APIC on the BSP.
12959243Sobrien */
13059243Sobrienstatic int
13159243Sobrienmadt_setup_local(void)
13259243Sobrien{
13359243Sobrien	ACPI_TABLE_DMAR *dmartbl;
13459243Sobrien	vm_paddr_t dmartbl_physaddr;
13559243Sobrien	const char *reason;
13659243Sobrien	char *hw_vendor;
13759243Sobrien	u_int p[4];
13859243Sobrien
13959243Sobrien	madt = pmap_mapbios(madt_physaddr, madt_length);
14059243Sobrien	if ((cpu_feature2 & CPUID2_X2APIC) != 0) {
14159243Sobrien		x2apic_mode = 1;
14259243Sobrien		reason = NULL;
14359243Sobrien
14459243Sobrien		/*
14559243Sobrien		 * Automatically detect several configurations where
14659243Sobrien		 * x2APIC mode is known to cause troubles.  User can
14759243Sobrien		 * override the setting with hw.x2apic_enable tunable.
14859243Sobrien		 */
14959243Sobrien		dmartbl_physaddr = acpi_find_table(ACPI_SIG_DMAR);
15059243Sobrien		if (dmartbl_physaddr != 0) {
15159243Sobrien			dmartbl = acpi_map_table(dmartbl_physaddr,
15259243Sobrien			    ACPI_SIG_DMAR);
15359243Sobrien			if ((dmartbl->Flags & ACPI_DMAR_X2APIC_OPT_OUT) != 0) {
15459243Sobrien				x2apic_mode = 0;
15559243Sobrien				reason = "by DMAR table";
15659243Sobrien			}
15759243Sobrien			acpi_unmap_table(dmartbl);
15859243Sobrien		}
15959243Sobrien		if (vm_guest == VM_GUEST_VMWARE) {
16059243Sobrien			vmware_hvcall(VMW_HVCMD_GETVCPU_INFO, p);
16159243Sobrien			if ((p[0] & VMW_VCPUINFO_VCPU_RESERVED) != 0 ||
16259243Sobrien			    (p[0] & VMW_VCPUINFO_LEGACY_X2APIC) == 0) {
16359243Sobrien				x2apic_mode = 0;
16459243Sobrien		reason = "inside VMWare without intr redirection";
16559243Sobrien			}
16659243Sobrien		} else if (vm_guest == VM_GUEST_XEN) {
16759243Sobrien			x2apic_mode = 0;
16859243Sobrien			reason = "due to running under XEN";
16959243Sobrien		} else if (vm_guest == VM_GUEST_NO) {
17059243Sobrien			hw_vendor = kern_getenv("smbios.planar.maker");
17159243Sobrien			/*
17259243Sobrien			 * It seems that some Lenovo SandyBridge-based
17359243Sobrien			 * notebook BIOSes have a bug which prevents
17459243Sobrien			 * booting AP in x2APIC mode.  Since the only
17559243Sobrien			 * way to detect mobile CPU is to check
17659243Sobrien			 * northbridge pci id, which cannot be done
17759243Sobrien			 * that early, disable x2APIC for all Lenovo
17859243Sobrien			 * SandyBridge machines.
17959243Sobrien			 */
18059243Sobrien			if (hw_vendor != NULL &&
18159243Sobrien			    !strcmp(hw_vendor, "LENOVO") &&
18259243Sobrien			    CPUID_TO_FAMILY(cpu_id) == 0x6 &&
18359243Sobrien			    CPUID_TO_MODEL(cpu_id) == 0x2a) {
18459243Sobrien				x2apic_mode = 0;
18559243Sobrien		reason = "for a suspected Lenovo SandyBridge BIOS bug";
18659243Sobrien			}
18759243Sobrien			freeenv(hw_vendor);
18859243Sobrien		}
18959243Sobrien		TUNABLE_INT_FETCH("hw.x2apic_enable", &x2apic_mode);
19059243Sobrien		if (!x2apic_mode && reason != NULL && bootverbose)
19159243Sobrien			printf("x2APIC available but disabled %s\n", reason);
19259243Sobrien	}
19359243Sobrien
19459243Sobrien	lapic_init(madt->Address);
19559243Sobrien	printf("ACPI APIC Table: <%.*s %.*s>\n",
19659243Sobrien	    (int)sizeof(madt->Header.OemId), madt->Header.OemId,
19759243Sobrien	    (int)sizeof(madt->Header.OemTableId), madt->Header.OemTableId);
19859243Sobrien
19959243Sobrien	/*
20059243Sobrien	 * We ignore 64-bit local APIC override entries.  Should we
20159243Sobrien	 * perhaps emit a warning here if we find one?
20259243Sobrien	 */
20359243Sobrien	return (0);
20459243Sobrien}
20559243Sobrien
20659243Sobrien/*
20759243Sobrien * Enumerate I/O APICs and setup interrupt sources.
20859243Sobrien */
20959243Sobrienstatic int
21059243Sobrienmadt_setup_io(void)
21159243Sobrien{
21259243Sobrien	void *ioapic;
21359243Sobrien	u_int pin;
21459243Sobrien	int i;
21559243Sobrien
21659243Sobrien	/* Try to initialize ACPI so that we can access the FADT. */
21759243Sobrien	i = acpi_Startup();
21859243Sobrien	if (ACPI_FAILURE(i)) {
21959243Sobrien		printf("MADT: ACPI Startup failed with %s\n",
22059243Sobrien		    AcpiFormatException(i));
22159243Sobrien		printf("Try disabling either ACPI or apic support.\n");
22259243Sobrien		panic("Using MADT but ACPI doesn't work");
22359243Sobrien	}
22459243Sobrien
22559243Sobrien	ioapics = malloc(sizeof(*ioapics) * (MAX_APIC_ID + 1), M_MADT,
22659243Sobrien	    M_WAITOK | M_ZERO);
22759243Sobrien
22859243Sobrien	/* First, we run through adding I/O APIC's. */
22959243Sobrien	madt_walk_table(madt_parse_apics, NULL);
23059243Sobrien
23159243Sobrien	/* Second, we run through the table tweaking interrupt sources. */
23259243Sobrien	madt_walk_table(madt_parse_ints, NULL);
23359243Sobrien
23459243Sobrien	/*
23559243Sobrien	 * If there was not an explicit override entry for the SCI,
23659243Sobrien	 * force it to use level trigger and active-low polarity.
23759243Sobrien	 */
23859243Sobrien	if (!madt_found_sci_override) {
23959243Sobrien		if (madt_find_interrupt(AcpiGbl_FADT.SciInterrupt, &ioapic,
24059243Sobrien		    &pin) != 0)
24159243Sobrien			printf("MADT: Could not find APIC for SCI IRQ %u\n",
24259243Sobrien			    AcpiGbl_FADT.SciInterrupt);
24359243Sobrien		else {
24459243Sobrien			printf(
24559243Sobrien	"MADT: Forcing active-low polarity and level trigger for SCI\n");
24659243Sobrien			ioapic_set_polarity(ioapic, pin, INTR_POLARITY_LOW);
24759243Sobrien			ioapic_set_triggermode(ioapic, pin, INTR_TRIGGER_LEVEL);
24859243Sobrien		}
24959243Sobrien	}
25059243Sobrien
25159243Sobrien	/* Third, we register all the I/O APIC's. */
25259243Sobrien	for (i = 0; i <= MAX_APIC_ID; i++)
25359243Sobrien		if (ioapics[i].io_apic != NULL)
25459243Sobrien			ioapic_register(ioapics[i].io_apic);
25559243Sobrien
25659243Sobrien	/* Finally, we throw the switch to enable the I/O APIC's. */
25759243Sobrien	acpi_SetDefaultIntrModel(ACPI_INTR_APIC);
25859243Sobrien
25959243Sobrien	free(ioapics, M_MADT);
26059243Sobrien	ioapics = NULL;
26159243Sobrien
26259243Sobrien	return (0);
26359243Sobrien}
26459243Sobrien
26559243Sobrienstatic void
26659243Sobrienmadt_register(void *dummy __unused)
26759243Sobrien{
26859243Sobrien
26959243Sobrien	apic_register_enumerator(&madt_enumerator);
27059243Sobrien}
27159243SobrienSYSINIT(madt_register, SI_SUB_TUNABLES - 1, SI_ORDER_FIRST, madt_register, NULL);
27259243Sobrien
27359243Sobrien/*
27459243Sobrien * Call the handler routine for each entry in the MADT table.
27559243Sobrien */
27659243Sobrienstatic void
27759243Sobrienmadt_walk_table(acpi_subtable_handler *handler, void *arg)
27859243Sobrien{
27959243Sobrien
28059243Sobrien	acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
28159243Sobrien	    handler, arg);
28259243Sobrien}
28359243Sobrien
28459243Sobrienstatic void
28559243Sobrienmadt_add_cpu(u_int acpi_id, u_int apic_id, u_int flags)
28659243Sobrien{
28759243Sobrien	struct lapic_info *la;
28859243Sobrien
28959243Sobrien	/*
29059243Sobrien	 * The MADT does not include a BSP flag, so we have to let the
29159243Sobrien	 * MP code figure out which CPU is the BSP on its own.
29259243Sobrien	 */
29359243Sobrien	if (bootverbose)
29459243Sobrien		printf("MADT: Found CPU APIC ID %u ACPI ID %u: %s\n",
29559243Sobrien		    apic_id, acpi_id, flags & ACPI_MADT_ENABLED ?
29659243Sobrien		    "enabled" : "disabled");
29759243Sobrien	if (!(flags & ACPI_MADT_ENABLED))
29859243Sobrien		return;
29959243Sobrien	if (apic_id > MAX_APIC_ID) {
30059243Sobrien		printf("MADT: Ignoring local APIC ID %u (too high)\n",
30159243Sobrien		    apic_id);
30259243Sobrien		return;
30359243Sobrien	}
30459243Sobrien
30559243Sobrien	la = &lapics[apic_id];
30659243Sobrien	KASSERT(la->la_enabled == 0, ("Duplicate local APIC ID %u", apic_id));
30759243Sobrien	la->la_enabled = 1;
30859243Sobrien	la->la_acpi_id = acpi_id;
30959243Sobrien	lapic_create(apic_id, 0);
31059243Sobrien}
31159243Sobrien
31259243Sobrienstatic void
31359243Sobrienmadt_probe_cpus_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
31459243Sobrien{
31559243Sobrien	ACPI_MADT_LOCAL_APIC *proc;
31659243Sobrien	ACPI_MADT_LOCAL_X2APIC *x2apic;
31759243Sobrien
31859243Sobrien	switch (entry->Type) {
31959243Sobrien	case ACPI_MADT_TYPE_LOCAL_APIC:
32059243Sobrien		proc = (ACPI_MADT_LOCAL_APIC *)entry;
32159243Sobrien		madt_add_cpu(proc->ProcessorId, proc->Id, proc->LapicFlags);
32259243Sobrien		break;
32359243Sobrien	case ACPI_MADT_TYPE_LOCAL_X2APIC:
32459243Sobrien		x2apic = (ACPI_MADT_LOCAL_X2APIC *)entry;
32559243Sobrien		madt_add_cpu(x2apic->Uid, x2apic->LocalApicId,
32659243Sobrien		    x2apic->LapicFlags);
32759243Sobrien		break;
32859243Sobrien	}
32959243Sobrien}
33059243Sobrien
33159243Sobrien
33259243Sobrien/*
33359243Sobrien * Add an I/O APIC from an entry in the table.
33459243Sobrien */
33559243Sobrienstatic void
33659243Sobrienmadt_parse_apics(ACPI_SUBTABLE_HEADER *entry, void *arg __unused)
33759243Sobrien{
33859243Sobrien	ACPI_MADT_IO_APIC *apic;
33959243Sobrien
34059243Sobrien	switch (entry->Type) {
34159243Sobrien	case ACPI_MADT_TYPE_IO_APIC:
34259243Sobrien		apic = (ACPI_MADT_IO_APIC *)entry;
34359243Sobrien		if (bootverbose)
34459243Sobrien			printf(
34559243Sobrien			    "MADT: Found IO APIC ID %u, Interrupt %u at %p\n",
34659243Sobrien			    apic->Id, apic->GlobalIrqBase,
34759243Sobrien			    (void *)(uintptr_t)apic->Address);
34859243Sobrien		if (apic->Id > MAX_APIC_ID)
34959243Sobrien			panic("%s: I/O APIC ID %u too high", __func__,
35059243Sobrien			    apic->Id);
35159243Sobrien		if (ioapics[apic->Id].io_apic != NULL)
35259243Sobrien			panic("%s: Double APIC ID %u", __func__, apic->Id);
35359243Sobrien		if (apic->GlobalIrqBase >= FIRST_MSI_INT) {
35459243Sobrien			printf("MADT: Ignoring bogus I/O APIC ID %u", apic->Id);
35559243Sobrien			break;
35659243Sobrien		}
35759243Sobrien		ioapics[apic->Id].io_apic = ioapic_create(apic->Address,
35859243Sobrien		    apic->Id, apic->GlobalIrqBase);
35959243Sobrien		ioapics[apic->Id].io_vector = apic->GlobalIrqBase;
36059243Sobrien		break;
36159243Sobrien	default:
36259243Sobrien		break;
36359243Sobrien	}
36459243Sobrien}
36559243Sobrien
36659243Sobrien/*
36759243Sobrien * Determine properties of an interrupt source.  Note that for ACPI these
36859243Sobrien * functions are only used for ISA interrupts, so we assume ISA bus values
36959243Sobrien * (Active Hi, Edge Triggered) for conforming values except for the ACPI
37059243Sobrien * SCI for which we use Active Lo, Level Triggered.
37159243Sobrien */
37259243Sobrienstatic enum intr_polarity
37359243Sobrieninterrupt_polarity(UINT16 IntiFlags, UINT8 Source)
37459243Sobrien{
37559243Sobrien
37659243Sobrien	switch (IntiFlags & ACPI_MADT_POLARITY_MASK) {
37759243Sobrien	default:
37859243Sobrien		printf("WARNING: Bogus Interrupt Polarity. Assume CONFORMS\n");
37959243Sobrien		/* FALLTHROUGH*/
38059243Sobrien	case ACPI_MADT_POLARITY_CONFORMS:
38159243Sobrien		if (Source == AcpiGbl_FADT.SciInterrupt)
38259243Sobrien			return (INTR_POLARITY_LOW);
38359243Sobrien		else
38459243Sobrien			return (INTR_POLARITY_HIGH);
38559243Sobrien	case ACPI_MADT_POLARITY_ACTIVE_HIGH:
38659243Sobrien		return (INTR_POLARITY_HIGH);
38759243Sobrien	case ACPI_MADT_POLARITY_ACTIVE_LOW:
38859243Sobrien		return (INTR_POLARITY_LOW);
38959243Sobrien	}
39059243Sobrien}
39159243Sobrien
39259243Sobrienstatic enum intr_trigger
39359243Sobrieninterrupt_trigger(UINT16 IntiFlags, UINT8 Source)
39459243Sobrien{
39559243Sobrien
39659243Sobrien	switch (IntiFlags & ACPI_MADT_TRIGGER_MASK) {
39759243Sobrien	default:
39859243Sobrien		printf("WARNING: Bogus Interrupt Trigger Mode. Assume CONFORMS.\n");
39959243Sobrien		/*FALLTHROUGH*/
40059243Sobrien	case ACPI_MADT_TRIGGER_CONFORMS:
40159243Sobrien		if (Source == AcpiGbl_FADT.SciInterrupt)
40259243Sobrien			return (INTR_TRIGGER_LEVEL);
40359243Sobrien		else
40459243Sobrien			return (INTR_TRIGGER_EDGE);
40559243Sobrien	case ACPI_MADT_TRIGGER_EDGE:
40659243Sobrien		return (INTR_TRIGGER_EDGE);
40759243Sobrien	case ACPI_MADT_TRIGGER_LEVEL:
40859243Sobrien		return (INTR_TRIGGER_LEVEL);
40959243Sobrien	}
41059243Sobrien}
41159243Sobrien
41259243Sobrien/*
41359243Sobrien * Find the local APIC ID associated with a given ACPI Processor ID.
41459243Sobrien */
41559243Sobrienstatic int
41659243Sobrienmadt_find_cpu(u_int acpi_id, u_int *apic_id)
41759243Sobrien{
41859243Sobrien	int i;
41959243Sobrien
42059243Sobrien	for (i = 0; i <= MAX_APIC_ID; i++) {
42159243Sobrien		if (!lapics[i].la_enabled)
42259243Sobrien			continue;
42359243Sobrien		if (lapics[i].la_acpi_id != acpi_id)
42459243Sobrien			continue;
42559243Sobrien		*apic_id = i;
42659243Sobrien		return (0);
42759243Sobrien	}
42859243Sobrien	return (ENOENT);
42959243Sobrien}
43059243Sobrien
43159243Sobrien/*
43259243Sobrien * Find the IO APIC and pin on that APIC associated with a given global
43359243Sobrien * interrupt.
43459243Sobrien */
43559243Sobrienstatic int
43659243Sobrienmadt_find_interrupt(int intr, void **apic, u_int *pin)
43759243Sobrien{
43859243Sobrien	int i, best;
43959243Sobrien
44059243Sobrien	best = -1;
44159243Sobrien	for (i = 0; i <= MAX_APIC_ID; i++) {
44259243Sobrien		if (ioapics[i].io_apic == NULL ||
44359243Sobrien		    ioapics[i].io_vector > intr)
44459243Sobrien			continue;
44559243Sobrien		if (best == -1 ||
44659243Sobrien		    ioapics[best].io_vector < ioapics[i].io_vector)
44759243Sobrien			best = i;
44859243Sobrien	}
44959243Sobrien	if (best == -1)
45059243Sobrien		return (ENOENT);
45159243Sobrien	*apic = ioapics[best].io_apic;
45259243Sobrien	*pin = intr - ioapics[best].io_vector;
45359243Sobrien	if (*pin > 32)
45459243Sobrien		printf("WARNING: Found intpin of %u for vector %d\n", *pin,
45559243Sobrien		    intr);
45659243Sobrien	return (0);
45759243Sobrien}
45859243Sobrien
45959243Sobrienvoid
46059243Sobrienmadt_parse_interrupt_values(void *entry,
46159243Sobrien    enum intr_trigger *trig, enum intr_polarity *pol)
46259243Sobrien{
46359243Sobrien	ACPI_MADT_INTERRUPT_OVERRIDE *intr;
46459243Sobrien	char buf[64];
46559243Sobrien
46659243Sobrien	intr = entry;
46759243Sobrien
46859243Sobrien	if (bootverbose)
46959243Sobrien		printf("MADT: Interrupt override: source %u, irq %u\n",
47059243Sobrien		    intr->SourceIrq, intr->GlobalIrq);
47159243Sobrien	KASSERT(intr->Bus == 0, ("bus for interrupt overrides must be zero"));
47259243Sobrien
47359243Sobrien	/*
47459243Sobrien	 * Lookup the appropriate trigger and polarity modes for this
47559243Sobrien	 * entry.
47659243Sobrien	 */
47759243Sobrien	*trig = interrupt_trigger(intr->IntiFlags, intr->SourceIrq);
47859243Sobrien	*pol = interrupt_polarity(intr->IntiFlags, intr->SourceIrq);
47959243Sobrien
48059243Sobrien	/*
48159243Sobrien	 * If the SCI is identity mapped but has edge trigger and
48259243Sobrien	 * active-hi polarity or the force_sci_lo tunable is set,
48359243Sobrien	 * force it to use level/lo.
48459243Sobrien	 */
48559243Sobrien	if (intr->SourceIrq == AcpiGbl_FADT.SciInterrupt) {
48659243Sobrien		madt_found_sci_override = 1;
48759243Sobrien		if (getenv_string("hw.acpi.sci.trigger", buf, sizeof(buf))) {
48859243Sobrien			if (tolower(buf[0]) == 'e')
48959243Sobrien				*trig = INTR_TRIGGER_EDGE;
49059243Sobrien			else if (tolower(buf[0]) == 'l')
49159243Sobrien				*trig = INTR_TRIGGER_LEVEL;
49259243Sobrien			else
49359243Sobrien				panic(
49459243Sobrien				"Invalid trigger %s: must be 'edge' or 'level'",
49559243Sobrien				    buf);
49659243Sobrien			printf("MADT: Forcing SCI to %s trigger\n",
49759243Sobrien			    *trig == INTR_TRIGGER_EDGE ? "edge" : "level");
49859243Sobrien		}
49959243Sobrien		if (getenv_string("hw.acpi.sci.polarity", buf, sizeof(buf))) {
50059243Sobrien			if (tolower(buf[0]) == 'h')
50159243Sobrien				*pol = INTR_POLARITY_HIGH;
50259243Sobrien			else if (tolower(buf[0]) == 'l')
50359243Sobrien				*pol = INTR_POLARITY_LOW;
50459243Sobrien			else
50559243Sobrien				panic(
50659243Sobrien				"Invalid polarity %s: must be 'high' or 'low'",
50759243Sobrien				    buf);
50859243Sobrien			printf("MADT: Forcing SCI to active %s polarity\n",
50959243Sobrien			    *pol == INTR_POLARITY_HIGH ? "high" : "low");
51059243Sobrien		}
51159243Sobrien	}
51259243Sobrien}
51359243Sobrien
51459243Sobrien/*
51559243Sobrien * Parse an interrupt source override for an ISA interrupt.
51659243Sobrien */
51759243Sobrienstatic void
51859243Sobrienmadt_parse_interrupt_override(ACPI_MADT_INTERRUPT_OVERRIDE *intr)
51959243Sobrien{
52059243Sobrien	void *new_ioapic, *old_ioapic;
52159243Sobrien	u_int new_pin, old_pin;
52259243Sobrien	enum intr_trigger trig;
52359243Sobrien	enum intr_polarity pol;
52459243Sobrien
52559243Sobrien	if (acpi_quirks & ACPI_Q_MADT_IRQ0 && intr->SourceIrq == 0 &&
52659243Sobrien	    intr->GlobalIrq == 2) {
52759243Sobrien		if (bootverbose)
52859243Sobrien			printf("MADT: Skipping timer override\n");
52959243Sobrien		return;
53059243Sobrien	}
53159243Sobrien
53259243Sobrien	if (madt_find_interrupt(intr->GlobalIrq, &new_ioapic, &new_pin) != 0) {
53359243Sobrien		printf("MADT: Could not find APIC for vector %u (IRQ %u)\n",
53459243Sobrien		    intr->GlobalIrq, intr->SourceIrq);
53559243Sobrien		return;
53659243Sobrien	}
53759243Sobrien
53859243Sobrien	madt_parse_interrupt_values(intr, &trig, &pol);
53959243Sobrien
54059243Sobrien	/* Remap the IRQ if it is mapped to a different interrupt vector. */
54159243Sobrien	if (intr->SourceIrq != intr->GlobalIrq) {
54259243Sobrien		/*
54359243Sobrien		 * If the SCI is remapped to a non-ISA global interrupt,
54459243Sobrien		 * then override the vector we use to setup and allocate
54559243Sobrien		 * the interrupt.
54659243Sobrien		 */
54759243Sobrien		if (intr->GlobalIrq > 15 &&
54859243Sobrien		    intr->SourceIrq == AcpiGbl_FADT.SciInterrupt)
54959243Sobrien			acpi_OverrideInterruptLevel(intr->GlobalIrq);
55059243Sobrien		else
55159243Sobrien			ioapic_remap_vector(new_ioapic, new_pin,
55259243Sobrien			    intr->SourceIrq);
55359243Sobrien		if (madt_find_interrupt(intr->SourceIrq, &old_ioapic,
55459243Sobrien		    &old_pin) != 0)
55559243Sobrien			printf("MADT: Could not find APIC for source IRQ %u\n",
55659243Sobrien			    intr->SourceIrq);
55759243Sobrien		else if (ioapic_get_vector(old_ioapic, old_pin) ==
55859243Sobrien		    intr->SourceIrq)
55959243Sobrien			ioapic_disable_pin(old_ioapic, old_pin);
56059243Sobrien	}
56159243Sobrien
56259243Sobrien	/* Program the polarity and trigger mode. */
56359243Sobrien	ioapic_set_triggermode(new_ioapic, new_pin, trig);
56459243Sobrien	ioapic_set_polarity(new_ioapic, new_pin, pol);
56559243Sobrien}
56659243Sobrien
56759243Sobrien/*
56859243Sobrien * Parse an entry for an NMI routed to an IO APIC.
56983098Smp */
57083098Smpstatic void
57183098Smpmadt_parse_nmi(ACPI_MADT_NMI_SOURCE *nmi)
57283098Smp{
57383098Smp	void *ioapic;
57483098Smp	u_int pin;
57559243Sobrien
57683098Smp	if (madt_find_interrupt(nmi->GlobalIrq, &ioapic, &pin) != 0) {
57759243Sobrien		printf("MADT: Could not find APIC for vector %u\n",
57883098Smp		    nmi->GlobalIrq);
57959243Sobrien		return;
58083098Smp	}
58183098Smp
58283098Smp	ioapic_set_nmi(ioapic, pin);
58383098Smp	if (!(nmi->IntiFlags & ACPI_MADT_TRIGGER_CONFORMS))
58459243Sobrien		ioapic_set_triggermode(ioapic, pin,
58559243Sobrien		    interrupt_trigger(nmi->IntiFlags, 0));
58659243Sobrien	if (!(nmi->IntiFlags & ACPI_MADT_POLARITY_CONFORMS))
58759243Sobrien		ioapic_set_polarity(ioapic, pin,
58859243Sobrien		    interrupt_polarity(nmi->IntiFlags, 0));
58959243Sobrien}
59059243Sobrien
59159243Sobrien/*
59259243Sobrien * Parse an entry for an NMI routed to a local APIC LVT pin.
59359243Sobrien */
59459243Sobrienstatic void
59559243Sobrienmadt_handle_local_nmi(u_int acpi_id, UINT8 Lint, UINT16 IntiFlags)
59659243Sobrien{
59759243Sobrien	u_int apic_id, pin;
59859243Sobrien
59959243Sobrien	if (acpi_id == 0xffffffff)
60059243Sobrien		apic_id = APIC_ID_ALL;
60159243Sobrien	else if (madt_find_cpu(acpi_id, &apic_id) != 0) {
60259243Sobrien		if (bootverbose)
60359243Sobrien			printf("MADT: Ignoring local NMI routed to "
60459243Sobrien			    "ACPI CPU %u\n", acpi_id);
60559243Sobrien		return;
60659243Sobrien	}
60759243Sobrien	if (Lint == 0)
60859243Sobrien		pin = APIC_LVT_LINT0;
60959243Sobrien	else
61059243Sobrien		pin = APIC_LVT_LINT1;
61159243Sobrien	lapic_set_lvt_mode(apic_id, pin, APIC_LVT_DM_NMI);
61259243Sobrien	if (!(IntiFlags & ACPI_MADT_TRIGGER_CONFORMS))
61359243Sobrien		lapic_set_lvt_triggermode(apic_id, pin,
61459243Sobrien		    interrupt_trigger(IntiFlags, 0));
61559243Sobrien	if (!(IntiFlags & ACPI_MADT_POLARITY_CONFORMS))
61659243Sobrien		lapic_set_lvt_polarity(apic_id, pin,
61759243Sobrien		    interrupt_polarity(IntiFlags, 0));
61859243Sobrien}
61959243Sobrien
62059243Sobrienstatic void
62159243Sobrienmadt_parse_local_nmi(ACPI_MADT_LOCAL_APIC_NMI *nmi)
62259243Sobrien{
62359243Sobrien
62459243Sobrien	madt_handle_local_nmi(nmi->ProcessorId == 0xff ? 0xffffffff :
62559243Sobrien	    nmi->ProcessorId, nmi->Lint, nmi->IntiFlags);
62659243Sobrien}
62759243Sobrien
62859243Sobrienstatic void
62959243Sobrienmadt_parse_local_x2apic_nmi(ACPI_MADT_LOCAL_X2APIC_NMI *nmi)
63059243Sobrien{
63159243Sobrien
63259243Sobrien	madt_handle_local_nmi(nmi->Uid, nmi->Lint, nmi->IntiFlags);
63359243Sobrien}
63459243Sobrien
63559243Sobrien/*
63659243Sobrien * Parse interrupt entries.
63759243Sobrien */
63859243Sobrienstatic void
63959243Sobrienmadt_parse_ints(ACPI_SUBTABLE_HEADER *entry, void *arg __unused)
64059243Sobrien{
64159243Sobrien
64259243Sobrien	switch (entry->Type) {
64359243Sobrien	case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE:
64459243Sobrien		madt_parse_interrupt_override(
64559243Sobrien			(ACPI_MADT_INTERRUPT_OVERRIDE *)entry);
64659243Sobrien		break;
64759243Sobrien	case ACPI_MADT_TYPE_NMI_SOURCE:
64859243Sobrien		madt_parse_nmi((ACPI_MADT_NMI_SOURCE *)entry);
64959243Sobrien		break;
65059243Sobrien	case ACPI_MADT_TYPE_LOCAL_APIC_NMI:
65159243Sobrien		madt_parse_local_nmi((ACPI_MADT_LOCAL_APIC_NMI *)entry);
65259243Sobrien		break;
65359243Sobrien	case ACPI_MADT_TYPE_LOCAL_X2APIC_NMI:
65459243Sobrien		madt_parse_local_x2apic_nmi(
65559243Sobrien		    (ACPI_MADT_LOCAL_X2APIC_NMI *)entry);
65659243Sobrien		break;
65759243Sobrien	}
65859243Sobrien}
65959243Sobrien
66059243Sobrien/*
66159243Sobrien * Setup per-CPU ACPI IDs.
66259243Sobrien */
66359243Sobrienstatic void
66459243Sobrienmadt_set_ids(void *dummy)
66559243Sobrien{
66659243Sobrien	struct lapic_info *la;
66759243Sobrien	struct pcpu *pc;
66859243Sobrien	u_int i;
66959243Sobrien
67059243Sobrien	if (madt == NULL)
67159243Sobrien		return;
67259243Sobrien	CPU_FOREACH(i) {
67359243Sobrien		pc = pcpu_find(i);
67459243Sobrien		KASSERT(pc != NULL, ("no pcpu data for CPU %u", i));
67559243Sobrien		la = &lapics[pc->pc_apic_id];
67659243Sobrien		if (!la->la_enabled)
67759243Sobrien			panic("APIC: CPU with APIC ID %u is not enabled",
67859243Sobrien			    pc->pc_apic_id);
67959243Sobrien		pc->pc_acpi_id = la->la_acpi_id;
68059243Sobrien		if (bootverbose)
68159243Sobrien			printf("APIC: CPU %u has ACPI ID %u\n", i,
68259243Sobrien			    la->la_acpi_id);
68359243Sobrien	}
68459243Sobrien}
68559243SobrienSYSINIT(madt_set_ids, SI_SUB_CPU, SI_ORDER_MIDDLE, madt_set_ids, NULL);
68659243Sobrien