diff --git a/sys/amd64/amd64/msi.c b/sys/amd64/amd64/msi.c index 6745ce2..d083166 100644 --- a/sys/amd64/amd64/msi.c +++ b/sys/amd64/amd64/msi.c @@ -128,11 +128,16 @@ static int msi_source_pending(struct intsrc *isrc); static int msi_config_intr(struct intsrc *isrc, enum intr_trigger trig, enum intr_polarity pol); static int msi_assign_cpu(struct intsrc *isrc, u_int apic_id); +static int fsb_assign_cpu(struct intsrc *isrc, u_int apic_id); struct pic msi_pic = { msi_enable_source, msi_disable_source, msi_eoi_source, msi_enable_intr, msi_disable_intr, msi_vector, msi_source_pending, NULL, NULL, msi_config_intr, msi_assign_cpu }; +struct pic fsb_pic = { msi_enable_source, msi_disable_source, msi_eoi_source, + msi_enable_intr, msi_disable_intr, msi_vector, + msi_source_pending, NULL, NULL, msi_config_intr, + fsb_assign_cpu }; static int msi_enabled; static int msi_last_irq; @@ -265,6 +270,46 @@ msi_assign_cpu(struct intsrc *isrc, u_int apic_id) return (0); } +static int +fsb_assign_cpu(struct intsrc *isrc, u_int apic_id) +{ + struct msi_intsrc *msi = (struct msi_intsrc *)isrc; + int old_vector; + u_int old_id; + int vector; + + if (1) { + printf("fsb_assign_cpu ignored\n"); + return (0); + } + /* Store information to free existing irq. */ + old_vector = msi->msi_vector; + old_id = msi->msi_cpu; + if (old_id == apic_id) + return (0); + + vector = apic_alloc_vector(apic_id, msi->msi_irq); + if (vector == 0) + return (ENOSPC); + + msi->msi_cpu = apic_id; + msi->msi_vector = vector; + if (msi->msi_intsrc.is_handlers > 0) + apic_enable_vector(msi->msi_cpu, msi->msi_vector); + if (bootverbose) + printf("msi: Assigning FSB IRQ %d to local APIC %u vector %u\n", + msi->msi_irq, msi->msi_cpu, msi->msi_vector); + + /* + * Free the old vector after the new one is established. This is done + * to prevent races where we could miss an interrupt. + */ + if (msi->msi_intsrc.is_handlers > 0) + apic_disable_vector(old_id, old_vector); + apic_free_vector(old_id, old_vector, msi->msi_irq); + return (0); +} + void msi_init(void) { @@ -310,6 +355,28 @@ msi_create_source(void) nexus_add_irq(irq); } +static void +fsb_create_source(void) +{ + struct msi_intsrc *msi; + u_int irq; + + mtx_lock(&msi_lock); + if (msi_last_irq >= NUM_MSI_INTS) { + mtx_unlock(&msi_lock); + return; + } + irq = msi_last_irq + FIRST_MSI_INT; + msi_last_irq++; + mtx_unlock(&msi_lock); + + msi = malloc(sizeof(struct msi_intsrc), M_MSI, M_WAITOK | M_ZERO); + msi->msi_intsrc.is_pic = &fsb_pic; + msi->msi_irq = irq; + intr_register_source(&msi->msi_intsrc); + nexus_add_irq(irq); +} + /* * Try to allocate 'count' interrupt sources with contiguous IDT values. */ @@ -340,7 +407,7 @@ again: break; /* If this is a free one, save its IRQ in the array. */ - if (msi->msi_dev == NULL) { + if (msi->msi_dev == NULL && msi->msi_intsrc.is_pic == &msi_pic) { irqs[cnt] = i; cnt++; if (cnt == count) @@ -404,6 +471,63 @@ again: } int +fsb_alloc(int *irq) +{ + struct msi_intsrc *msi; + u_int cpu; + int i, vector; + + if (!msi_enabled) + return (ENXIO); + + *irq = 0; +again: + mtx_lock(&msi_lock); + for (i = FIRST_MSI_INT; i < FIRST_MSI_INT + NUM_MSI_INTS; i++) { + msi = (struct msi_intsrc *)intr_lookup_source(i); + + /* End of allocated sources, so break. */ + if (msi == NULL) + break; + if (msi->msi_vector == 0 && msi->msi_intsrc.is_pic == &fsb_pic) { + *irq = i; + break; + } + } + + /* Do we need to create a new source? */ + if (*irq == 0) { + mtx_unlock(&msi_lock); + /* If we would exceed the max, give up. */ + if (i == FIRST_MSI_INT + NUM_MSI_INTS) + return (ENXIO); + fsb_create_source(); + goto again; + } + + cpu = intr_next_cpu(); + //vector = apic_alloc_vector(cpu, i); + vector = apic_alloc_vector_broadcast(i); + if (vector == 0) { + mtx_unlock(&msi_lock); + return (ENOSPC); + } + + //msi->msi_cpu = cpu; + msi->msi_cpu = APIC_ID_ALL; + msi->msi_vector = vector; + if (bootverbose) + printf( + "msi: routing FSB IRQ %d to local APIC %u vector %u\n", + msi->msi_irq, msi->msi_cpu, msi->msi_vector); + KASSERT(msi->msi_intsrc.is_handlers == 0, + ("dead MSI has handlers")); + mtx_unlock(&msi_lock); + + return (0); +} + +int msi_release(int *irqs, int count) { struct msi_intsrc *msi, *first; @@ -416,6 +540,9 @@ msi_release(int *irqs, int count) return (ENOENT); } + KASSERT(first->msi_intsrc.is_pic == &msi_pic, + ("msi_release called on FSB IRQ")); + /* Make sure this isn't an MSI-X message. */ if (first->msi_msix) { mtx_unlock(&msi_lock); @@ -464,6 +591,26 @@ msi_release(int *irqs, int count) } int +fsb_release(int irq) +{ + struct msi_intsrc *msi; + + mtx_lock(&msi_lock); + msi = (struct msi_intsrc *)intr_lookup_source(irq); + if (msi == NULL) { + mtx_unlock(&msi_lock); + return (ENOENT); + } + KASSERT(msi->msi_intsrc.is_pic == &fsb_pic, + ("fsb_release called on MSI IRQ")); + apic_free_vector(msi->msi_cpu, msi->msi_vector, msi->msi_irq); + msi->msi_vector = 0; + + mtx_unlock(&msi_lock); + return (0); +} + +int msi_map(int irq, uint64_t *addr, uint32_t *data) { struct msi_intsrc *msi; @@ -501,6 +648,24 @@ msi_map(int irq, uint64_t *addr, uint32_t *data) } int +fsb_map(int irq, uint64_t *addr, uint32_t *data) +{ + struct msi_intsrc *msi; + + mtx_lock(&msi_lock); + msi = (struct msi_intsrc *)intr_lookup_source(irq); + if (msi == NULL) { + mtx_unlock(&msi_lock); + return (ENOENT); + } + + *addr = INTEL_ADDR(msi); + *data = INTEL_DATA(msi); + mtx_unlock(&msi_lock); + return (0); +} + +int msix_alloc(device_t dev, int *irq) { struct msi_intsrc *msi; @@ -522,7 +687,7 @@ again: break; /* Stop at the first free source. */ - if (msi->msi_dev == NULL) + if (msi->msi_dev == NULL && msi->msi_intsrc.is_pic == &msi_pic) break; } @@ -579,6 +744,8 @@ msix_release(int irq) mtx_unlock(&msi_lock); return (ENOENT); } + KASSERT(msi->msi_intsrc.is_pic == &msi_pic, + ("msix_release called on FSB IRQ")); /* Make sure this is an MSI-X message. */ if (!msi->msi_msix) { diff --git a/sys/amd64/include/intr_machdep.h b/sys/amd64/include/intr_machdep.h index 6cd4eee..2eacc14 100644 --- a/sys/amd64/include/intr_machdep.h +++ b/sys/amd64/include/intr_machdep.h @@ -168,6 +168,9 @@ int msi_map(int irq, uint64_t *addr, uint32_t *data); int msi_release(int *irqs, int count); int msix_alloc(device_t dev, int *irq); int msix_release(int irq); +int fsb_alloc(int *irq); +int fsb_map(int irq, uint64_t *addr, uint32_t *data); +int fsb_release(int irq); #endif /* !LOCORE */ #endif /* _KERNEL */