Index: head/sys/i386/acpica/madt.c =================================================================== --- head/sys/i386/acpica/madt.c (revision 123132) +++ head/sys/i386/acpica/madt.c (revision 123133) @@ -1,667 +1,666 @@ /*- * Copyright (c) 2003 John Baldwin * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "acpi.h" #include #include #include #define NIOAPICS 32 /* Max number of I/O APICs */ #define NLAPICS 32 /* Max number of local APICs */ typedef void madt_entry_handler(APIC_HEADER *entry, void *arg); /* These two arrays are indexed by APIC IDs. */ struct ioapic_info { void *io_apic; UINT32 io_vector; } ioapics[NIOAPICS]; struct lapic_info { u_int la_present:1; u_int la_enabled:1; u_int la_apic_id:8; } lapics[NLAPICS + 1]; static APIC_TABLE *madt; static vm_paddr_t madt_physaddr; static vm_offset_t madt_length; MALLOC_DEFINE(M_MADT, "MADT Table", "ACPI MADT Table Items"); static u_char interrupt_polarity(UINT16 Polarity); static u_char interrupt_trigger(UINT16 TriggerMode); 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(APIC_HEADER *entry, void *arg); static void madt_parse_interrupt_override(INTERRUPT_SOURCE_OVERRIDE *intr); static void madt_parse_ints(APIC_HEADER *entry, void *arg __unused); static void madt_parse_local_nmi(LAPIC_NMI *nmi); static void madt_parse_nmi(NMI *nmi); static int madt_probe(void); static int madt_probe_cpus(void); static void madt_probe_cpus_handler(APIC_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: page 0 is used to map in the first page of each table * found via the RSDT or XSDT and pages 1 to n are used to map in the * RSDT or XSDT. 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; header = madt_map(pa, offset, sizeof(ACPI_TABLE_HEADER)); if (strncmp(header->Signature, sig, 4) != 0) { madt_unmap(header, sizeof(ACPI_TABLE_HEADER)); return (NULL); } length = header->Length; madt_unmap(header, sizeof(ACPI_TABLE_HEADER)); return (madt_map(pa, offset, length)); } 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_POINTER rsdp_ptr; RSDP_DESCRIPTOR *rsdp; RSDT_DESCRIPTOR *rsdt; XSDT_DESCRIPTOR *xsdt; int i, count; if (resource_disabled("acpi", 0)) return (ENXIO); /* * Map in the RSDP. Since ACPI uses AcpiOsMapMemory() which in turn * calls pmap_mapdev() to find the RSDP, we assume that we can use * pmap_mapdev() to map the RSDP. */ if (AcpiOsGetRootPointer(ACPI_LOGICAL_ADDRESSING, &rsdp_ptr) != AE_OK) return (ENXIO); #ifdef __i386__ KASSERT(rsdp_ptr.Pointer.Physical < KERNLOAD, ("RSDP too high")); #endif rsdp = pmap_mapdev(rsdp_ptr.Pointer.Physical, sizeof(RSDP_DESCRIPTOR)); if (rsdp == NULL) { if (bootverbose) printf("MADT: Failed to map RSDP\n"); return (ENXIO); } /* * For ACPI < 2.0, use the RSDT. For ACPI >= 2.0, use the XSDT. * We map the XSDT and 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) { xsdt = madt_map_table(rsdp->XsdtPhysicalAddress, 1, XSDT_SIG); 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, 1, RSDT_SIG); 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_unmapdev((vm_offset_t)rsdp, sizeof(RSDP_DESCRIPTOR)); 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); 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); /* XXX: Verify checksum? */ if (strncmp(table->Signature, APIC_SIG, 4) != 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, APIC_SIG); 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_mapdev(madt_physaddr, madt_length); lapic_init((uintptr_t)madt->LocalApicAddress); 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); } /* * Run through the MP table enumerating I/O APICs. */ static int madt_setup_io(void) { int i; /* 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); /* Third, we register all the I/O APIC's. */ for (i = 0; i < NIOAPICS; 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) +SYSINIT(madt_register, SI_SUB_CPU - 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) { APIC_HEADER *entry; u_char *p, *end; end = (u_char *)(madt) + madt->Header.Length; for (p = (u_char *)(madt + 1); p < end; ) { entry = (APIC_HEADER *)p; handler(entry, arg); p += entry->Length; } } static void madt_probe_cpus_handler(APIC_HEADER *entry, void *arg) { PROCESSOR_APIC *proc; struct lapic_info *la; switch (entry->Type) { case APIC_PROC: /* * 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 = (PROCESSOR_APIC *)entry; if (bootverbose) printf("MADT: Found CPU APIC ID %d ACPI ID %d: %s\n", proc->LocalApicId, proc->ProcessorApicId, proc->ProcessorEnabled ? "enabled" : "disabled"); if (proc->ProcessorApicId > NLAPICS) panic("%s: CPU ID %d too high", __func__, proc->ProcessorApicId); la = &lapics[proc->ProcessorApicId]; KASSERT(la->la_present == 0, ("Duplicate local ACPI ID %d", proc->ProcessorApicId)); la->la_present = 1; la->la_apic_id = proc->LocalApicId; if (proc->ProcessorEnabled) { la->la_enabled = 1; lapic_create(proc->LocalApicId, 0); } break; } } /* * Add an I/O APIC from an entry in the table. */ static void madt_parse_apics(APIC_HEADER *entry, void *arg __unused) { IO_APIC *apic; switch (entry->Type) { case APIC_IO: apic = (IO_APIC *)entry; if (bootverbose) printf("MADT: Found IO APIC ID %d, Vector %d at %p\n", apic->IoApicId, apic->Vector, (void *)(uintptr_t)apic->IoApicAddress); if (apic->IoApicId >= NIOAPICS) panic("%s: I/O APIC ID %d too high", __func__, apic->IoApicId); if (ioapics[apic->IoApicId].io_apic != NULL) panic("%s: Double APIC ID %d", __func__, apic->IoApicId); ioapics[apic->IoApicId].io_apic = ioapic_create( (uintptr_t)apic->IoApicAddress, apic->IoApicId, apic->Vector); ioapics[apic->IoApicId].io_vector = apic->Vector; break; default: break; } } /* * Determine properties of an interrupt source. Note that for ACPI, * these are only used for ISA interrupts, so we assume ISA bus values * (Active Hi, Edge Triggered) for conforming values. */ static u_char interrupt_polarity(UINT16 Polarity) { switch (Polarity) { case APIC_POLARITY_CONFORM: case APIC_POLARITY_ACTIVEHI: return (1); case APIC_POLARITY_ACTIVELO: return (0); default: panic("Bogus Interrupt Polarity"); } } static u_char interrupt_trigger(UINT16 TriggerMode) { switch (TriggerMode) { case APIC_TRIGGER_CONFORM: case APIC_TRIGGER_EDGE: return (1); case APIC_TRIGGER_LEVEL: return (0); 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) { if (!lapics[acpi_id].la_present) return (ENOENT); *apic_id = lapics[acpi_id].la_apic_id; if (lapics[acpi_id].la_enabled) return (0); else return (ENXIO); } /* * 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 < NIOAPICS; 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(INTERRUPT_SOURCE_OVERRIDE *intr) { void *new_ioapic, *old_ioapic; u_int new_pin, old_pin; if (bootverbose) printf("MADT: intr override: source %u, irq %u\n", intr->Source, intr->GlobalSystemInterrupt); KASSERT(intr->Bus == 0, ("bus for interrupt overrides must be zero")); if (madt_find_interrupt(intr->GlobalSystemInterrupt, &new_ioapic, &new_pin) != 0) { printf("MADT: Could not find APIC for vector %d (IRQ %d)\n", intr->GlobalSystemInterrupt, intr->Source); return; } if (intr->Source != intr->GlobalSystemInterrupt) { /* XXX: This assumes that the SCI uses IRQ 9. */ if (intr->GlobalSystemInterrupt > 15 && intr->Source == 9) acpi_OverrideInterruptLevel( intr->GlobalSystemInterrupt); else ioapic_remap_vector(new_ioapic, new_pin, intr->Source); if (madt_find_interrupt(intr->Source, &old_ioapic, &old_pin) != 0) printf("MADT: Could not find APIC for source IRQ %d\n", intr->Source); else if (ioapic_get_vector(old_ioapic, old_pin) == intr->Source) ioapic_disable_pin(old_ioapic, old_pin); } ioapic_set_triggermode(new_ioapic, new_pin, interrupt_trigger(intr->TriggerMode)); ioapic_set_polarity(new_ioapic, new_pin, interrupt_polarity(intr->Polarity)); } /* * Parse an entry for an NMI routed to an IO APIC. */ static void madt_parse_nmi(NMI *nmi) { void *ioapic; u_int pin; if (madt_find_interrupt(nmi->GlobalSystemInterrupt, &ioapic, &pin) != 0) { printf("MADT: Could not find APIC for vector %d\n", nmi->GlobalSystemInterrupt); return; } ioapic_set_nmi(ioapic, pin); if (nmi->TriggerMode != APIC_TRIGGER_CONFORM) ioapic_set_triggermode(ioapic, pin, interrupt_trigger(nmi->TriggerMode)); if (nmi->Polarity != APIC_TRIGGER_CONFORM) ioapic_set_polarity(ioapic, pin, interrupt_polarity(nmi->Polarity)); } /* * Parse an entry for an NMI routed to a local APIC LVT pin. */ static void madt_parse_local_nmi(LAPIC_NMI *nmi) { u_int apic_id, pin; if (nmi->ProcessorApicId == 0xff) apic_id = APIC_ID_ALL; else if (madt_find_cpu(nmi->ProcessorApicId, &apic_id) != 0) { if (bootverbose) printf("MADT: Ignoring local NMI routed to ACPI CPU %u\n", nmi->ProcessorApicId); return; } if (nmi->LINTPin == 0) pin = LVT_LINT0; else pin = LVT_LINT1; lapic_set_lvt_mode(apic_id, pin, APIC_LVT_DM_NMI); if (nmi->TriggerMode != APIC_TRIGGER_CONFORM) lapic_set_lvt_triggermode(apic_id, pin, interrupt_trigger(nmi->TriggerMode)); if (nmi->Polarity != APIC_POLARITY_CONFORM) lapic_set_lvt_polarity(apic_id, pin, interrupt_polarity(nmi->Polarity)); } /* * Parse interrupt entries. */ static void madt_parse_ints(APIC_HEADER *entry, void *arg __unused) { switch (entry->Type) { case APIC_INTERRUPT_SOURCE_OVERRIDE: madt_parse_interrupt_override( (INTERRUPT_SOURCE_OVERRIDE *)entry); break; case APIC_NMI: madt_parse_nmi((NMI *)entry); break; case APIC_LOCAL_APIC_NMI: madt_parse_local_nmi((LAPIC_NMI *)entry); break; } } /* * Setup per-CPU ACPI IDs. */ static void madt_set_ids(void *dummy) { struct pcpu *pc; u_int i, j; if (madt == NULL) return; - for (i = 0; i < MAXCPU; i++) { + for (i = 0; i <= mp_maxid; i++) { if (CPU_ABSENT(i)) continue; pc = pcpu_find(i); KASSERT(pc != NULL, ("no pcpu data for CPU %d", i)); for (j = 0; j < NLAPICS + 1; j++) { if (!lapics[j].la_present || !lapics[j].la_enabled) continue; if (lapics[j].la_apic_id == pc->pc_apic_id) { pc->pc_acpi_id = j; if (bootverbose) printf("APIC: CPU %u has ACPI ID %u\n", i, j); break; } } if (j == NLAPICS + 1) panic("Unable to find ACPI ID for CPU %d", i); } } SYSINIT(madt_set_ids, SI_SUB_CPU, SI_ORDER_ANY, madt_set_ids, NULL) Index: head/sys/i386/i386/local_apic.c =================================================================== --- head/sys/i386/i386/local_apic.c (revision 123132) +++ head/sys/i386/i386/local_apic.c (revision 123133) @@ -1,767 +1,759 @@ /*- * Copyright (c) 2003 John Baldwin * Copyright (c) 1996, by Steve Passe * 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. The name of the developer may NOT be used to endorse or promote products * derived from this software without specific prior written permission. * 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. */ /* * Local APIC support on Pentium and later processors. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * We can handle up to 60 APICs via our logical cluster IDs, but currently * the physical IDs on Intel processors up to the Pentium 4 are limited to * 16. */ #define MAX_APICID 16 /* Sanity checks on IDT vectors. */ CTASSERT(APIC_IO_INTS + APIC_NUM_IOINTS <= APIC_LOCAL_INTS); CTASSERT(IPI_STOP < APIC_SPURIOUS_INT); /* * Support for local APICs. Local APICs manage interrupts on each * individual processor as opposed to I/O APICs which receive interrupts * from I/O devices and then forward them on to the local APICs. * * Local APICs can also send interrupts to each other thus providing the * mechanism for IPIs. */ struct lvt { u_int lvt_edgetrigger:1; u_int lvt_activehi:1; u_int lvt_masked:1; u_int lvt_active:1; u_int lvt_mode:16; u_int lvt_vector:8; }; struct lapic { struct lvt la_lvts[LVT_MAX + 1]; u_int la_id:8; u_int la_cluster:4; u_int la_cluster_id:2; u_int la_present:1; } static lapics[MAX_APICID]; /* XXX: should thermal be an NMI? */ /* Global defaults for local APIC LVT entries. */ static struct lvt lvts[LVT_MAX + 1] = { { 1, 1, 1, 1, APIC_LVT_DM_EXTINT, 0 }, /* LINT0: masked ExtINT */ { 1, 1, 0, 1, APIC_LVT_DM_NMI, 0 }, /* LINT1: NMI */ { 1, 1, 1, 1, APIC_LVT_DM_FIXED, 0 }, /* Timer: needs a vector */ { 1, 1, 1, 1, APIC_LVT_DM_FIXED, 0 }, /* Error: needs a vector */ { 1, 1, 1, 1, APIC_LVT_DM_FIXED, 0 }, /* PMC */ { 1, 1, 1, 1, APIC_LVT_DM_FIXED, 0 }, /* Thermal: needs a vector */ }; static inthand_t *ioint_handlers[] = { NULL, /* 0 - 31 */ IDTVEC(apic_isr1), /* 32 - 63 */ IDTVEC(apic_isr2), /* 64 - 95 */ IDTVEC(apic_isr3), /* 96 - 127 */ IDTVEC(apic_isr4), /* 128 - 159 */ IDTVEC(apic_isr5), /* 160 - 191 */ IDTVEC(apic_isr6), /* 192 - 223 */ IDTVEC(apic_isr7), /* 224 - 255 */ }; volatile lapic_t *lapic; static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value) { struct lvt *lvt; KASSERT(pin <= LVT_MAX, ("%s: pin %u out of range", __func__, pin)); if (la->la_lvts[pin].lvt_active) lvt = &la->la_lvts[pin]; else lvt = &lvts[pin]; value &= ~(APIC_LVT_M | APIC_LVT_TM | APIC_LVT_IIPP | APIC_LVT_DM | APIC_LVT_VECTOR); if (lvt->lvt_edgetrigger == 0) value |= APIC_LVT_TM; if (lvt->lvt_activehi == 0) value |= APIC_LVT_IIPP_INTALO; if (lvt->lvt_masked) value |= APIC_LVT_M; value |= lvt->lvt_mode; switch (lvt->lvt_mode) { case APIC_LVT_DM_NMI: case APIC_LVT_DM_SMI: case APIC_LVT_DM_INIT: case APIC_LVT_DM_EXTINT: if (!lvt->lvt_edgetrigger) { printf("lapic%u: Forcing LINT%u to edge trigger\n", la->la_id, pin); value |= APIC_LVT_TM; } /* Use a vector of 0. */ break; case APIC_LVT_DM_FIXED: #if 0 value |= lvt->lvt_vector; #else panic("Fixed LINT pins not supported"); #endif break; default: panic("bad APIC LVT delivery mode: %#x\n", value); } return (value); } /* * Map the local APIC and setup necessary interrupt vectors. */ void lapic_init(uintptr_t addr) { u_int32_t value; /* Map the local APIC and setup the spurious interrupt handler. */ KASSERT(trunc_page(addr) == addr, ("local APIC not aligned on a page boundary")); lapic = (lapic_t *)pmap_mapdev(addr, sizeof(lapic_t)); setidt(APIC_SPURIOUS_INT, IDTVEC(spuriousint), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); /* Perform basic initialization of the BSP's local APIC. */ value = lapic->svr; value &= ~(APIC_SVR_VECTOR | APIC_SVR_FOCUS); value |= (APIC_SVR_FEN | APIC_SVR_SWEN | APIC_SPURIOUS_INT); lapic->svr = value; /* Set BSP's per-CPU local APIC ID. */ PCPU_SET(apic_id, lapic_id()); /* XXX: timer/error/thermal interrupts */ } /* * Create a local APIC instance. */ void lapic_create(u_int apic_id, int boot_cpu) { int i; if (apic_id > MAX_APICID) { printf("APIC: Ignoring local APIC with ID %d\n", apic_id); if (boot_cpu) panic("Can't ignore BSP"); return; } KASSERT(!lapics[apic_id].la_present, ("duplicate local APIC %u", apic_id)); /* * Assume no local LVT overrides and a cluster of 0 and * intra-cluster ID of 0. */ lapics[apic_id].la_present = 1; lapics[apic_id].la_id = apic_id; for (i = 0; i < LVT_MAX; i++) { lapics[apic_id].la_lvts[i] = lvts[i]; lapics[apic_id].la_lvts[i].lvt_active = 0; } #ifdef SMP cpu_add(apic_id, boot_cpu); #endif } /* * Dump contents of local APIC registers */ void lapic_dump(const char* str) { printf("cpu%d %s:\n", PCPU_GET(cpuid), str); printf(" ID: 0x%08x VER: 0x%08x LDR: 0x%08x DFR: 0x%08x\n", lapic->id, lapic->version, lapic->ldr, lapic->dfr); printf(" lint0: 0x%08x lint1: 0x%08x TPR: 0x%08x SVR: 0x%08x\n", lapic->lvt_lint0, lapic->lvt_lint1, lapic->tpr, lapic->svr); } void lapic_enable_intr(u_int irq) { u_int vector; vector = apic_irq_to_idt(irq); KASSERT(vector != IDT_SYSCALL, ("Attempt to overwrite syscall entry")); KASSERT(ioint_handlers[vector / 32] != NULL, ("No ISR handler for IRQ %u", irq)); setidt(vector, ioint_handlers[vector / 32], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } void lapic_setup(void) { struct lapic *la; u_int32_t value, maxlvt; register_t eflags; la = &lapics[lapic_id()]; KASSERT(la->la_present, ("missing APIC structure")); eflags = intr_disable(); maxlvt = (lapic->version & APIC_VER_MAXLVT) >> MAXLVTSHIFT; /* Program LINT[01] LVT entries. */ lapic->lvt_lint0 = lvt_mode(la, LVT_LINT0, lapic->lvt_lint0); lapic->lvt_lint1 = lvt_mode(la, LVT_LINT1, lapic->lvt_lint1); /* XXX: more LVT entries */ /* Clear the TPR. */ value = lapic->tpr; value &= ~APIC_TPR_PRIO; lapic->tpr = value; /* Use the cluster model for logical IDs. */ value = lapic->dfr; value &= ~APIC_DFR_MODEL_MASK; value |= APIC_DFR_MODEL_CLUSTER; lapic->dfr = value; /* Set this APIC's logical ID. */ value = lapic->ldr; value &= ~APIC_ID_MASK; value |= (la->la_cluster << APIC_ID_CLUSTER_SHIFT | 1 << la->la_cluster_id) << APIC_ID_SHIFT; lapic->ldr = value; /* Setup spurious vector and enable the local APIC. */ value = lapic->svr; value &= ~(APIC_SVR_VECTOR | APIC_SVR_FOCUS); value |= (APIC_SVR_FEN | APIC_SVR_SWEN | APIC_SPURIOUS_INT); lapic->svr = value; intr_restore(eflags); } void lapic_disable(void) { uint32_t value; /* Software disable the local APIC. */ value = lapic->svr; value &= ~APIC_SVR_SWEN; lapic->svr = value; } int lapic_id(void) { KASSERT(lapic != NULL, ("local APIC is not mapped")); return (lapic->id >> APIC_ID_SHIFT); } int lapic_intr_pending(u_int vector) { volatile u_int32_t *irr; /* * The IRR registers are an array of 128-bit registers each of * which only describes 32 interrupts in the low 32 bits.. Thus, * we divide the vector by 32 to get the 128-bit index. We then * multiply that index by 4 to get the equivalent index from * treating the IRR as an array of 32-bit registers. Finally, we * modulus the vector by 32 to determine the individual bit to * test. */ irr = &lapic->irr0; return (irr[(vector / 32) * 4] & 1 << (vector % 32)); } void lapic_set_logical_id(u_int apic_id, u_int cluster, u_int cluster_id) { struct lapic *la; KASSERT(lapics[apic_id].la_present, ("%s: APIC %u doesn't exist", __func__, apic_id)); KASSERT(cluster <= APIC_MAX_CLUSTER, ("%s: cluster %u too big", __func__, cluster)); KASSERT(cluster_id <= APIC_MAX_INTRACLUSTER_ID, ("%s: intra cluster id %u too big", __func__, cluster_id)); la = &lapics[apic_id]; la->la_cluster = cluster; la->la_cluster_id = cluster_id; } int lapic_set_lvt_mask(u_int apic_id, u_int pin, u_char masked) { if (pin > LVT_MAX) return (EINVAL); if (apic_id == APIC_ID_ALL) { lvts[pin].lvt_masked = masked; if (bootverbose) printf("lapic:"); } else { KASSERT(lapics[apic_id].la_present, ("%s: missing APIC %u", __func__, apic_id)); lapics[apic_id].la_lvts[pin].lvt_masked = masked; lapics[apic_id].la_lvts[pin].lvt_active = 1; if (bootverbose) printf("lapic%u:", apic_id); } if (bootverbose) printf(" LINT%u %s\n", pin, masked ? "masked" : "unmasked"); return (0); } int lapic_set_lvt_mode(u_int apic_id, u_int pin, u_int32_t mode) { struct lvt *lvt; if (pin > LVT_MAX) return (EINVAL); if (apic_id == APIC_ID_ALL) { lvt = &lvts[pin]; if (bootverbose) printf("lapic:"); } else { KASSERT(lapics[apic_id].la_present, ("%s: missing APIC %u", __func__, apic_id)); lvt = &lapics[apic_id].la_lvts[pin]; lvt->lvt_active = 1; if (bootverbose) printf("lapic%u:", apic_id); } lvt->lvt_mode = mode; switch (mode) { case APIC_LVT_DM_NMI: case APIC_LVT_DM_SMI: case APIC_LVT_DM_INIT: case APIC_LVT_DM_EXTINT: lvt->lvt_edgetrigger = 1; lvt->lvt_activehi = 1; if (mode == APIC_LVT_DM_EXTINT) lvt->lvt_masked = 1; else lvt->lvt_masked = 0; break; default: panic("Unsupported delivery mode: 0x%x\n", mode); } if (bootverbose) { printf(" Routing "); switch (mode) { case APIC_LVT_DM_NMI: printf("NMI"); break; case APIC_LVT_DM_SMI: printf("SMI"); break; case APIC_LVT_DM_INIT: printf("INIT"); break; case APIC_LVT_DM_EXTINT: printf("ExtINT"); break; } printf(" -> LINT%u\n", pin); } return (0); } int lapic_set_lvt_polarity(u_int apic_id, u_int pin, u_char activehi) { if (pin > LVT_MAX) return (EINVAL); if (apic_id == APIC_ID_ALL) { lvts[pin].lvt_activehi = activehi; if (bootverbose) printf("lapic:"); } else { KASSERT(lapics[apic_id].la_present, ("%s: missing APIC %u", __func__, apic_id)); lapics[apic_id].la_lvts[pin].lvt_active = 1; lapics[apic_id].la_lvts[pin].lvt_activehi = activehi; if (bootverbose) printf("lapic%u:", apic_id); } if (bootverbose) printf(" LINT%u polarity: active-%s\n", pin, activehi ? "hi" : "lo"); return (0); } int lapic_set_lvt_triggermode(u_int apic_id, u_int pin, u_char edgetrigger) { if (pin > LVT_MAX) return (EINVAL); if (apic_id == APIC_ID_ALL) { lvts[pin].lvt_edgetrigger = edgetrigger; if (bootverbose) printf("lapic:"); } else { KASSERT(lapics[apic_id].la_present, ("%s: missing APIC %u", __func__, apic_id)); lapics[apic_id].la_lvts[pin].lvt_edgetrigger = edgetrigger; lapics[apic_id].la_lvts[pin].lvt_active = 1; if (bootverbose) printf("lapic%u:", apic_id); } if (bootverbose) printf(" LINT%u trigger: %s\n", pin, edgetrigger ? "edge" : "level"); return (0); } void lapic_eoi(void) { lapic->eoi = 0; } void lapic_handle_intr(struct intrframe frame) { struct intsrc *isrc; if (frame.if_vec == -1) panic("Couldn't get vector from ISR!"); isrc = intr_lookup_source(apic_idt_to_irq(frame.if_vec)); intr_execute_handlers(isrc, &frame); } /* Translate between IDT vectors and IRQ vectors. */ u_int apic_irq_to_idt(u_int irq) { u_int vector; KASSERT(irq < NUM_IO_INTS, ("Invalid IRQ %u", irq)); vector = irq + APIC_IO_INTS; if (vector >= IDT_SYSCALL) vector++; return (vector); } u_int apic_idt_to_irq(u_int vector) { KASSERT(vector >= APIC_IO_INTS && vector != IDT_SYSCALL && vector <= APIC_IO_INTS + NUM_IO_INTS, ("Vector %u does not map to an IRQ line", vector)); if (vector > IDT_SYSCALL) vector--; return (vector - APIC_IO_INTS); } /* * APIC probing support code. This includes code to manage enumerators. */ static SLIST_HEAD(, apic_enumerator) enumerators = SLIST_HEAD_INITIALIZER(enumerators); static struct apic_enumerator *best_enum; void apic_register_enumerator(struct apic_enumerator *enumerator) { #ifdef INVARIANTS struct apic_enumerator *apic_enum; SLIST_FOREACH(apic_enum, &enumerators, apic_next) { if (apic_enum == enumerator) panic("%s: Duplicate register of %s", __func__, enumerator->apic_name); } #endif SLIST_INSERT_HEAD(&enumerators, enumerator, apic_next); } /* - * We have to look for CPU's very, very early because certain subsystems - * want to know how many CPU's we have extremely early on in the boot - * process. + * Probe the APIC enumerators, enumerate CPUs, and initialize the + * local APIC. */ static void apic_init(void *dummy __unused) { struct apic_enumerator *enumerator; + uint64_t apic_base; int retval, best; /* We only support built in local APICs. */ if (!(cpu_feature & CPUID_APIC)) return; + /* Don't probe if APIC mode is disabled. */ + if (resource_disabled("apic", 0)) + return; + /* First, probe all the enumerators to find the best match. */ best_enum = NULL; best = 0; SLIST_FOREACH(enumerator, &enumerators, apic_next) { retval = enumerator->apic_probe(); if (retval > 0) continue; if (best_enum == NULL || best < retval) { best_enum = enumerator; best = retval; } } if (best_enum == NULL) { if (bootverbose) printf("APIC: Could not find any APICs.\n"); return; } if (bootverbose) printf("APIC: Using the %s enumerator.\n", best_enum->apic_name); - /* Second, probe the CPU's in the system. */ - retval = best_enum->apic_probe_cpus(); - if (retval != 0) - printf("%s: Failed to probe CPUs: returned %d\n", - best_enum->apic_name, retval); -} -SYSINIT(apic_init, SI_SUB_TUNABLES - 1, SI_ORDER_SECOND, apic_init, NULL) - -/* - * Setup the local APIC. We have to do this prior to starting up the APs - * in the SMP case. - */ -static void -apic_setup_local(void *dummy __unused) -{ - int retval; - uint64_t apic_base; - - if (best_enum == NULL) - return; /* * To work around an errata, we disable the local APIC on some * CPUs during early startup. We need to turn the local APIC back * on on such CPUs now. */ if (cpu == CPU_686 && strcmp(cpu_vendor, "GenuineIntel") == 0 && (cpu_id & 0xff0) == 0x610) { apic_base = rdmsr(MSR_APICBASE); apic_base |= APICBASE_ENABLED; wrmsr(MSR_APICBASE, apic_base); } + + /* Second, probe the CPU's in the system. */ + retval = best_enum->apic_probe_cpus(); + if (retval != 0) + printf("%s: Failed to probe CPUs: returned %d\n", + best_enum->apic_name, retval); + + /* Third, initialize the local APIC. */ retval = best_enum->apic_setup_local(); if (retval != 0) printf("%s: Failed to setup the local APIC: returned %d\n", best_enum->apic_name, retval); } -SYSINIT(apic_setup_local, SI_SUB_CPU, SI_ORDER_FIRST, apic_setup_local, NULL) +SYSINIT(apic_init, SI_SUB_CPU, SI_ORDER_FIRST, apic_init, NULL) /* * Setup the I/O APICs. */ static void apic_setup_io(void *dummy __unused) { int retval; if (best_enum == NULL) return; retval = best_enum->apic_setup_io(); if (retval != 0) printf("%s: Failed to setup I/O APICs: returned %d\n", best_enum->apic_name, retval); /* * Finish setting up the local APIC on the BSP once we know how to * properly program the LINT pins. */ lapic_setup(); if (bootverbose) lapic_dump("BSP"); } SYSINIT(apic_setup_io, SI_SUB_INTR, SI_ORDER_SECOND, apic_setup_io, NULL) #ifdef SMP /* * Inter Processor Interrupt functions. The lapic_ipi_*() functions are * private the sys/i386 code. The public interface for the rest of the * kernel is defined in mp_machdep.c. */ #define DETECT_DEADLOCK int lapic_ipi_wait(int delay) { int x, incr; /* * Wait delay loops for IPI to be sent. This is highly bogus * since this is sensitive to CPU clock speed. If delay is * -1, we wait forever. */ if (delay == -1) { incr = 0; delay = 1; } else incr = 1; for (x = 0; x < delay; x += incr) { if ((lapic->icr_lo & APIC_DELSTAT_MASK) == APIC_DELSTAT_IDLE) return (1); ia32_pause(); } return (0); } void lapic_ipi_raw(register_t icrlo, u_int dest) { register_t value, eflags; /* XXX: Need more sanity checking of icrlo? */ KASSERT(lapic != NULL, ("%s called too early", __func__)); KASSERT((dest & ~(APIC_ID_MASK >> APIC_ID_SHIFT)) == 0, ("%s: invalid dest field", __func__)); KASSERT((icrlo & APIC_ICRLO_RESV_MASK) == 0, ("%s: reserved bits set in ICR LO register", __func__)); /* Set destination in ICR HI register if it is being used. */ eflags = intr_disable(); if ((icrlo & APIC_DEST_MASK) == APIC_DEST_DESTFLD) { value = lapic->icr_hi; value &= ~APIC_ID_MASK; value |= dest << APIC_ID_SHIFT; lapic->icr_hi = value; } /* Program the contents of the IPI and dispatch it. */ value = lapic->icr_lo; value &= APIC_ICRLO_RESV_MASK; value |= icrlo; lapic->icr_lo = value; intr_restore(eflags); } #ifdef DETECT_DEADLOCK #define BEFORE_SPIN 1000000 #define AFTER_SPIN 1000 #endif void lapic_ipi_vectored(u_int vector, int dest) { register_t icrlo, destfield; KASSERT((vector & ~APIC_VECTOR_MASK) == 0, ("%s: invalid vector %d", __func__, vector)); icrlo = vector | APIC_DELMODE_FIXED | APIC_DESTMODE_PHY | APIC_LEVEL_DEASSERT | APIC_TRIGMOD_EDGE; destfield = 0; switch (dest) { case APIC_IPI_DEST_SELF: icrlo |= APIC_DEST_SELF; break; case APIC_IPI_DEST_ALL: icrlo |= APIC_DEST_ALLISELF; break; case APIC_IPI_DEST_OTHERS: icrlo |= APIC_DEST_ALLESELF; break; default: KASSERT((dest & ~(APIC_ID_MASK >> APIC_ID_SHIFT)) == 0, ("%s: invalid destination 0x%x", __func__, dest)); destfield = dest; } #ifdef DETECT_DEADLOCK /* Check for an earlier stuck IPI. */ if (!lapic_ipi_wait(BEFORE_SPIN)) panic("APIC: Previous IPI is stuck"); #endif lapic_ipi_raw(icrlo, destfield); #ifdef DETECT_DEADLOCK /* Wait for IPI to be delivered. */ if (!lapic_ipi_wait(AFTER_SPIN)) { #ifdef needsattention /* * XXX FIXME: * * The above function waits for the message to actually be * delivered. It breaks out after an arbitrary timeout * since the message should eventually be delivered (at * least in theory) and that if it wasn't we would catch * the failure with the check above when the next IPI is * sent. * * We could skiip this wait entirely, EXCEPT it probably * protects us from other routines that assume that the * message was delivered and acted upon when this function * returns. */ printf("APIC: IPI might be stuck\n"); #else /* !needsattention */ /* Wait until mesage is sent without a timeout. */ while (lapic->icr_lo & APIC_DELSTAT_PEND) ia32_pause(); #endif /* needsattention */ } #endif /* DETECT_DEADLOCK */ } #endif /* SMP */ Index: head/sys/i386/i386/mptable.c =================================================================== --- head/sys/i386/i386/mptable.c (revision 123132) +++ head/sys/i386/i386/mptable.c (revision 123133) @@ -1,968 +1,968 @@ /*- * Copyright (c) 2003 John Baldwin * Copyright (c) 1996, by Steve Passe * 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. The name of the developer may NOT 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 __FBSDID("$FreeBSD$"); #include "opt_mptable_force_htt.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* EISA Edge/Level trigger control registers */ #define ELCR0 0x4d0 /* eisa irq 0-7 */ #define ELCR1 0x4d1 /* eisa irq 8-15 */ /* string defined by the Intel MP Spec as identifying the MP table */ #define MP_SIG 0x5f504d5f /* _MP_ */ #define NAPICID 32 /* Max number of I/O APIC's */ #ifdef PC98 #define BIOS_BASE (0xe8000) #define BIOS_SIZE (0x18000) #else #define BIOS_BASE (0xf0000) #define BIOS_SIZE (0x10000) #endif #define BIOS_COUNT (BIOS_SIZE/4) typedef void mptable_entry_handler(u_char *entry, void *arg); static basetable_entry basetable_entry_types[] = { {0, 20, "Processor"}, {1, 8, "Bus"}, {2, 8, "I/O APIC"}, {3, 8, "I/O INT"}, {4, 8, "Local INT"} }; typedef struct BUSDATA { u_char bus_id; enum busTypes bus_type; } bus_datum; typedef struct INTDATA { u_char int_type; u_short int_flags; u_char src_bus_id; u_char src_bus_irq; u_char dst_apic_id; u_char dst_apic_int; u_char int_vector; } io_int, local_int; typedef struct BUSTYPENAME { u_char type; char name[7]; } bus_type_name; /* From MP spec v1.4, table 4-8. */ static bus_type_name bus_type_table[] = { {UNKNOWN_BUSTYPE, "CBUS "}, {UNKNOWN_BUSTYPE, "CBUSII"}, {EISA, "EISA "}, {UNKNOWN_BUSTYPE, "FUTURE"}, {UNKNOWN_BUSTYPE, "INTERN"}, {ISA, "ISA "}, {UNKNOWN_BUSTYPE, "MBI "}, {UNKNOWN_BUSTYPE, "MBII "}, {MCA, "MCA "}, {UNKNOWN_BUSTYPE, "MPI "}, {UNKNOWN_BUSTYPE, "MPSA "}, {UNKNOWN_BUSTYPE, "NUBUS "}, {PCI, "PCI "}, {UNKNOWN_BUSTYPE, "PCMCIA"}, {UNKNOWN_BUSTYPE, "TC "}, {UNKNOWN_BUSTYPE, "VL "}, {UNKNOWN_BUSTYPE, "VME "}, {UNKNOWN_BUSTYPE, "XPRESS"} }; /* From MP spec v1.4, table 5-1. */ static int default_data[7][5] = { /* nbus, id0, type0, id1, type1 */ {1, 0, ISA, 255, NOBUS}, {1, 0, EISA, 255, NOBUS}, {1, 0, EISA, 255, NOBUS}, {1, 0, MCA, 255, NOBUS}, {2, 0, ISA, 1, PCI}, {2, 0, EISA, 1, PCI}, {2, 0, MCA, 1, PCI} }; struct pci_probe_table_args { u_char bus; u_char found; }; struct pci_route_interrupt_args { u_char bus; /* Source bus. */ u_char irq; /* Source slot:pin. */ int vector; /* Return value. */ }; static mpfps_t mpfps; static mpcth_t mpct; static void *ioapics[NAPICID]; static bus_datum *busses; static int mptable_nioapics, mptable_nbusses, mptable_maxbusid; static int pci0 = -1; MALLOC_DEFINE(M_MPTABLE, "MP Table", "MP Table Items"); static u_char conforming_polarity(u_char src_bus); static u_char conforming_trigger(u_char src_bus, u_char src_bus_irq); static u_char intentry_polarity(int_entry_ptr intr); static u_char intentry_trigger(int_entry_ptr intr); static int lookup_bus_type(char *name); static void mptable_count_items(void); static void mptable_count_items_handler(u_char *entry, void *arg); #ifdef MPTABLE_FORCE_HTT static void mptable_hyperthread_fixup(u_int id_mask); #endif static void mptable_parse_apics_and_busses(void); static void mptable_parse_apics_and_busses_handler(u_char *entry, void *arg); static void mptable_parse_ints(void); static void mptable_parse_ints_handler(u_char *entry, void *arg); static void mptable_parse_io_int(int_entry_ptr intr); static void mptable_parse_local_int(int_entry_ptr intr); static void mptable_pci_probe_table_handler(u_char *entry, void *arg); static void mptable_pci_route_interrupt_handler(u_char *entry, void *arg); static void mptable_pci_setup(void); static int mptable_probe(void); static int mptable_probe_cpus(void); static void mptable_probe_cpus_handler(u_char *entry, void *arg __unused); static void mptable_register(void *dummy); static int mptable_setup_local(void); static int mptable_setup_io(void); static void mptable_walk_table(mptable_entry_handler *handler, void *arg); static int search_for_sig(u_int32_t target, int count); static struct apic_enumerator mptable_enumerator = { "MPTable", mptable_probe, mptable_probe_cpus, mptable_setup_local, mptable_setup_io }; /* * look for the MP spec signature */ static int search_for_sig(u_int32_t target, int count) { int x; u_int32_t *addr = (u_int32_t *) (KERNBASE + target); for (x = 0; x < count; x += 4) if (addr[x] == MP_SIG) /* make array index a byte index */ return (target + (x * sizeof(u_int32_t))); return (-1); } static int lookup_bus_type(char *name) { int x; for (x = 0; x < MAX_BUSTYPE; ++x) if (strncmp(bus_type_table[x].name, name, 6) == 0) return (bus_type_table[x].type); return (UNKNOWN_BUSTYPE); } /* * Look for an Intel MP spec table (ie, SMP capable hardware). */ static int mptable_probe(void) { int x; u_long segment; u_int32_t target; /* see if EBDA exists */ if ((segment = (u_long) * (u_short *) (KERNBASE + 0x40e)) != 0) { /* search first 1K of EBDA */ target = (u_int32_t) (segment << 4); if ((x = search_for_sig(target, 1024 / 4)) >= 0) goto found; } else { /* last 1K of base memory, effective 'top of base' passed in */ target = (u_int32_t) ((basemem * 1024) - 0x400); if ((x = search_for_sig(target, 1024 / 4)) >= 0) goto found; } /* search the BIOS */ target = (u_int32_t) BIOS_BASE; if ((x = search_for_sig(target, BIOS_COUNT)) >= 0) goto found; /* nothing found */ return (ENXIO); found: mpfps = (mpfps_t)(KERNBASE + x); /* Map in the configuration table if it exists. */ if (mpfps->config_type != 0) mpct = NULL; else { if ((uintptr_t)mpfps->pap >= 1024 * 1024) { printf("%s: Unable to map MP Configuration Table\n", __func__); return (ENXIO); } mpct = (mpcth_t)(KERNBASE + (uintptr_t)mpfps->pap); if (mpct->base_table_length + (uintptr_t)mpfps->pap >= 1024 * 1024) { printf("%s: Unable to map end of MP Config Table\n", __func__); return (ENXIO); } if (mpct->signature[0] != 'P' || mpct->signature[1] != 'C' || mpct->signature[2] != 'M' || mpct->signature[3] != 'P') { printf("%s: MP Config Table has bad signature: %c%c%c%c\n", __func__, mpct->signature[0], mpct->signature[1], mpct->signature[2], mpct->signature[3]); return (ENXIO); } if (bootverbose) printf( "MP Configuration Table version 1.%d found at %p\n", mpct->spec_rev, mpct); } return (-100); } /* * Run through the MP table enumerating CPUs. */ static int mptable_probe_cpus(void) { u_int cpu_mask; /* Is this a pre-defined config? */ if (mpfps->config_type != 0) { lapic_create(0, 1); lapic_create(1, 0); } else { cpu_mask = 0; mptable_walk_table(mptable_probe_cpus_handler, &cpu_mask); #ifdef MPTABLE_FORCE_HTT mptable_hyperthread_fixup(cpu_mask); #endif } return (0); } /* * Initialize the local APIC on the BSP. */ static int mptable_setup_local(void) { /* Is this a pre-defined config? */ printf("MPTable: <"); if (mpfps->config_type != 0) { lapic_init(DEFAULT_APIC_BASE); printf("Preset Config %d", mpfps->config_type); } else { lapic_init((uintptr_t)mpct->apic_address); printf("%.*s %.*s", (int)sizeof(mpct->oem_id), mpct->oem_id, (int)sizeof(mpct->product_id), mpct->product_id); } printf(">\n"); return (0); } /* * Run through the MP table enumerating I/O APICs. */ static int mptable_setup_io(void) { int i; u_char byte; /* First, we count individual items and allocate arrays. */ mptable_count_items(); busses = malloc((mptable_maxbusid + 1) * sizeof(bus_datum), M_MPTABLE, M_WAITOK); for (i = 0; i <= mptable_maxbusid; i++) busses[i].bus_type = NOBUS; /* Second, we run through adding I/O APIC's and busses. */ mptable_parse_apics_and_busses(); /* Third, we run through the table tweaking interrupt sources. */ mptable_parse_ints(); /* Fourth, we register all the I/O APIC's. */ for (i = 0; i < NAPICID; i++) if (ioapics[i] != NULL) ioapic_register(ioapics[i]); /* Fifth, we setup data structures to handle PCI interrupt routing. */ mptable_pci_setup(); /* Finally, we throw the switch to enable the I/O APIC's. */ if (mpfps->mpfb2 & MPFB2_IMCR_PRESENT) { outb(0x22, 0x70); /* select IMCR */ byte = inb(0x23); /* current contents */ byte |= 0x01; /* mask external INTR */ outb(0x23, byte); /* disconnect 8259s/NMI */ } return (0); } static void mptable_register(void *dummy __unused) { apic_register_enumerator(&mptable_enumerator); } -SYSINIT(mptable_register, SI_SUB_TUNABLES - 1, SI_ORDER_FIRST, - mptable_register, NULL) +SYSINIT(mptable_register, SI_SUB_CPU - 1, SI_ORDER_FIRST, mptable_register, + NULL) /* * Call the handler routine for each entry in the MP config table. */ static void mptable_walk_table(mptable_entry_handler *handler, void *arg) { u_int i; u_char *entry; entry = (u_char *)(mpct + 1); for (i = 0; i < mpct->entry_count; i++) { switch (*entry) { case MPCT_ENTRY_PROCESSOR: case MPCT_ENTRY_IOAPIC: case MPCT_ENTRY_BUS: case MPCT_ENTRY_INT: case MPCT_ENTRY_LOCAL_INT: break; default: panic("%s: Unknown MP Config Entry %d\n", __func__, (int)*entry); } handler(entry, arg); entry += basetable_entry_types[*entry].length; } } static void mptable_probe_cpus_handler(u_char *entry, void *arg) { proc_entry_ptr proc; u_int *cpu_mask; switch (*entry) { case MPCT_ENTRY_PROCESSOR: proc = (proc_entry_ptr)entry; if (proc->cpu_flags & PROCENTRY_FLAG_EN) { lapic_create(proc->apic_id, proc->cpu_flags & PROCENTRY_FLAG_BP); cpu_mask = (u_int *)arg; *cpu_mask |= (1 << proc->apic_id); } break; } } static void mptable_count_items_handler(u_char *entry, void *arg __unused) { io_apic_entry_ptr apic; bus_entry_ptr bus; switch (*entry) { case MPCT_ENTRY_BUS: bus = (bus_entry_ptr)entry; mptable_nbusses++; if (bus->bus_id > mptable_maxbusid) mptable_maxbusid = bus->bus_id; break; case MPCT_ENTRY_IOAPIC: apic = (io_apic_entry_ptr)entry; if (apic->apic_flags & IOAPICENTRY_FLAG_EN) mptable_nioapics++; break; } } /* * Count items in the table. */ static void mptable_count_items(void) { /* Is this a pre-defined config? */ if (mpfps->config_type != 0) { mptable_nioapics = 1; switch (mpfps->config_type) { case 1: case 2: case 3: case 4: mptable_nbusses = 1; break; case 5: case 6: case 7: mptable_nbusses = 2; break; default: panic("Unknown pre-defined MP Table config type %d", mpfps->config_type); } mptable_maxbusid = mptable_nbusses - 1; } else mptable_walk_table(mptable_count_items_handler, NULL); } /* * Add a bus or I/O APIC from an entry in the table. */ static void mptable_parse_apics_and_busses_handler(u_char *entry, void *arg __unused) { io_apic_entry_ptr apic; bus_entry_ptr bus; enum busTypes bus_type; int i; switch (*entry) { case MPCT_ENTRY_BUS: bus = (bus_entry_ptr)entry; bus_type = lookup_bus_type(bus->bus_type); if (bus_type == UNKNOWN_BUSTYPE) { printf("MPTable: Unknown bus %d type \"", bus->bus_id); for (i = 0; i < 6; i++) printf("%c", bus->bus_type[i]); printf("\"\n"); } busses[bus->bus_id].bus_id = bus->bus_id; busses[bus->bus_id].bus_type = bus_type; break; case MPCT_ENTRY_IOAPIC: apic = (io_apic_entry_ptr)entry; if (!(apic->apic_flags & IOAPICENTRY_FLAG_EN)) break; if (apic->apic_id >= NAPICID) panic("%s: I/O APIC ID %d too high", __func__, apic->apic_id); if (ioapics[apic->apic_id] != NULL) panic("%s: Double APIC ID %d", __func__, apic->apic_id); ioapics[apic->apic_id] = ioapic_create( (uintptr_t)apic->apic_address, apic->apic_id, -1); break; default: break; } } /* * Enumerate I/O APIC's and busses. */ static void mptable_parse_apics_and_busses(void) { /* Is this a pre-defined config? */ if (mpfps->config_type != 0) { ioapics[0] = ioapic_create(DEFAULT_IO_APIC_BASE, 2, 0); busses[0].bus_id = 0; busses[0].bus_type = default_data[mpfps->config_type][2]; if (mptable_nbusses > 1) { busses[1].bus_id = 1; busses[1].bus_type = default_data[mpfps->config_type][4]; } } else mptable_walk_table(mptable_parse_apics_and_busses_handler, NULL); } /* * Determine conforming polarity for a given bus type. */ static u_char conforming_polarity(u_char src_bus) { KASSERT(src_bus <= mptable_maxbusid, ("bus id %d too large", src_bus)); switch (busses[src_bus].bus_type) { case ISA: case EISA: /* Active Hi */ return (1); case PCI: /* Active Lo */ return (0); default: panic("%s: unknown bus type %d", __func__, busses[src_bus].bus_type); } } /* * Determine conforming trigger for a given bus type. */ static u_char conforming_trigger(u_char src_bus, u_char src_bus_irq) { static int eisa_int_control = -1; KASSERT(src_bus <= mptable_maxbusid, ("bus id %d too large", src_bus)); switch (busses[src_bus].bus_type) { case ISA: /* Edge Triggered */ return (1); case PCI: /* Level Triggered */ return (0); case EISA: KASSERT(src_bus_irq < 16, ("Invalid EISA IRQ %d", src_bus_irq)); if (eisa_int_control == -1) eisa_int_control = inb(ELCR1) << 8 | inb(ELCR0); if (eisa_int_control & (1 << src_bus_irq)) /* Level Triggered */ return (0); else /* Edge Triggered */ return (1); default: panic("%s: unknown bus type %d", __func__, busses[src_bus].bus_type); } } static u_char intentry_polarity(int_entry_ptr intr) { switch (intr->int_flags & INTENTRY_FLAGS_POLARITY) { case INTENTRY_FLAGS_POLARITY_CONFORM: return (conforming_polarity(intr->src_bus_id)); case INTENTRY_FLAGS_POLARITY_ACTIVEHI: return (1); case INTENTRY_FLAGS_POLARITY_ACTIVELO: return (0); default: panic("Bogus interrupt flags"); } } static u_char intentry_trigger(int_entry_ptr intr) { switch (intr->int_flags & INTENTRY_FLAGS_TRIGGER) { case INTENTRY_FLAGS_TRIGGER_CONFORM: return (conforming_trigger(intr->src_bus_id, intr->src_bus_irq)); case INTENTRY_FLAGS_TRIGGER_EDGE: return (1); case INTENTRY_FLAGS_TRIGGER_LEVEL: return (0); default: panic("Bogus interrupt flags"); } } /* * Parse an interrupt entry for an I/O interrupt routed to a pin on an I/O APIC. */ static void mptable_parse_io_int(int_entry_ptr intr) { void *ioapic; u_int pin; if (intr->dst_apic_id == 0xff) { printf("MPTable: Ignoring global interrupt entry for pin %d\n", intr->dst_apic_int); return; } if (intr->dst_apic_id >= NAPICID) { printf("MPTable: Ignoring interrupt entry for ioapic%d\n", intr->dst_apic_id); return; } ioapic = ioapics[intr->dst_apic_id]; if (ioapic == NULL) { printf( "MPTable: Ignoring interrupt entry for missing ioapic%d\n", intr->dst_apic_id); return; } pin = intr->dst_apic_int; switch (intr->int_type) { case INTENTRY_TYPE_INT: if (busses[intr->src_bus_id].bus_type == NOBUS) panic("interrupt from missing bus"); if (busses[intr->src_bus_id].bus_type == ISA && intr->src_bus_irq != pin) { ioapic_remap_vector(ioapic, pin, intr->src_bus_irq); if (ioapic_get_vector(ioapic, intr->src_bus_irq) == intr->src_bus_irq) ioapic_disable_pin(ioapic, intr->src_bus_irq); } break; case INTENTRY_TYPE_NMI: ioapic_set_nmi(ioapic, pin); break; case INTENTRY_TYPE_SMI: ioapic_set_smi(ioapic, pin); break; case INTENTRY_TYPE_EXTINT: ioapic_set_extint(ioapic, pin); break; default: panic("%s: invalid interrupt entry type %d\n", __func__, intr->int_type); } if (intr->int_type == INTENTRY_TYPE_INT || (intr->int_flags & INTENTRY_FLAGS_TRIGGER) != INTENTRY_FLAGS_TRIGGER_CONFORM) ioapic_set_triggermode(ioapic, pin, intentry_trigger(intr)); if (intr->int_type == INTENTRY_TYPE_INT || (intr->int_flags & INTENTRY_FLAGS_POLARITY) != INTENTRY_FLAGS_POLARITY_CONFORM) ioapic_set_polarity(ioapic, pin, intentry_polarity(intr)); } /* * Parse an interrupt entry for a local APIC LVT pin. */ static void mptable_parse_local_int(int_entry_ptr intr) { u_int apic_id, pin; if (intr->dst_apic_id == 0xff) apic_id = APIC_ID_ALL; else apic_id = intr->dst_apic_id; if (intr->dst_apic_int == 0) pin = LVT_LINT0; else pin = LVT_LINT1; switch (intr->int_type) { case INTENTRY_TYPE_INT: #if 1 printf( "MPTable: Ignoring vectored local interrupt for LINTIN%d vector %d\n", intr->dst_apic_int, intr->src_bus_irq); return; #else lapic_set_lvt_mode(apic_id, pin, APIC_LVT_DM_FIXED); break; #endif case INTENTRY_TYPE_NMI: lapic_set_lvt_mode(apic_id, pin, APIC_LVT_DM_NMI); break; case INTENTRY_TYPE_SMI: lapic_set_lvt_mode(apic_id, pin, APIC_LVT_DM_SMI); break; case INTENTRY_TYPE_EXTINT: lapic_set_lvt_mode(apic_id, pin, APIC_LVT_DM_EXTINT); break; default: panic("%s: invalid interrupt entry type %d\n", __func__, intr->int_type); } if ((intr->int_flags & INTENTRY_FLAGS_TRIGGER) != INTENTRY_FLAGS_TRIGGER_CONFORM) lapic_set_lvt_triggermode(apic_id, pin, intentry_trigger(intr)); if ((intr->int_flags & INTENTRY_FLAGS_POLARITY) != INTENTRY_FLAGS_POLARITY_CONFORM) lapic_set_lvt_polarity(apic_id, pin, intentry_polarity(intr)); } /* * Parse interrupt entries. */ static void mptable_parse_ints_handler(u_char *entry, void *arg __unused) { int_entry_ptr intr; intr = (int_entry_ptr)entry; switch (*entry) { case MPCT_ENTRY_INT: mptable_parse_io_int(intr); break; case MPCT_ENTRY_LOCAL_INT: mptable_parse_local_int(intr); break; } } /* * Configure the interrupt pins */ static void mptable_parse_ints(void) { /* Is this a pre-defined config? */ if (mpfps->config_type != 0) { /* Configure LINT pins. */ lapic_set_lvt_mode(APIC_ID_ALL, LVT_LINT0, APIC_LVT_DM_EXTINT); lapic_set_lvt_mode(APIC_ID_ALL, LVT_LINT1, APIC_LVT_DM_NMI); /* Configure I/O APIC pins. */ if (mpfps->config_type != 7) ioapic_set_extint(ioapics[0], 0); else ioapic_disable_pin(ioapics[0], 0); if (mpfps->config_type != 2) ioapic_remap_vector(ioapics[0], 2, 0); else ioapic_disable_pin(ioapics[0], 2); if (mpfps->config_type == 2) ioapic_disable_pin(ioapics[0], 13); } else mptable_walk_table(mptable_parse_ints_handler, NULL); } #ifdef MPTABLE_FORCE_HTT /* * Perform a hyperthreading "fix-up" to enumerate any logical CPU's * that aren't already listed in the table. * * XXX: We assume that all of the physical CPUs in the * system have the same number of logical CPUs. * * XXX: We assume that APIC ID's are allocated such that * the APIC ID's for a physical processor are aligned * with the number of logical CPU's in the processor. */ static void mptable_hyperthread_fixup(u_int id_mask) { u_int i, id, logical_cpus; /* Nothing to do if there is no HTT support. */ if ((cpu_feature & CPUID_HTT) == 0) return; logical_cpus = (cpu_procinfo & CPUID_HTT_CORES) >> 16; if (logical_cpus <= 1) return; /* * For each APIC ID of a CPU that is set in the mask, * scan the other candidate APIC ID's for this * physical processor. If any of those ID's are * already in the table, then kill the fixup. */ for (id = 0; id <= MAXCPU; id++) { if ((id_mask & 1 << id) == 0) continue; /* First, make sure we are on a logical_cpus boundary. */ if (id % logical_cpus != 0) return; for (i = id + 1; i < id + logical_cpus; i++) if ((id_mask & 1 << i) != 0) return; } /* * Ok, the ID's checked out, so perform the fixup by * adding the logical CPUs. */ while ((id = ffs(id_mask)) != 0) { id--; for (i = id + 1; i < id + logical_cpus; i++) { if (bootverbose) printf( "MPTable: Adding logical CPU %d from main CPU %d\n", i, id); lapic_create(i, 0); } id_mask &= ~(1 << id); } } #endif /* MPTABLE_FORCE_HTT */ /* * Support code for routing PCI interrupts using the MP Table. */ static void mptable_pci_setup(void) { int i; /* * Find the first pci bus and call it 0. Panic if pci0 is not * bus zero and there are multiple PCI busses. */ for (i = 0; i <= mptable_maxbusid; i++) if (busses[i].bus_type == PCI) { if (pci0 == -1) pci0 = i; else if (pci0 != 0) panic( "MPTable contains multiple PCI busses but no PCI bus 0"); } } static void mptable_pci_probe_table_handler(u_char *entry, void *arg) { struct pci_probe_table_args *args; int_entry_ptr intr; if (*entry != MPCT_ENTRY_INT) return; intr = (int_entry_ptr)entry; args = (struct pci_probe_table_args *)arg; KASSERT(args->bus <= mptable_maxbusid, ("bus %d is too big", args->bus)); KASSERT(busses[args->bus].bus_type == PCI, ("probing for non-PCI bus")); if (intr->src_bus_id == args->bus) args->found = 1; } int mptable_pci_probe_table(int bus) { struct pci_probe_table_args args; if (bus < 0) return (EINVAL); if (pci0 == -1 || pci0 + bus > mptable_maxbusid) return (ENXIO); if (busses[pci0 + bus].bus_type != PCI) return (ENXIO); args.bus = pci0 + bus; args.found = 0; mptable_walk_table(mptable_pci_probe_table_handler, &args); if (args.found == 0) return (ENXIO); return (0); } static void mptable_pci_route_interrupt_handler(u_char *entry, void *arg) { struct pci_route_interrupt_args *args; int_entry_ptr intr; int vector; if (*entry != MPCT_ENTRY_INT) return; intr = (int_entry_ptr)entry; args = (struct pci_route_interrupt_args *)arg; if (intr->src_bus_id != args->bus || intr->src_bus_irq != args->irq) return; /* Make sure the APIC maps to a known APIC. */ KASSERT(ioapics[intr->dst_apic_id] != NULL, ("No I/O APIC %d to route interrupt to", intr->dst_apic_id)); /* * Look up the vector for this APIC / pin combination. If we * have previously matched an entry for this PCI IRQ but it * has the same vector as this entry, just return. Otherwise, * we use the vector for this APIC / pin combination. */ vector = ioapic_get_vector(ioapics[intr->dst_apic_id], intr->dst_apic_int); if (args->vector == vector) return; KASSERT(args->vector == -1, ("Multiple entries for PCI IRQ %d", args->vector)); args->vector = vector; } int mptable_pci_route_interrupt(device_t pcib, device_t dev, int pin) { struct pci_route_interrupt_args args; int slot; /* Like ACPI, pin numbers are 0-3, not 1-4. */ pin--; KASSERT(pci0 != -1, ("do not know how to route PCI interrupts")); args.bus = pci_get_bus(dev) + pci0; slot = pci_get_slot(dev); /* * PCI interrupt entries in the MP Table encode both the slot and * pin into the IRQ with the pin being the two least significant * bits, the slot being the next five bits, and the most significant * bit being reserved. */ args.irq = slot << 2 | pin; args.vector = -1; mptable_walk_table(mptable_pci_route_interrupt_handler, &args); if (args.vector < 0) { device_printf(pcib, "unable to route slot %d INT%c\n", slot, 'A' + pin); return (PCI_INVALID_IRQ); } device_printf(pcib, "slot %d INT%c routed to irq %d\n", slot, 'A' + pin, args.vector); return (args.vector); }