#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mpacpi.c,v 1.112 2025/10/06 12:27:52 riastradh Exp $");
#include "acpica.h"
#include "opt_acpi.h"
#include "opt_ddb.h"
#include "opt_mpbios.h"
#include "opt_multiprocessor.h"
#include "opt_pci.h"
#include "pchb.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/kmem.h>
#include <sys/queue.h>
#include <uvm/uvm_extern.h>
#include <machine/specialreg.h>
#include <machine/cpuvar.h>
#include <sys/bus.h>
#include <machine/mpacpi.h>
#include <machine/mpbiosvar.h>
#include <machine/i82093reg.h>
#include <machine/i82093var.h>
#include <machine/i82489reg.h>
#include <machine/i82489var.h>
#include <dev/isa/isareg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/ppbreg.h>
#include <dev/acpi/acpi_mcfg.h>
#include <dev/acpi/acpica.h>
#include <dev/acpi/acpireg.h>
#include <dev/acpi/acpivar.h>
#include <dev/cons.h>
#define _COMPONENT ACPI_RESOURCE_COMPONENT
ACPI_MODULE_NAME ("mpacpi")
#include "pci.h"
#include "ioapic.h"
#include "lapic.h"
#include "locators.h"
#define BUS_BUFFER (16)
#if NPCI > 0
struct mpacpi_pcibus {
TAILQ_ENTRY(mpacpi_pcibus) mpr_list;
ACPI_HANDLE mpr_handle;
devhandle_t mpr_devhandle;
ACPI_BUFFER mpr_buf;
int mpr_seg;
int mpr_bus;
};
static TAILQ_HEAD(, mpacpi_pcibus) mpacpi_pcibusses;
#endif
static int mpacpi_cpuprint(void *, const char *);
static int mpacpi_ioapicprint(void *, const char *);
static ACPI_STATUS mpacpi_count(ACPI_SUBTABLE_HEADER *, void *);
static ACPI_STATUS mpacpi_config_cpu(ACPI_SUBTABLE_HEADER *, void *);
static ACPI_STATUS mpacpi_config_ioapic(ACPI_SUBTABLE_HEADER *, void *);
static ACPI_STATUS mpacpi_nonpci_intr(ACPI_SUBTABLE_HEADER *, void *);
#if NPCI > 0
static int mpacpi_pcircount(struct mpacpi_pcibus *);
static int mpacpi_pciroute(struct mpacpi_pcibus *);
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);
static void mpacpi_user_continue(const char *fmt, ...);
#ifdef DDB
void mpacpi_dump(void);
#endif
int mpacpi_nioapic;
int mpacpi_ncpu;
int mpacpi_nintsrc;
#if NPCI > 0
static int mpacpi_npci;
static int mpacpi_maxpci;
static int mpacpi_npciroots;
#endif
static int mpacpi_intr_index;
static paddr_t mpacpi_lapic_base = LAPIC_BASE;
int mpacpi_step;
int mpacpi_force;
static int
mpacpi_cpuprint(void *aux, const char *pnp)
{
struct cpu_attach_args *caa = aux;
if (pnp)
aprint_normal("cpu at %s", pnp);
aprint_normal(" apid %d", caa->cpu_number);
return UNCONF;
}
static int
mpacpi_ioapicprint(void *aux, const char *pnp)
{
struct apic_attach_args *aaa = aux;
if (pnp)
aprint_normal("ioapic at %s", pnp);
aprint_normal(" apid %d", aaa->apic_id);
return UNCONF;
}
static ACPI_STATUS
mpacpi_nonpci_intr(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
{
int *index = aux, pin, lindex;
struct mp_intr_map *mpi;
ACPI_MADT_NMI_SOURCE *ioapic_nmi;
ACPI_MADT_LOCAL_APIC_NMI *lapic_nmi;
ACPI_MADT_INTERRUPT_OVERRIDE *isa_ovr;
ACPI_MADT_LOCAL_X2APIC_NMI *x2apic_nmi;
struct pic *pic;
extern struct acpi_softc *acpi_softc;
switch (hdrp->Type) {
case ACPI_MADT_TYPE_NMI_SOURCE:
ioapic_nmi = (ACPI_MADT_NMI_SOURCE *)hdrp;
pic = intr_findpic(ioapic_nmi->GlobalIrq);
if (pic == NULL)
break;
#if NIOAPIC == 0
if (pic->pic_type == PIC_IOAPIC)
break;
#endif
mpi = &mp_intrs[*index];
(*index)++;
mpi->next = NULL;
mpi->bus = NULL;
mpi->type = MPS_INTTYPE_NMI;
mpi->ioapic = pic;
pin = ioapic_nmi->GlobalIrq - pic->pic_vecbase;
mpi->ioapic_pin = pin;
mpi->bus_pin = -1;
mpi->redir = (IOAPIC_REDLO_DEL_NMI << IOAPIC_REDLO_DEL_SHIFT);
#if NIOAPIC > 0
if (pic->pic_type == PIC_IOAPIC) {
pic->pic_ioapic->sc_pins[pin].ip_map = mpi;
mpi->ioapic_ih = APIC_INT_VIA_APIC |
(pic->pic_apicid << APIC_INT_APIC_SHIFT) |
(pin << APIC_INT_PIN_SHIFT);
} else
#endif
mpi->ioapic_ih = pin;
mpi->flags = ioapic_nmi->IntiFlags;
mpi->global_int = ioapic_nmi->GlobalIrq;
break;
case ACPI_MADT_TYPE_LOCAL_APIC_NMI:
lapic_nmi = (ACPI_MADT_LOCAL_APIC_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->ProcessorId;
mpi->redir = (IOAPIC_REDLO_DEL_NMI << IOAPIC_REDLO_DEL_SHIFT);
mpi->global_int = -1;
break;
case ACPI_MADT_TYPE_INTERRUPT_OVERRIDE:
isa_ovr = (ACPI_MADT_INTERRUPT_OVERRIDE *)hdrp;
if (mp_verbose) {
printf("mpacpi: ISA interrupt override %d -> %d (%d/%d)\n",
isa_ovr->SourceIrq, isa_ovr->GlobalIrq,
isa_ovr->IntiFlags & ACPI_MADT_POLARITY_MASK,
(isa_ovr->IntiFlags & ACPI_MADT_TRIGGER_MASK) >>2);
}
if (isa_ovr->SourceIrq > 15 || isa_ovr->SourceIrq == 2 ||
(isa_ovr->SourceIrq == 0 && isa_ovr->GlobalIrq == 2 &&
(acpi_softc->sc_quirks & ACPI_QUIRK_IRQ0)))
break;
pic = intr_findpic(isa_ovr->GlobalIrq);
if (pic == NULL)
break;
#if NIOAPIC == 0
if (pic->pic_type == PIC_IOAPIC)
break;
#endif
pin = isa_ovr->GlobalIrq - pic->pic_vecbase;
lindex = isa_ovr->SourceIrq;
if (lindex > 2)
lindex--;
mpi = &mp_intrs[lindex];
#if NIOAPIC > 0
if (pic->pic_type == PIC_IOAPIC) {
mpi->ioapic_ih = APIC_INT_VIA_APIC |
(pic->pic_apicid << APIC_INT_APIC_SHIFT) |
(pin << APIC_INT_PIN_SHIFT);
} else
#endif
mpi->ioapic_ih = pin;
mpi->bus_pin = isa_ovr->SourceIrq;
mpi->ioapic = (struct pic *)pic;
mpi->ioapic_pin = pin;
mpi->sflags |= MPI_OVR;
mpi->redir = 0;
mpi->global_int = isa_ovr->GlobalIrq;
switch (isa_ovr->IntiFlags & ACPI_MADT_POLARITY_MASK) {
case ACPI_MADT_POLARITY_ACTIVE_HIGH:
mpi->redir &= ~IOAPIC_REDLO_ACTLO;
break;
case ACPI_MADT_POLARITY_ACTIVE_LOW:
mpi->redir |= IOAPIC_REDLO_ACTLO;
break;
case ACPI_MADT_POLARITY_CONFORMS:
if (isa_ovr->SourceIrq == AcpiGbl_FADT.SciInterrupt)
mpi->redir |= IOAPIC_REDLO_ACTLO;
else
mpi->redir &= ~IOAPIC_REDLO_ACTLO;
break;
}
mpi->redir |= (IOAPIC_REDLO_DEL_FIXED<<IOAPIC_REDLO_DEL_SHIFT);
switch (isa_ovr->IntiFlags & ACPI_MADT_TRIGGER_MASK) {
case ACPI_MADT_TRIGGER_LEVEL:
mpi->redir |= IOAPIC_REDLO_LEVEL;
break;
case ACPI_MADT_TRIGGER_EDGE:
mpi->redir &= ~IOAPIC_REDLO_LEVEL;
break;
case ACPI_MADT_TRIGGER_CONFORMS:
if (isa_ovr->SourceIrq == AcpiGbl_FADT.SciInterrupt)
mpi->redir |= IOAPIC_REDLO_LEVEL;
else
mpi->redir &= ~IOAPIC_REDLO_LEVEL;
break;
}
mpi->flags = isa_ovr->IntiFlags;
#if NIOAPIC > 0
if (pic->pic_type == PIC_IOAPIC)
pic->pic_ioapic->sc_pins[pin].ip_map = mpi;
#endif
break;
case ACPI_MADT_TYPE_LOCAL_X2APIC_NMI:
x2apic_nmi = (ACPI_MADT_LOCAL_X2APIC_NMI *)hdrp;
mpi = &mp_intrs[*index];
(*index)++;
mpi->next = NULL;
mpi->bus = NULL;
mpi->ioapic = NULL;
mpi->type = MPS_INTTYPE_NMI;
mpi->ioapic_pin = x2apic_nmi->Lint;
mpi->cpu_id = x2apic_nmi->Uid;
mpi->redir = (IOAPIC_REDLO_DEL_NMI << IOAPIC_REDLO_DEL_SHIFT);
mpi->global_int = -1;
break;
default:
break;
}
return AE_OK;
}
static ACPI_STATUS
mpacpi_count(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
{
ACPI_MADT_LOCAL_APIC_OVERRIDE *lop;
switch (hdrp->Type) {
case ACPI_MADT_TYPE_LOCAL_APIC:
case ACPI_MADT_TYPE_LOCAL_X2APIC:
mpacpi_ncpu++;
break;
case ACPI_MADT_TYPE_IO_APIC:
mpacpi_nioapic++;
break;
case ACPI_MADT_TYPE_NMI_SOURCE:
case ACPI_MADT_TYPE_LOCAL_APIC_NMI:
case ACPI_MADT_TYPE_LOCAL_X2APIC_NMI:
mpacpi_nintsrc++;
break;
case ACPI_MADT_TYPE_LOCAL_APIC_OVERRIDE:
lop = (ACPI_MADT_LOCAL_APIC_OVERRIDE *)hdrp;
mpacpi_lapic_base = lop->Address;
default:
break;
}
return AE_OK;
}
static ACPI_STATUS
mpacpi_config_cpu(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
{
device_t parent = aux;
ACPI_MADT_LOCAL_APIC *lapic;
ACPI_MADT_LOCAL_X2APIC *x2apic;
struct cpu_attach_args caa;
int cpunum = 0;
int locs[CPUBUSCF_NLOCS];
#if defined(MULTIPROCESSOR) || defined(IOAPIC)
if (mpacpi_ncpu > 1)
cpunum = lapic_cpu_number();
#endif
switch (hdrp->Type) {
case ACPI_MADT_TYPE_LOCAL_APIC:
lapic = (ACPI_MADT_LOCAL_APIC *)hdrp;
if (lapic->LapicFlags & ACPI_MADT_ENABLED) {
if (lapic->Id != cpunum)
caa.cpu_role = CPU_ROLE_AP;
else
caa.cpu_role = CPU_ROLE_BP;
caa.cpu_id = lapic->ProcessorId;
caa.cpu_number = lapic->Id;
caa.cpu_func = &mp_cpu_funcs;
locs[CPUBUSCF_APID] = caa.cpu_number;
config_found(parent, &caa, mpacpi_cpuprint,
CFARGS(.submatch = config_stdsubmatch,
.iattr = "cpubus",
.locators = locs));
}
break;
case ACPI_MADT_TYPE_LOCAL_X2APIC:
x2apic = (ACPI_MADT_LOCAL_X2APIC *)hdrp;
if (x2apic->LapicFlags & ACPI_MADT_ENABLED) {
if (x2apic->LocalApicId != cpunum)
caa.cpu_role = CPU_ROLE_AP;
else
caa.cpu_role = CPU_ROLE_BP;
caa.cpu_id = x2apic->Uid;
caa.cpu_number = x2apic->LocalApicId;
caa.cpu_func = &mp_cpu_funcs;
locs[CPUBUSCF_APID] = caa.cpu_number;
config_found(parent, &caa, mpacpi_cpuprint,
CFARGS(.submatch = config_stdsubmatch,
.iattr = "cpubus",
.locators = locs));
}
break;
}
return AE_OK;
}
static ACPI_STATUS
mpacpi_config_ioapic(ACPI_SUBTABLE_HEADER *hdrp, void *aux)
{
device_t parent = aux;
struct apic_attach_args aaa;
ACPI_MADT_IO_APIC *p;
int locs[IOAPICBUSCF_NLOCS];
if (hdrp->Type == ACPI_MADT_TYPE_IO_APIC) {
p = (ACPI_MADT_IO_APIC *)hdrp;
aaa.apic_id = p->Id;
aaa.apic_address = p->Address;
aaa.apic_version = -1;
aaa.flags = IOAPIC_VWIRE;
aaa.apic_vecbase = p->GlobalIrqBase;
locs[IOAPICBUSCF_APID] = aaa.apic_id;
config_found(parent, &aaa, mpacpi_ioapicprint,
CFARGS(.submatch = config_stdsubmatch,
.iattr = "ioapicbus",
.locators = locs));
}
return AE_OK;
}
int
mpacpi_scan_apics(device_t self, int *ncpup)
{
int rv = 0;
if (acpi_madt_map() != AE_OK)
return 0;
mpacpi_ncpu = mpacpi_nintsrc = mpacpi_nioapic = 0;
acpi_madt_walk(mpacpi_count, self);
acpi_madt_walk(mpacpi_config_ioapic, self);
#if NLAPIC > 0
lapic_boot_init(mpacpi_lapic_base);
#endif
acpi_madt_walk(mpacpi_config_cpu, self);
if (mpacpi_ncpu == 0)
goto done;
#if NPCI > 0
if (!mpacpi_force &&
(acpi_find_quirks() & (ACPI_QUIRK_BADPCI)) != 0)
goto done;
#endif
rv = 1;
done:
*ncpup = mpacpi_ncpu;
acpi_madt_unmap();
return rv;
}
#if NPCI > 0
static void
mpacpi_pci_foundbus(struct acpi_devnode *ad)
{
struct mpacpi_pcibus *mpr;
ACPI_BUFFER buf;
int rv;
rv = acpi_get(ad->ad_handle, &buf, AcpiGetIrqRoutingTable);
if (ACPI_FAILURE(rv)) {
buf.Length = 0;
buf.Pointer = NULL;
}
mpr = kmem_zalloc(sizeof(struct mpacpi_pcibus), KM_SLEEP);
mpr->mpr_handle = ad->ad_handle;
mpr->mpr_devhandle =
devhandle_from_acpi(devhandle_invalid(), ad->ad_handle);
mpr->mpr_buf = buf;
mpr->mpr_seg = ad->ad_pciinfo->ap_segment;
mpr->mpr_bus = ad->ad_pciinfo->ap_downbus;
TAILQ_INSERT_TAIL(&mpacpi_pcibusses, mpr, mpr_list);
if ((ad->ad_devinfo->Flags & ACPI_PCI_ROOT_BRIDGE) != 0) {
if (mp_verbose)
printf("mpacpi: found root PCI bus %d\n",
mpr->mpr_bus);
mpacpi_npciroots++;
} else {
if (mp_verbose)
printf("mpacpi: found subordinate bus %d\n",
mpr->mpr_bus);
}
if (mpr->mpr_bus > mpacpi_maxpci)
mpacpi_maxpci = mpr->mpr_bus;
mpacpi_npci++;
}
static void
mpacpi_pci_walk(struct acpi_devnode *ad)
{
struct acpi_devnode *child;
if (ad->ad_pciinfo &&
(ad->ad_pciinfo->ap_flags & ACPI_PCI_INFO_BRIDGE) != 0) {
mpacpi_pci_foundbus(ad);
}
SIMPLEQ_FOREACH(child, &ad->ad_child_head, ad_child_list) {
mpacpi_pci_walk(child);
}
}
static int
mpacpi_find_pcibusses(struct acpi_softc *sc)
{
TAILQ_INIT(&mpacpi_pcibusses);
mpacpi_pci_walk(sc->sc_root);
return 0;
}
static int
mpacpi_pciroute(struct mpacpi_pcibus *mpr)
{
ACPI_PCI_ROUTING_TABLE *ptrp;
ACPI_HANDLE linkdev;
char *p;
struct mp_intr_map *mpi, *iter;
struct mp_bus *mpb;
struct pic *pic;
unsigned dev;
int pin;
if (mp_verbose)
printf("mpacpi: configuring PCI bus %d int routing\n",
mpr->mpr_bus);
mpb = &mp_busses[mpr->mpr_bus];
if (mpb->mb_name != NULL)
printf("mpacpi: PCI bus %d int routing already done!\n",
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;
if (mpr->mpr_buf.Length == 0) {
goto out;
}
for (p = mpr->mpr_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 &&
(ptrp->SourceIndex == 14 || ptrp->SourceIndex == 15)) {
printf("Skipping PCI routing entry for PCI IDE compat IRQ");
continue;
}
mpi = &mp_intrs[mpacpi_intr_index];
mpi->bus_pin = (dev << 2) | (ptrp->Pin & 3);
mpi->bus = mpb;
mpi->type = MPS_INTTYPE_INT;
for (iter = mpb->mb_intrs; iter != NULL; iter = iter->next) {
if (iter->bus_pin == mpi->bus_pin)
break;
}
if (iter != NULL)
continue;
++mpacpi_intr_index;
if (ptrp->Source[0] != 0) {
if (mp_verbose > 1)
printf("pciroute: dev %d INT%c on lnkdev %s\n",
dev, 'A' + (ptrp->Pin & 3), ptrp->Source);
mpi->global_int = -1;
mpi->sourceindex = ptrp->SourceIndex;
if (AcpiGetHandle(ACPI_ROOT_OBJECT, ptrp->Source,
&linkdev) != AE_OK) {
printf("AcpiGetHandle failed for '%s'\n",
ptrp->Source);
continue;
}
mpi->ioapic_pin = -1;
mpi->linkdev = acpi_pci_link_devbyhandle(linkdev);
acpi_pci_link_add_reference(mpi->linkdev, NULL, 0,
mpr->mpr_bus, dev, ptrp->Pin & 3);
mpi->ioapic = NULL;
mpi->flags = MPS_INTPO_ACTLO | (MPS_INTTR_LEVEL << 2);
if (mp_verbose > 1)
printf("pciroute: done adding entry\n");
} else {
if (mp_verbose > 1)
printf("pciroute: dev %d INT%c on globint %d\n",
dev, 'A' + (ptrp->Pin & 3),
ptrp->SourceIndex);
mpi->sourceindex = 0;
mpi->global_int = ptrp->SourceIndex;
pic = intr_findpic(ptrp->SourceIndex);
if (pic == NULL)
continue;
mpi->redir =
(IOAPIC_REDLO_DEL_FIXED <<IOAPIC_REDLO_DEL_SHIFT) |
IOAPIC_REDLO_LEVEL | IOAPIC_REDLO_ACTLO;
mpi->ioapic = pic;
pin = ptrp->SourceIndex - pic->pic_vecbase;
if (pic->pic_type == PIC_I8259 && pin > 15)
panic("bad pin %d for legacy IRQ", pin);
mpi->ioapic_pin = pin;
#if NIOAPIC > 0
if (pic->pic_type == PIC_IOAPIC) {
pic->pic_ioapic->sc_pins[pin].ip_map = mpi;
mpi->ioapic_ih = APIC_INT_VIA_APIC |
(pic->pic_apicid << APIC_INT_APIC_SHIFT) |
(pin << APIC_INT_PIN_SHIFT);
} else
#endif
mpi->ioapic_ih = pin;
mpi->linkdev = NULL;
mpi->flags = MPS_INTPO_ACTLO | (MPS_INTTR_LEVEL << 2);
if (mp_verbose > 1)
printf("pciroute: done adding entry\n");
}
mpi->cpu_id = 0;
mpi->next = mpb->mb_intrs;
mpb->mb_intrs = mpi;
}
ACPI_FREE(mpr->mpr_buf.Pointer);
mpr->mpr_buf.Pointer = NULL;
out:
if (mp_verbose > 1)
printf("pciroute: done\n");
return 0;
}
static int
mpacpi_pcircount(struct mpacpi_pcibus *mpr)
{
int count = 0;
ACPI_PCI_ROUTING_TABLE *PrtElement;
uint8_t *Buffer;
if (mpr->mpr_buf.Length == 0) {
return 0;
}
for (Buffer = mpr->mpr_buf.Pointer;; Buffer += PrtElement->Length) {
PrtElement = (ACPI_PCI_ROUTING_TABLE *)Buffer;
if (PrtElement->Length == 0)
break;
count++;
}
return count;
}
#endif
static void
mpacpi_config_irouting(struct acpi_softc *acpi)
{
#if NPCI > 0
struct mpacpi_pcibus *mpr;
#endif
int nintr;
int i;
struct mp_bus *mbp;
struct mp_intr_map *mpi;
struct pic *pic;
nintr = mpacpi_nintsrc + NUM_LEGACY_IRQS - 1;
#if NPCI > 0
TAILQ_FOREACH(mpr, &mpacpi_pcibusses, mpr_list) {
nintr += mpacpi_pcircount(mpr);
}
mp_isa_bus = mpacpi_maxpci + BUS_BUFFER;
#else
mp_isa_bus = 0;
#endif
mp_nbus = mp_isa_bus + 1;
mp_nintr = nintr;
mp_busses = kmem_zalloc(sizeof(struct mp_bus) * mp_nbus, KM_SLEEP);
mp_intrs = kmem_zalloc(sizeof(struct mp_intr_map) * mp_nintr, KM_SLEEP);
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;
pic = intr_findpic(0);
if (pic == NULL)
panic("mpacpi: can't find first PIC");
#if NIOAPIC == 0
if (pic->pic_type == PIC_IOAPIC)
panic("mpacpi: ioapic but no i8259?");
#endif
for (i = mpacpi_intr_index = 0; i < NUM_LEGACY_IRQS; i++) {
if (i == 2)
continue;
mpi = &mp_intrs[mpacpi_intr_index];
if (mpacpi_intr_index < (NUM_LEGACY_IRQS - 2))
mpi->next = &mp_intrs[mpacpi_intr_index + 1];
else
mpi->next = NULL;
mpi->bus = mbp;
mpi->bus_pin = i;
mpi->ioapic_pin = i;
mpi->ioapic = pic;
mpi->type = MPS_INTTYPE_INT;
mpi->cpu_id = 0;
mpi->redir = 0;
#if NIOAPIC > 0
if (pic->pic_type == PIC_IOAPIC) {
mpi->ioapic_ih = APIC_INT_VIA_APIC |
(pic->pic_apicid << APIC_INT_APIC_SHIFT) |
(i << APIC_INT_PIN_SHIFT);
mpi->redir =
(IOAPIC_REDLO_DEL_FIXED << IOAPIC_REDLO_DEL_SHIFT);
pic->pic_ioapic->sc_pins[i].ip_map = mpi;
} else
#endif
mpi->ioapic_ih = i;
mpi->flags = MPS_INTPO_DEF | (MPS_INTTR_DEF << 2);
mpi->global_int = i;
mpacpi_intr_index++;
}
mpacpi_user_continue("done setting up mp_bus array and ISA maps");
if (acpi_madt_map() == AE_OK) {
acpi_madt_walk(mpacpi_nonpci_intr, &mpacpi_intr_index);
acpi_madt_unmap();
}
mpacpi_user_continue("done with non-PCI interrupts");
#if NPCI > 0
TAILQ_FOREACH(mpr, &mpacpi_pcibusses, mpr_list) {
mpacpi_pciroute(mpr);
}
#endif
mpacpi_user_continue("done routing PCI interrupts");
mp_nintr = mpacpi_intr_index;
}
#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 pic *sc;
const 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 = "<unknown>";
break;
}
}
if (mpi->linkdev != NULL)
printf("linkdev %s attached to %s",
acpi_pci_link_name(mpi->linkdev), busname);
else
printf("%s: pin %d attached to %s",
sc ? sc->pic_name : "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);
}
snprintb(buf, sizeof(buf), inttype_fmt, mpi->type);
printf(" (type %s", buf);
snprintb(buf, sizeof(buf), flagtype_fmt, mpi->flags);
printf(" flags %s)\n", buf);
}
int
mpacpi_find_interrupts(void *self)
{
#if NIOAPIC > 0
ACPI_STATUS rv;
#endif
struct acpi_softc *acpi = self;
int i;
#ifdef MPBIOS
if (mpbios_scanned)
return 0;
#endif
#if NIOAPIC > 0
if (mpacpi_nioapic != 0) {
rv = acpi_eval_set_integer(NULL, "\\_PIC", 1);
if (ACPI_FAILURE(rv) && rv != AE_NOT_FOUND) {
if (mp_verbose)
printf("mpacpi: switch to APIC mode failed\n");
return 0;
}
}
#endif
#if NPCI > 0
mpacpi_user_continue("finding PCI busses ");
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;
}
#if NPCI > 0
static void
mpacpi_set_devhandle(device_t self, struct pcibus_attach_args *pba)
{
devhandle_t devhandle = device_handle(self);
struct mpacpi_pcibus *mpr;
if (devhandle_type(devhandle) != DEVHANDLE_TYPE_INVALID) {
return;
}
TAILQ_FOREACH(mpr, &mpacpi_pcibusses, mpr_list) {
if (mpr->mpr_seg != 0) {
continue;
}
if (mpr->mpr_bus == pba->pba_bus) {
device_set_handle(self, mpr->mpr_devhandle);
return;
}
}
}
int
mpacpi_pci_attach_hook(device_t parent, device_t self,
struct pcibus_attach_args *pba)
{
struct mp_bus *mpb;
#ifdef MPBIOS
if (mpbios_scanned != 0)
return ENOENT;
#endif
if (TAILQ_EMPTY(&mpacpi_pcibusses))
return 0;
if (pba->pba_bus >= mp_isa_bus) {
intr_add_pcibus(pba);
return 0;
}
mpb = &mp_busses[pba->pba_bus];
if (mpb->mb_name != NULL) {
if (strcmp(mpb->mb_name, "pci"))
return EINVAL;
} else {
mpb->mb_name = "pci";
}
mpacpi_set_devhandle(self, pba);
mpb->mb_dev = self;
mpb->mb_pci_bridge_tag = pba->pba_bridgetag;
mpb->mb_pci_chipset_tag = pba->pba_pc;
if (mp_verbose)
printf("\n%s: added to list as bus %d", device_xname(parent),
pba->pba_bus);
if (pba->pba_bus > mpacpi_maxpci)
mpacpi_maxpci = pba->pba_bus;
return 0;
}
#endif
void
mpacpi_configure_busses(void)
{
#ifdef PCI_RESOURCE
struct mpacpi_pcibus *mpr;
struct mp_bus *mpb;
int error;
TAILQ_FOREACH(mpr, &mpacpi_pcibusses, mpr_list) {
if (mp_verbose) {
printf("MPACPI PCI bus handle %s seg %d bus %d\n",
acpi_name(mpr->mpr_handle),
mpr->mpr_seg,
mpr->mpr_bus);
}
if (mpr->mpr_bus < 0) {
printf("MPACPI PCI bus number %d is negative\n",
mpr->mpr_bus);
return;
}
if (mpr->mpr_bus < 0 || mpr->mpr_bus >= mpacpi_maxpci) {
printf("MPACPI PCI bus number %d exceeds mpacpi_maxpci"
" %d\n",
mpr->mpr_bus, mpacpi_maxpci);
return;
}
mpb = &mp_busses[mpr->mpr_bus];
error = acpimcfg_configure_bus(mpb->mb_dev,
mpb->mb_pci_chipset_tag, mpr->mpr_handle, mpr->mpr_bus,
true);
if (error) {
printf("MPACPI PCI bus number %d configuration failed:"
" %d\n",
mpr->mpr_bus, error);
return;
}
}
#endif
}
int
mpacpi_findintr_linkdev(struct mp_intr_map *mip)
{
int irq, line, pol, trig;
struct pic *pic;
int pin;
if (mip->linkdev == NULL)
return ENOENT;
irq = acpi_pci_link_route_interrupt(mip->linkdev, NULL,
mip->sourceindex, &line, &pol, &trig);
if (mp_verbose)
printf("linkdev %s returned ACPI global irq %d, line %d\n",
acpi_pci_link_name(mip->linkdev), irq, line);
if (irq == X86_PCI_INTERRUPT_LINE_NO_CONNECTION)
return ENOENT;
if (irq != line) {
aprint_error("%s: mpacpi_findintr_linkdev:"
" irq mismatch (%d vs %d)\n",
acpi_pci_link_name(mip->linkdev), irq, line);
return ENOENT;
}
if (pol == ACPI_ACTIVE_LOW)
pol = MPS_INTPO_ACTLO;
else
pol = MPS_INTPO_ACTHI;
if (trig == ACPI_EDGE_SENSITIVE)
trig = MPS_INTTR_EDGE;
else
trig = MPS_INTTR_LEVEL;
mip->flags = pol | (trig << 2);
mip->global_int = irq;
pic = intr_findpic(irq);
if (pic == NULL)
return ENOENT;
mip->ioapic = pic;
pin = irq - pic->pic_vecbase;
if (pic->pic_type == PIC_IOAPIC) {
#if NIOAPIC > 0
mip->redir = (IOAPIC_REDLO_DEL_FIXED <<IOAPIC_REDLO_DEL_SHIFT);
if (pol == MPS_INTPO_ACTLO)
mip->redir |= IOAPIC_REDLO_ACTLO;
if (trig == MPS_INTTR_LEVEL)
mip->redir |= IOAPIC_REDLO_LEVEL;
mip->ioapic_ih = APIC_INT_VIA_APIC |
(pic->pic_apicid << APIC_INT_APIC_SHIFT) |
(pin << APIC_INT_PIN_SHIFT);
pic->pic_ioapic->sc_pins[pin].ip_map = mip;
mip->ioapic_pin = pin;
#else
return ENOENT;
#endif
} else
mip->ioapic_ih = pin;
return 0;
}
static void
mpacpi_user_continue(const char *fmt, ...)
{
va_list ap;
if (!mpacpi_step)
return;
printf("mpacpi: ");
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
printf("<press any key to continue>\n>");
cngetc();
}
#ifdef DDB
void
mpacpi_dump(void)
{
int i;
for (i = 0; i < mp_nintr; i++)
mpacpi_print_intr(&mp_intrs[i]);
}
#endif