diff --git a/sys/arch/x86/x86/acpi_machdep.c b/sys/arch/x86/x86/acpi_machdep.c new file mode 100644 index 000000000000..228c8afb3ea9 --- /dev/null +++ b/sys/arch/x86/x86/acpi_machdep.c @@ -0,0 +1,333 @@ +/* $NetBSD: acpi_machdep.c,v 1.1 2003/05/11 18:21:16 fvdl Exp $ */ + +/* + * Copyright 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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. + */ + +/* + * Machine-dependent routines for ACPICA. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: acpi_machdep.c,v 1.1 2003/05/11 18:21:16 fvdl Exp $"); + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "ioapic.h" + +#include "opt_mpacpi.h" +#include "opt_mpbios.h" + +static int acpi_intrcold = 1; + +struct acpi_intr_defer { + UINT32 number; + OSD_HANDLER function; + void *context; + void *ih; + LIST_ENTRY(acpi_intr_defer) list; +}; + +LIST_HEAD(, acpi_intr_defer) acpi_intr_deferq = + LIST_HEAD_INITIALIZER(acpi_intr_deferq); + +ACPI_STATUS +acpi_md_OsInitialize(void) +{ + + /* Nothing to do, yet. */ + return (AE_OK); +} + +ACPI_STATUS +acpi_md_OsTerminate(void) +{ + + /* Nothing to do, yet. */ + return (AE_OK); +} + +ACPI_STATUS +acpi_md_OsGetRootPointer(UINT32 Flags, ACPI_POINTER *PhysicalAddress) +{ + + return (AcpiFindRootPointer(Flags, PhysicalAddress)); +} + +ACPI_STATUS +acpi_md_OsInstallInterruptHandler(UINT32 InterruptNumber, + OSD_HANDLER ServiceRoutine, void *Context, void **cookiep) +{ + void *ih; + struct pic *pic; + int irq, pin, trigger; + struct acpi_intr_defer *aip; +#ifdef MPACPI + int i, h; + struct mp_intr_map *mip; +#endif +#if NIOAPIC > 0 + struct ioapic_softc *sc; +#endif + + if (acpi_intrcold) { + aip = malloc(sizeof(struct acpi_intr_defer), M_TEMP, M_WAITOK); + aip->number = InterruptNumber; + aip->function = ServiceRoutine; + aip->context = Context; + aip->ih = NULL; + + LIST_INSERT_HEAD(&acpi_intr_deferq, aip, list); + + *cookiep = (void *)aip; + return AE_OK; + } + + trigger = IST_LEVEL; + +#ifdef MPACPI + /* + * Can only match on ACPI global interrupt numbers if the ACPI + * interrupt info was extracted, which is in the MPACPI case. + */ + if (mp_busses == NULL) + goto nomap; + for (i = 0; i < mp_nbus; i++) { + for (mip = mp_busses[i].mb_intrs; mip != NULL; + mip = mip->next) { + if (mip->global_int == (int)InterruptNumber) { + h = mip->ioapic_ih; + if (APIC_IRQ_ISLEGACY(h)) { + irq = APIC_IRQ_LEGACY_IRQ(h); + pin = irq; + pic = &i8259_pic; + trigger = IST_EDGE; + } else { + sc = ioapic_find(APIC_IRQ_APIC(h)); + if (sc == NULL) + goto nomap; + pic = (struct pic *)sc; + pin = APIC_IRQ_PIN(h); + irq = -1; + trigger = + ((mip->flags >> 2) & 3) == + MPS_INTTR_EDGE ? + IST_EDGE : IST_LEVEL; + } + goto found; + } + } + } +nomap: +#endif + +#if NIOAPIC > 0 + pin = (int)InterruptNumber; + for (sc = ioapics ; sc != NULL && pin > sc->sc_apic_sz; + sc = sc->sc_next) + pin -= sc->sc_apic_sz; + if (sc != NULL) { + if (nioapics > 1) + printf("acpi: WARNING: no matching " + "I/O apic for SCI, assuming %s\n", + sc->sc_pic.pic_dev.dv_xname); + pic = (struct pic *)sc; + irq = -1; + } else +#endif + { + pic = &i8259_pic; + irq = pin = (int)InterruptNumber; + } + +#ifdef MPACPI +found: +#endif + + /* + * XXX probably, IPL_BIO is enough. + */ + ih = intr_establish(irq, pic, pin, trigger, IPL_VM, + (int (*)(void *)) ServiceRoutine, Context); + if (ih == NULL) + return (AE_NO_MEMORY); + *cookiep = ih; + return (AE_OK); +} + +void +acpi_md_OsRemoveInterruptHandler(void *cookie) +{ + struct acpi_intr_defer *aip; + + LIST_FOREACH(aip, &acpi_intr_deferq, list) { + if (aip == cookie) { + if (aip->ih != NULL) + intr_disestablish(aip->ih); + return; + } + } + + intr_disestablish(cookie); +} + +ACPI_STATUS +acpi_md_OsMapMemory(ACPI_PHYSICAL_ADDRESS PhysicalAddress, + UINT32 Length, void **LogicalAddress) +{ + + if (_x86_memio_map(X86_BUS_SPACE_MEM, PhysicalAddress, Length, + 0, (bus_space_handle_t *) LogicalAddress) == 0) + return (AE_OK); + + return (AE_NO_MEMORY); +} + +void +acpi_md_OsUnmapMemory(void *LogicalAddress, UINT32 Length) +{ + + (void) _x86_memio_unmap(X86_BUS_SPACE_MEM, + (bus_space_handle_t) LogicalAddress, Length, NULL); +} + +ACPI_STATUS +acpi_md_OsGetPhysicalAddress(void *LogicalAddress, + ACPI_PHYSICAL_ADDRESS *PhysicalAddress) +{ + paddr_t pa; + + if (pmap_extract(pmap_kernel(), (vaddr_t) LogicalAddress, &pa)) { + *PhysicalAddress = pa; + return (AE_OK); + } + + return (AE_ERROR); +} + +BOOLEAN +acpi_md_OsReadable(void *Pointer, UINT32 Length) +{ + BOOLEAN rv = TRUE; + vaddr_t sva, eva; + pt_entry_t *pte; + + sva = trunc_page((vaddr_t) Pointer); + eva = round_page((vaddr_t) Pointer + Length); + + if (sva < VM_MIN_KERNEL_ADDRESS) + return (FALSE); + + for (; sva < eva; sva += PAGE_SIZE) { + pte = kvtopte(sva); + if ((*pte & PG_V) == 0) { + rv = FALSE; + break; + } + } + + return (rv); +} + +BOOLEAN +acpi_md_OsWritable(void *Pointer, UINT32 Length) +{ + BOOLEAN rv = FALSE; + vaddr_t sva, eva; + pt_entry_t *pte; + + sva = trunc_page((vaddr_t) Pointer); + eva = round_page((vaddr_t) Pointer + Length); + + if (sva < VM_MIN_KERNEL_ADDRESS) + return (FALSE); + + for (; sva < eva; sva += PAGE_SIZE) { + pte = kvtopte(sva); + if ((*pte & (PG_V|PG_W)) != (PG_V|PG_W)) { + rv = FALSE; + break; + } + } + + return (rv); +} + +void +acpi_md_OsDisableInterrupt(void) +{ + disable_intr(); +} + +void +acpi_md_callback(struct device *acpi) +{ + struct acpi_intr_defer *aip; + +#ifdef MPACPI +#ifdef MPBIOS + if (!mpbios_scanned) +#endif + mpacpi_find_interrupts(acpi); +#endif + acpi_intrcold = 0; + + /* Proces deferred interrupt handler establish calls. */ + LIST_FOREACH(aip, &acpi_intr_deferq, list) { + acpi_md_OsInstallInterruptHandler(aip->number, aip->function, + aip->context, &aip->ih); + } +} diff --git a/sys/arch/x86/x86/mpacpi.c b/sys/arch/x86/x86/mpacpi.c new file mode 100644 index 000000000000..ec168a9549d1 --- /dev/null +++ b/sys/arch/x86/x86/mpacpi.c @@ -0,0 +1,779 @@ +/* $NetBSD: mpacpi.c,v 1.1 2003/05/11 18:21:50 fvdl Exp $ */ + +/* + * Copyright (c) 2003 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Frank van der Linden for Wasabi Systems, Inc. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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 "opt_acpi.h" +#include "opt_mpbios.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +/* XXX */ +#include +#include + +#include "pci.h" + +#if NPCI > 0 +struct mpacpi_pcibus { + TAILQ_ENTRY(mpacpi_pcibus) mpr_list; + ACPI_NAMESPACE_NODE *mpr_node; + ACPI_HANDLE *mpr_handle; /* Same thing really, but.. */ + int mpr_bus; + int mpr_level; + struct mpacpi_pcibus *mpr_parent; +}; + +TAILQ_HEAD(, mpacpi_pcibus) mpacpi_pcibusses; + +#endif + +int mpacpi_print(void *, const char *); +int mpacpi_match(struct device *, struct cfdata *, void *); + +/* + * acpi_madt_walk callbacks + */ +static ACPI_STATUS mpacpi_count(APIC_HEADER *, void *); +static ACPI_STATUS mpacpi_config_cpu(APIC_HEADER *, void *); +static ACPI_STATUS mpacpi_config_ioapic(APIC_HEADER *, void *); +static ACPI_STATUS mpacpi_nonpci_intr(APIC_HEADER *, void *); + +#if NPCI > 0 +/* + * Callbacks for the device namespace walk. + */ +static ACPI_STATUS mpacpi_pcibus_cb(ACPI_HANDLE, UINT32, void *, void **); +static int mpacpi_pcircount(struct mpacpi_pcibus *); +static int mpacpi_pciroute(struct mpacpi_pcibus *); +static void mpacpi_pcihier(struct acpi_softc *, struct mpacpi_pcibus *); +static struct mpacpi_pcibus *mpacpi_find_pcibus(ACPI_NAMESPACE_NODE *); +static int mpacpi_find_pcibusses(struct acpi_softc *); + +static void mpacpi_print_pci_intr(int); +#endif + +static void mpacpi_config_irouting(struct acpi_softc *); + +static void mpacpi_print_intr(struct mp_intr_map *); +static void mpacpi_print_isa_intr(int); + +int mpacpi_nioapic; +int mpacpi_ncpu; +int mpacpi_nintsrc; + +#if NPCI > 0 +int mpacpi_npci; +int mpacpi_maxpci; +static int mpacpi_maxbuslevel; +static int mpacpi_npciroots; +#endif + +static int mpacpi_intr_index; +static paddr_t mpacpi_lapic_base = LAPIC_BASE; + +int +mpacpi_print(void *aux, const char *pnp) +{ + struct cpu_attach_args * caa = (struct cpu_attach_args *) aux; + if (pnp) + printf("%s at %s:",caa->caa_name, pnp); + return (UNCONF); +} + +int +mpacpi_match(struct device *parent, struct cfdata *cf, void *aux) +{ + struct cpu_attach_args * caa = (struct cpu_attach_args *) aux; + if (strcmp(caa->caa_name, cf->cf_name)) + return 0; + + return (config_match(parent, cf, aux)); +} + +/* + * Handle special interrupt sources and overrides from the MADT. + * This is a callback function for acpi_madt_walk(). + */ +static ACPI_STATUS +mpacpi_nonpci_intr(APIC_HEADER *hdrp, void *aux) +{ + int *index = aux, pin, lindex; + struct mp_intr_map *mpi; + INT_IOAPIC_SOURCE_NMI *ioapic_nmi; + INT_LAPIC_SOURCE_NMI *lapic_nmi; + INT_SOURCE_OVERRIDE *isa_ovr; + struct ioapic_softc *ioapic; + + switch (hdrp->Type) { + case APIC_INTSRC_NMI: + ioapic_nmi = (INT_IOAPIC_SOURCE_NMI *)hdrp; + ioapic = ioapic_find_bybase(ioapic_nmi->GlobalInt); + if (ioapic == NULL) + break; + mpi = &mp_intrs[*index]; + (*index)++; + mpi->next = NULL; + mpi->bus = NULL; + mpi->type = MPS_INTTYPE_NMI; + mpi->ioapic = ioapic; + pin = ioapic_nmi->GlobalInt - ioapic->sc_apic_vecbase; + mpi->ioapic_pin = pin; + mpi->bus_pin = -1; + mpi->redir = (IOAPIC_REDLO_DEL_NMI<sc_pins[pin].ip_map = mpi; + mpi->ioapic_ih = APIC_INT_VIA_APIC | + (ioapic->sc_apicid << APIC_INT_APIC_SHIFT) | + (pin << APIC_INT_PIN_SHIFT); + mpi->flags = ioapic_nmi->Polarity | (ioapic_nmi->Trigger << 2); + mpi->global_int = ioapic_nmi->GlobalInt; + break; + case APIC_LAPIC_NMI: + lapic_nmi = (INT_LAPIC_SOURCE_NMI *)hdrp; + mpi = &mp_intrs[*index]; + (*index)++; + mpi->next = NULL; + mpi->bus = NULL; + mpi->ioapic = NULL; + mpi->type = MPS_INTTYPE_NMI; + mpi->ioapic_pin = lapic_nmi->Lint; + mpi->cpu_id = lapic_nmi->ApicId; + mpi->redir = (IOAPIC_REDLO_DEL_NMI<global_int = -1; + break; + case APIC_INTSRC_OVR: + isa_ovr = (INT_SOURCE_OVERRIDE *)hdrp; + if (isa_ovr->Source > 15 || isa_ovr->Source == 2) + break; + ioapic = ioapic_find_bybase(isa_ovr->GlobalInt); + if (ioapic == NULL) + break; + pin = isa_ovr->GlobalInt - ioapic->sc_apic_vecbase; + lindex = isa_ovr->Source; + /* + * IRQ 2 was skipped in the default setup. + */ + if (lindex > 2) + lindex--; + mpi = &mp_intrs[lindex]; + mpi->ioapic_ih = APIC_INT_VIA_APIC | + (ioapic->sc_apicid << APIC_INT_APIC_SHIFT) | + (pin << APIC_INT_PIN_SHIFT); + mpi->bus_pin = isa_ovr->Source; + mpi->ioapic_pin = pin; + mpi->redir = 0; + switch (isa_ovr->Polarity) { + case MPS_INTPO_ACTHI: + mpi->redir &= ~IOAPIC_REDLO_ACTLO; + break; + case MPS_INTPO_DEF: + case MPS_INTPO_ACTLO: + mpi->redir |= IOAPIC_REDLO_ACTLO; + break; + } + mpi->redir |= (IOAPIC_REDLO_DEL_LOPRI<Trigger) { + case MPS_INTTR_DEF: + case MPS_INTTR_LEVEL: + mpi->redir |= IOAPIC_REDLO_LEVEL; + break; + case MPS_INTTR_EDGE: + mpi->redir &= ~IOAPIC_REDLO_LEVEL; + break; + } + mpi->flags = isa_ovr->Polarity | (isa_ovr->Trigger << 2); + ioapic->sc_pins[pin].ip_map = mpi; + default: + break; + } + return AE_OK; +} + +/* + * Count various MP resources present in the MADT. + * This is a callback function for acpi_madt_walk(). + */ +static ACPI_STATUS +mpacpi_count(APIC_HEADER *hdrp, void *aux) +{ + LAPIC_ADDR_OVR *lop; + + switch (hdrp->Type) { + case APIC_PROC: + mpacpi_ncpu++; + break; + case APIC_IO: + mpacpi_nioapic++; + break; + case APIC_INTSRC_NMI: + case APIC_LAPIC_NMI: + mpacpi_nintsrc++; + break; + case APIC_ADDR_OVR: + lop = (LAPIC_ADDR_OVR *)hdrp; + mpacpi_lapic_base = lop->LocalApicAddress; + default: + break; + } + return AE_OK; +} + +static ACPI_STATUS +mpacpi_config_cpu(APIC_HEADER *hdrp, void *aux) +{ + struct device *parent = aux; + PROCESSOR_APIC *p; + struct cpu_attach_args caa; + + if (hdrp->Type == APIC_PROC) { + p = (PROCESSOR_APIC *)hdrp; + if (p->ProcessorEnabled) { + /* + * Assume ACPI Id 0 == BSP. + * XXX check if that's correct. + * XXX field name in structure is wrong. + */ + if (p->ProcessorApicId == 0) + caa.cpu_role = CPU_ROLE_BP; + else + caa.cpu_role = CPU_ROLE_AP; + caa.caa_name = "cpu"; + caa.cpu_number = p->LocalApicId; + caa.cpu_func = &mp_cpu_funcs; + config_found_sm(parent, &caa, mpacpi_print, + mpacpi_match); + + } + } + return AE_OK; +} + +static ACPI_STATUS +mpacpi_config_ioapic(APIC_HEADER *hdrp, void *aux) +{ + struct device *parent = aux; + struct apic_attach_args aaa; + IO_APIC *p; + + if (hdrp->Type == APIC_IO) { + p = (IO_APIC *)hdrp; + aaa.aaa_name = "ioapic"; + aaa.apic_id = p->IoApicId; + aaa.apic_address = p->IoApicAddress; + aaa.apic_version = -1; + aaa.flags = IOAPIC_VWIRE; + aaa.apic_vecbase = p->Vector; + config_found_sm(parent, &aaa, mpacpi_print, mpacpi_match); + } + return AE_OK; +} + +int +mpacpi_scan_apics(struct device *self) +{ + if (acpi_madt_map() != AE_OK) + return 0; + + mpacpi_ncpu = mpacpi_nintsrc = mpacpi_nioapic = 0; + acpi_madt_walk(mpacpi_count, self); + + lapic_boot_init(mpacpi_lapic_base); + + if (mpacpi_ncpu == 0) + return 0; + + acpi_madt_walk(mpacpi_config_cpu, self); + acpi_madt_walk(mpacpi_config_ioapic, self); + + acpi_madt_unmap(); + return 1; +} + +#if NPCI > 0 + +static struct mpacpi_pcibus * +mpacpi_find_pcibus(ACPI_NAMESPACE_NODE *node) +{ + struct mpacpi_pcibus *mpr; + + TAILQ_FOREACH(mpr, &mpacpi_pcibusses, mpr_list) { + if (mpr->mpr_node == node) + return mpr; + } + return NULL; +} + +static int +mpacpi_find_pcibusses(struct acpi_softc *acpi) +{ + ACPI_HANDLE sbhandle; + + if (AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &sbhandle) != AE_OK) + return ENOENT; + TAILQ_INIT(&mpacpi_pcibusses); + AcpiWalkNamespace(ACPI_TYPE_DEVICE, sbhandle, 100, + mpacpi_pcibus_cb, acpi, NULL); + return 0; +} + +/* + * Callback function for a namespace walk through ACPI space, finding all + * PCI root busses. + */ +static ACPI_STATUS +mpacpi_pcibus_cb(ACPI_HANDLE handle, UINT32 level, void *ct, void **status) +{ + ACPI_STATUS ret; + ACPI_NAMESPACE_NODE *node; + ACPI_BUFFER buf; + ACPI_INTEGER val; + struct mpacpi_pcibus *mpr; + + ret = acpi_get(handle, &buf, AcpiGetIrqRoutingTable); + if (ACPI_FAILURE(ret)) + return AE_OK; + AcpiOsFree(buf.Pointer); + + mpr = malloc(sizeof (struct mpacpi_pcibus), M_TEMP, M_WAITOK|M_ZERO); + if (mpr == NULL) + return AE_NO_MEMORY; + node = AcpiNsMapHandleToNode(handle); + if (level == 1) { + ret = AcpiUtEvaluateNumericObject(METHOD_NAME__BBN, + node, &val); + if (ACPI_FAILURE(ret)) { + mpr->mpr_bus = mpacpi_npciroots; + if (mp_verbose) + printf("mpacpi: could not get bus number for " + "PCI root bus, assuming %d\n", + mpr->mpr_bus); + } else + mpr->mpr_bus = ACPI_LOWORD(val); + mpacpi_npciroots++; + mpr->mpr_parent = NULL; + if (mp_verbose) + printf("mpacpi: found root PCI bus %d at level %u\n", + mpr->mpr_bus, level); + } else { + mpr->mpr_bus = -1; + if (mp_verbose) + printf("mpacpi: found subordinate bus at level %u\n", + level); + } + + mpr->mpr_handle = handle; + mpr->mpr_node = node; + mpr->mpr_level = (int)level; + TAILQ_INSERT_TAIL(&mpacpi_pcibusses, mpr, mpr_list); + mpacpi_npci++; + if ((int)level > mpacpi_maxbuslevel) + mpacpi_maxbuslevel = level; + return AE_OK; +} + +static void +mpacpi_pcihier(struct acpi_softc *acpi, struct mpacpi_pcibus *mpr) +{ + ACPI_NAMESPACE_NODE *parentnode; + ACPI_STATUS ret; + ACPI_INTEGER val; + struct mpacpi_pcibus *parentmpr; + pcireg_t binf; + pcitag_t tag; + + if (mpr->mpr_bus == -1) { + parentnode = AcpiNsGetParentNode(mpr->mpr_node); + parentmpr = mpacpi_find_pcibus(parentnode); + if (parentmpr == NULL) { + if (mp_verbose) + printf("mpacpi: no parent bus at level %d\n", + mpr->mpr_level); + return; + } + ret = AcpiUtEvaluateNumericObject(METHOD_NAME__ADR, + mpr->mpr_node, &val); + if (ACPI_FAILURE(ret)) + return; + tag = pci_make_tag(acpi->sc_pc, parentmpr->mpr_bus, + ACPI_HIWORD(val), ACPI_LOWORD(val)); + binf = pci_conf_read(acpi->sc_pc, tag, PPB_REG_BUSINFO); + mpr->mpr_bus = PPB_BUSINFO_SECONDARY(binf); + mpr->mpr_parent = parentmpr; + if (mp_verbose) + printf("mpacpi: found subordinate PCI bus %d\n", + mpr->mpr_bus); + } + if (mpr->mpr_bus > mpacpi_maxpci) + mpacpi_maxpci = mpr->mpr_bus; +} + +/* + * Find all static PRT entries for a PCI bus. + */ +static int +mpacpi_pciroute(struct mpacpi_pcibus *mpr) +{ + ACPI_STATUS ret; + ACPI_BUFFER buf; + ACPI_PCI_ROUTING_TABLE *ptrp; + char *p; + struct mp_intr_map *mpi; + struct mp_bus *mpb; + struct ioapic_softc *ioapic; + unsigned dev; + int pin; + + ret = acpi_get(mpr->mpr_handle, &buf, AcpiGetIrqRoutingTable); + if (ACPI_FAILURE(ret)) + return -1; + + if (mp_verbose) + printf("mpacpi: configuring PCI bus %d int routing\n", + mpr->mpr_bus); + + mpb = &mp_busses[mpr->mpr_bus]; + mpb->mb_intrs = NULL; + mpb->mb_name = "pci"; + mpb->mb_idx = mpr->mpr_bus; + mpb->mb_intr_print = mpacpi_print_pci_intr; + mpb->mb_intr_cfg = NULL; + mpb->mb_data = 0; + + for (p = buf.Pointer; ; p += ptrp->Length) { + ptrp = (ACPI_PCI_ROUTING_TABLE *)p; + if (ptrp->Length == 0) + break; + dev = ACPI_HIWORD(ptrp->Address); + if (ptrp->Source[0] != 0) + continue; + ioapic = ioapic_find_bybase(ptrp->SourceIndex); + if (ioapic == NULL) + continue; + mpi = &mp_intrs[mpacpi_intr_index++]; + mpi->bus = mpb; + mpi->bus_pin = (dev << 2) | ptrp->Pin; + mpi->type = MPS_INTTYPE_INT; + + /* Defaults for PCI (active low, level triggered) */ + mpi->redir = (IOAPIC_REDLO_DEL_LOPRI<flags = MPS_INTPO_ACTLO | (MPS_INTTR_LEVEL << 2); + mpi->cpu_id = 0; + + pin = ptrp->SourceIndex - ioapic->sc_apic_vecbase; + mpi->ioapic = ioapic; + mpi->ioapic_pin = pin; + mpi->ioapic_ih = APIC_INT_VIA_APIC | + (ioapic->sc_apicid << APIC_INT_APIC_SHIFT) | + (pin << APIC_INT_PIN_SHIFT); + ioapic->sc_pins[pin].ip_map = mpi; + mpi->next = mpb->mb_intrs; + mpi->global_int = ptrp->SourceIndex; + mpb->mb_intrs = mpi; + } + AcpiOsFree(buf.Pointer); + return 0; +} + +static int +mpacpi_pcircount(struct mpacpi_pcibus *mpr) +{ + int count = 0; + ACPI_STATUS ret; + ACPI_BUFFER buf; + ACPI_PCI_ROUTING_TABLE *PrtElement; + UINT8 *Buffer; + + ret = acpi_get(mpr->mpr_handle, &buf, AcpiGetIrqRoutingTable); + if (!ACPI_FAILURE(ret)) { + for (Buffer = buf.Pointer; ; Buffer += PrtElement->Length) { + PrtElement = (ACPI_PCI_ROUTING_TABLE *)Buffer; + if (PrtElement->Length == 0) + break; + count++; + } + AcpiOsFree(buf.Pointer); + } + return count; +} + +#endif + +/* + * Set up the interrupt config lists, in the same format as the mpbios + * does. + */ +static void +mpacpi_config_irouting(struct acpi_softc *acpi) +{ +#if NPCI > 0 + struct mpacpi_pcibus *mpr; +#endif + int nintr; + int i, index; + struct mp_bus *mbp; + struct mp_intr_map *mpi; + struct ioapic_softc *ioapic; + + nintr = mpacpi_nintsrc + NUM_LEGACY_IRQS - 1; +#if NPCI > 0 + TAILQ_FOREACH(mpr, &mpacpi_pcibusses, mpr_list) { + nintr += mpacpi_pcircount(mpr); + } + + for (i = 0; i <= mpacpi_maxbuslevel; i++) { + TAILQ_FOREACH(mpr, &mpacpi_pcibusses, mpr_list) { + if (mpr->mpr_level == i) + mpacpi_pcihier(acpi, mpr); + } + } + mp_isa_bus = mpacpi_maxpci + 1; +#else + mp_isa_bus = 0; +#endif + mp_nbus = mp_isa_bus + 1; + mp_nintr = nintr + mpacpi_nintsrc + NUM_LEGACY_IRQS - 1; + + mp_busses = malloc(sizeof(struct mp_bus) * mp_nbus, M_DEVBUF, + M_NOWAIT|M_ZERO); + if (mp_busses == NULL) + panic("can't allocate mp_busses"); + + mp_intrs = malloc(sizeof(struct mp_intr_map) * mp_nintr, M_DEVBUF, + M_NOWAIT|M_ZERO); + if (mp_intrs == NULL) + panic("can't allocate mp_intrs"); + + mbp = &mp_busses[mp_isa_bus]; + mbp->mb_name = "isa"; + mbp->mb_idx = 0; + mbp->mb_intr_print = mpacpi_print_isa_intr; + mbp->mb_intr_cfg = NULL; + mbp->mb_intrs = &mp_intrs[0]; + mbp->mb_data = 0; + + ioapic = ioapic_find_bybase(0); + if (ioapic == NULL) + panic("can't find first ioapic"); + + /* + * Set up default identity mapping for ISA irqs to first ioapic. + */ + for (i = index = 0; i < NUM_LEGACY_IRQS; i++) { + if (i == 2) + continue; + mpi = &mp_intrs[index]; + if (index < (NUM_LEGACY_IRQS - 2)) + mpi->next = &mp_intrs[index + 1]; + else + mpi->next = NULL; + mpi->bus = mbp; + mpi->bus_pin = i; + mpi->ioapic_pin = i; + mpi->ioapic = ioapic; + mpi->type = MPS_INTTYPE_INT; + mpi->cpu_id = 0; + mpi->ioapic_ih = APIC_INT_VIA_APIC | + (ioapic->sc_apicid << APIC_INT_APIC_SHIFT) | + (i << APIC_INT_PIN_SHIFT); + mpi->redir = (IOAPIC_REDLO_DEL_LOPRI<flags = MPS_INTPO_DEF | (MPS_INTTR_DEF << 2); + mpi->global_int = i; + ioapic->sc_pins[i].ip_map = mpi; + index++; + } + + mpacpi_intr_index = index; + if (acpi_madt_map() != AE_OK) + panic("failed to map the MADT a second time"); + + acpi_madt_walk(mpacpi_nonpci_intr, &mpacpi_intr_index); + acpi_madt_unmap(); + +#if NPCI > 0 + TAILQ_FOREACH(mpr, &mpacpi_pcibusses, mpr_list) { + mpacpi_pciroute(mpr); + } +#endif + mp_nintr = mpacpi_intr_index; +} + +/* + * XXX code duplication with mpbios.c + */ + +#if NPCI > 0 +static void +mpacpi_print_pci_intr(int intr) +{ + printf(" device %d INT_%c", (intr>>2)&0x1f, 'A' + (intr & 0x3)); +} +#endif + +static void +mpacpi_print_isa_intr(int intr) +{ + printf(" irq %d", intr); +} + +static const char inttype_fmt[] = "\177\020" + "f\0\2type\0" "=\1NMI\0" "=\2SMI\0" "=\3ExtINT\0"; + +static const char flagtype_fmt[] = "\177\020" + "f\0\2pol\0" "=\1Act Hi\0" "=\3Act Lo\0" + "f\2\2trig\0" "=\1Edge\0" "=\3Level\0"; + +static void +mpacpi_print_intr(struct mp_intr_map *mpi) +{ + char buf[256]; + int pin; + struct ioapic_softc *sc; + char *busname; + + sc = mpi->ioapic; + pin = mpi->ioapic_pin; + if (mpi->bus != NULL) + busname = mpi->bus->mb_name; + else { + switch (mpi->type) { + case MPS_INTTYPE_NMI: + busname = "NMI"; + break; + case MPS_INTTYPE_SMI: + busname = "SMI"; + break; + case MPS_INTTYPE_ExtINT: + busname = "ExtINT"; + break; + default: + busname = ""; + break; + } + } + + printf("%s: int%d attached to %s", + sc ? sc->sc_pic.pic_dev.dv_xname : "local apic", + pin, busname); + + if (mpi->bus != NULL) { + if (mpi->bus->mb_idx != -1) + printf("%d", mpi->bus->mb_idx); + (*(mpi->bus->mb_intr_print))(mpi->bus_pin); + } + + printf(" (type %s", + bitmask_snprintf(mpi->type, inttype_fmt, buf, sizeof(buf))); + + printf(" flags %s)\n", + bitmask_snprintf(mpi->flags, flagtype_fmt, buf, sizeof(buf))); +} + + + +int +mpacpi_find_interrupts(void *self) +{ + ACPI_OBJECT_LIST arglist; + ACPI_OBJECT arg; + ACPI_STATUS ret; + struct acpi_softc *acpi = self; + int i; + +#ifdef MPBIOS + /* + * If MPBIOS was enabled, and did the work (because the initial + * MADT scan failed for some reason), there's nothing left to + * do here. Same goes for the case where no I/O APICS were found. + */ + if (mpbios_scanned) + return 0; +#endif + + if (mpacpi_nioapic == 0) + return 0; + + /* + * Switch us into APIC mode by evaluating the _PIC(1). + * Needs to be done now, since it has an effect on + * the interrupt information we're about to retrieve. + */ + arglist.Count = 1; + arglist.Pointer = &arg; + arg.Type = ACPI_TYPE_INTEGER; + arg.Integer.Value = 1; /* I/O APIC mode (0 = PIC, 2 = IOSAPIC) */ + ret = AcpiEvaluateObject(NULL, "\\_PIC", &arglist, NULL); + if (ACPI_FAILURE(ret)) { + if (mp_verbose) + printf("mpacpi: switch to APIC mode failed\n"); + return 0; + } + +#if NPCI > 0 + mpacpi_find_pcibusses(acpi); + if (mp_verbose) + printf("mpacpi: %d PCI busses\n", mpacpi_npci); +#endif + mpacpi_config_irouting(acpi); + if (mp_verbose) + for (i = 0; i < mp_nintr; i++) + mpacpi_print_intr(&mp_intrs[i]); + return 0; +}