Today mpi@ reminded me that I had written support for MSI-X some time ago. Since he is interested in using multiple vectors, I extended the code I had a bit to support that feature as well. This introduces a new function:
int pci_intr_map_msix(struct pci_attach_args *, int, pci_intr_handle_t *); You use it like pci_intr_map_msi(9) and pci_intr_map(9), buthave to pass it a vector number as the 2nd argument. Typically you'll pass 0, wich will map the 1st vector, which is always there on hardware with MSI-X support. Some hardware supports more than 1 vector. Typical examples are network cards that support multiple rings. Please be aware that on architectures like i386 and amd64, the number of interrupt vectors at each level is limited. If you consume too many of them, devices may fail to attach. As for the implementation. This only adds code for amd64. I'll probably add support for sparc64 as well. I'm hesitant about i386 because it has even less available interrupt vectors than amd64. Notice that the amd64 implementation uses _bus_space_map() and _bus_space_unmap(). That is deliberate. The MSI-X registers miht share a BAR with other registers that our drivers already map. The underscored versions of the mapping routines make sure that we don't fail to map things in that case. ok? Index: arch/alpha/pci/pci_machdep.h =================================================================== RCS file: /cvs/src/sys/arch/alpha/pci/pci_machdep.h,v retrieving revision 1.29 diff -u -p -r1.29 pci_machdep.h --- arch/alpha/pci/pci_machdep.h 26 Jul 2015 05:09:44 -0000 1.29 +++ arch/alpha/pci/pci_machdep.h 3 May 2016 19:26:30 -0000 @@ -104,6 +104,7 @@ int alpha_sysctl_chipset(int *, u_int, c #define pci_conf_write(c, t, r, v) \ (*(c)->pc_conf_write)((c)->pc_conf_v, (t), (r), (v)) #define pci_intr_map_msi(pa, ihp) (-1) +#define pci_intr_map_msix(pa, vec, ihp) (-1) #define pci_intr_string(c, ih) \ (*(c)->pc_intr_string)((c)->pc_intr_v, (ih)) #define pci_intr_line(c, ih) \ Index: arch/amd64/include/i82093var.h =================================================================== RCS file: /cvs/src/sys/arch/amd64/include/i82093var.h,v retrieving revision 1.4 diff -u -p -r1.4 i82093var.h --- arch/amd64/include/i82093var.h 21 May 2011 15:58:27 -0000 1.4 +++ arch/amd64/include/i82093var.h 3 May 2016 19:26:30 -0000 @@ -70,6 +70,7 @@ struct ioapic_softc { #define APIC_INT_VIA_APIC 0x10000000 #define APIC_INT_VIA_MSG 0x20000000 +#define APIC_INT_VIA_MSGX 0x40000000 #define APIC_INT_APIC_MASK 0x00ff0000 #define APIC_INT_APIC_SHIFT 16 #define APIC_INT_PIN_MASK 0x0000ff00 Index: arch/amd64/include/pci_machdep.h =================================================================== RCS file: /cvs/src/sys/arch/amd64/include/pci_machdep.h,v retrieving revision 1.24 diff -u -p -r1.24 pci_machdep.h --- arch/amd64/include/pci_machdep.h 29 Oct 2015 23:08:45 -0000 1.24 +++ arch/amd64/include/pci_machdep.h 3 May 2016 19:26:30 -0000 @@ -82,7 +82,10 @@ int pci_conf_size(pci_chipset_tag_t, pc pcireg_t pci_conf_read(pci_chipset_tag_t, pcitag_t, int); void pci_conf_write(pci_chipset_tag_t, pcitag_t, int, pcireg_t); -int pci_intr_map_msi(struct pci_attach_args *, pci_intr_handle_t *); +int pci_intr_map_msi(struct pci_attach_args *, + pci_intr_handle_t *); +int pci_intr_map_msix(struct pci_attach_args *, + int, pci_intr_handle_t *); int pci_intr_map(struct pci_attach_args *, pci_intr_handle_t *); const char *pci_intr_string(pci_chipset_tag_t, pci_intr_handle_t); void *pci_intr_establish(pci_chipset_tag_t, pci_intr_handle_t, Index: arch/amd64/pci/pci_machdep.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/pci/pci_machdep.c,v retrieving revision 1.62 diff -u -p -r1.62 pci_machdep.c --- arch/amd64/pci/pci_machdep.c 14 Mar 2015 03:38:46 -0000 1.62 +++ arch/amd64/pci/pci_machdep.c 3 May 2016 19:26:30 -0000 @@ -395,6 +395,143 @@ pci_intr_map_msi(struct pci_attach_args return 0; } +void msix_hwmask(struct pic *, int); +void msix_hwunmask(struct pic *, int); +void msix_addroute(struct pic *, struct cpu_info *, int, int, int); +void msix_delroute(struct pic *, struct cpu_info *, int, int, int); + +struct pic msix_pic = { + {0, {NULL}, NULL, 0, "msix", NULL, 0, 0}, + PIC_MSI, +#ifdef MULTIPROCESSOR + {}, +#endif + msix_hwmask, + msix_hwunmask, + msix_addroute, + msix_delroute, + NULL, + ioapic_edge_stubs +}; + +/* + * We pack the MSI-X vector number into the lower 8 bits of the PCI + * tag and use that as the MSI-X "PIC" pin number. This allows us to + * address 256 MSI-X vectors which ought to be enough for anybody. + */ +#define PCI_MSIX_VEC_MASK 0xff +#define PCI_MSIX_VEC(pin) ((pin) & PCI_MSIX_VEC_MASK) +#define PCI_MSIX_TAG(pin) ((pin) & ~PCI_MSIX_VEC_MASK) +#define PCI_MSIX_PIN(tag, vec) ((tag) | (vec)) + +void +msix_hwmask(struct pic *pic, int pin) +{ +} + +void +msix_hwunmask(struct pic *pic, int pin) +{ +} + +void +msix_addroute(struct pic *pic, struct cpu_info *ci, int pin, int vec, int type) +{ + pci_chipset_tag_t pc = NULL; /* XXX */ + bus_space_tag_t memt = X86_BUS_SPACE_MEM; /* XXX */ + bus_space_handle_t memh; + bus_addr_t base; + pcitag_t tag = PCI_MSIX_TAG(pin); + int entry = PCI_MSIX_VEC(pin); + pcireg_t reg, addr, table; + uint32_t ctrl; + int bir, offset; + int off, tblsz; + + if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, ®) == 0) + panic("%s: no msix capability", __func__); + + addr = 0xfee00000UL | (ci->ci_apicid << 12); + + table = pci_conf_read(pc, tag, off + PCI_MSIX_TABLE); + bir = (table & PCI_MSIX_TABLE_BIR); + offset = (table & PCI_MSIX_TABLE_OFF); + tblsz = (reg & PCI_MSIX_MC_TBLSZ) + 1; + + bir = PCI_MAPREG_START + bir * 4; + if (pci_mem_find(pc, tag, bir, &base, NULL, NULL) || + _bus_space_map(memt, base + offset, tblsz * 16, 0, &memh)) + panic("%s: cannot map registers", __func__); + + bus_space_write_8(memt, memh, PCI_MSIX_MA(entry), addr); + bus_space_write_4(memt, memh, PCI_MSIX_MD(entry), vec); + bus_space_barrier(memt, memh, PCI_MSIX_MA(entry), 16, + BUS_SPACE_BARRIER_WRITE); + ctrl = bus_space_read_4(memt, memh, PCI_MSIX_VC(entry)); + bus_space_write_4(memt, memh, PCI_MSIX_VC(entry), + ctrl & ~PCI_MSIX_VC_MASK); + + _bus_space_unmap(memt, memh, tblsz * 16, NULL); + + pci_conf_write(pc, tag, off, reg | PCI_MSIX_MC_MSIXE); +} + +void +msix_delroute(struct pic *pic, struct cpu_info *ci, int pin, int vec, int type) +{ + pci_chipset_tag_t pc = NULL; /* XXX */ + bus_space_tag_t memt = X86_BUS_SPACE_MEM; /* XXX */ + bus_space_handle_t memh; + bus_addr_t base; + pcitag_t tag = PCI_MSIX_TAG(pin); + int entry = PCI_MSIX_VEC(pin); + pcireg_t reg, table; + uint32_t ctrl; + int bir, offset; + int off, tblsz; + + if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, ®) == 0) + return; + + table = pci_conf_read(pc, tag, off + PCI_MSIX_TABLE); + bir = (table & PCI_MSIX_TABLE_BIR); + offset = (table & PCI_MSIX_TABLE_OFF); + tblsz = (reg & PCI_MSIX_MC_TBLSZ) + 1; + + bir = PCI_MAPREG_START + bir * 4; + if (pci_mem_find(pc, tag, bir, &base, NULL, NULL) || + _bus_space_map(memt, base + offset, tblsz * 16, 0, &memh)) + panic("%s: cannot map registers", __func__); + + ctrl = bus_space_read_4(memt, memh, PCI_MSIX_VC(entry)); + bus_space_write_4(memt, memh, PCI_MSIX_VC(entry), + ctrl | PCI_MSIX_VC_MASK); + + _bus_space_unmap(memt, memh, tblsz * 16, NULL); +} + +int +pci_intr_map_msix(struct pci_attach_args *pa, int vec, pci_intr_handle_t *ihp) +{ + pci_chipset_tag_t pc = pa->pa_pc; + pcitag_t tag = pa->pa_tag; + pcireg_t reg; + + KASSERT(PCI_MSIX_VEC(vec) == vec); + + if ((pa->pa_flags & PCI_FLAGS_MSI_ENABLED) == 0 || mp_busses == NULL || + pci_get_capability(pc, tag, PCI_CAP_MSIX, NULL, NULL) == 0) + return 1; + + if (vec > (reg & PCI_MSIX_MC_TBLSZ)) + return 1; + + ihp->tag = PCI_MSIX_PIN(tag, vec); + ihp->line = APIC_INT_VIA_MSGX; + ihp->pin = 0; + return 0; +} + int pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp) { @@ -527,6 +664,8 @@ pci_intr_string(pci_chipset_tag_t pc, pc if (ih.line & APIC_INT_VIA_MSG) return ("msi"); + if (ih.line & APIC_INT_VIA_MSGX) + return ("msix"); #if NIOAPIC > 0 if (ih.line & APIC_INT_VIA_APIC) @@ -557,6 +696,10 @@ pci_intr_establish(pci_chipset_tag_t pc, if (ih.line & APIC_INT_VIA_MSG) { return intr_establish(-1, &msi_pic, tag, IST_PULSE, level, + func, arg, what); + } + if (ih.line & APIC_INT_VIA_MSGX) { + return intr_establish(-1, &msix_pic, tag, IST_PULSE, level, func, arg, what); } Index: arch/arm/include/pci_machdep.h =================================================================== RCS file: /cvs/src/sys/arch/arm/include/pci_machdep.h,v retrieving revision 1.13 diff -u -p -r1.13 pci_machdep.h --- arch/arm/include/pci_machdep.h 5 Nov 2013 10:12:35 -0000 1.13 +++ arch/arm/include/pci_machdep.h 3 May 2016 19:26:30 -0000 @@ -93,6 +93,7 @@ struct arm32_pci_chipset { #define pci_intr_map(pa, ihp) \ (*(pa)->pa_pc->pc_intr_map)((pa), (ihp)) #define pci_intr_map_msi(pa, ihp) (-1) +#define pci_intr_map_msix(pa, vec, ihp) (-1) #define pci_intr_string(c, ih) \ (*(c)->pc_intr_string)((c)->pc_intr_v, (ih)) #define pci_intr_establish(c, ih, l, h, a, n) \ Index: arch/hppa/include/pci_machdep.h =================================================================== RCS file: /cvs/src/sys/arch/hppa/include/pci_machdep.h,v retrieving revision 1.11 diff -u -p -r1.11 pci_machdep.h --- arch/hppa/include/pci_machdep.h 5 Nov 2013 10:12:35 -0000 1.11 +++ arch/hppa/include/pci_machdep.h 3 May 2016 19:26:30 -0000 @@ -81,6 +81,7 @@ struct hppa_pci_chipset_tag { #define pci_intr_map(p, ihp) \ (*(p)->pa_pc->pc_intr_map)((p), (ihp)) #define pci_intr_map_msi(p, ihp) (-1) +#define pci_intr_map_msix(p, vec, ihp) (-1) #define pci_intr_line(c, ih) (ih) #define pci_intr_string(c, ih) \ (*(c)->pc_intr_string)((c)->_cookie, (ih)) Index: arch/hppa64/include/pci_machdep.h =================================================================== RCS file: /cvs/src/sys/arch/hppa64/include/pci_machdep.h,v retrieving revision 1.11 diff -u -p -r1.11 pci_machdep.h --- arch/hppa64/include/pci_machdep.h 5 Nov 2013 10:12:35 -0000 1.11 +++ arch/hppa64/include/pci_machdep.h 3 May 2016 19:26:31 -0000 @@ -72,6 +72,7 @@ struct hppa64_pci_chipset_tag { #define pci_intr_map(p, ihp) \ (*(p)->pa_pc->pc_intr_map)((p), (ihp)) #define pci_intr_map_msi(p, ihp) (-1) +#define pci_intr_map_msix(p, vec, ihp) (-1) #define pci_intr_line(c, ih) (ih) #define pci_intr_string(c, ih) \ (*(c)->pc_intr_string)((c)->_cookie, (ih)) Index: arch/i386/pci/pci_machdep.h =================================================================== RCS file: /cvs/src/sys/arch/i386/pci/pci_machdep.h,v retrieving revision 1.27 diff -u -p -r1.27 pci_machdep.h --- arch/i386/pci/pci_machdep.h 17 Jul 2015 22:42:09 -0000 1.27 +++ arch/i386/pci/pci_machdep.h 3 May 2016 19:26:31 -0000 @@ -99,6 +99,7 @@ void pci_conf_write(pci_chipset_tag_t, struct pci_attach_args; int pci_intr_map_msi(struct pci_attach_args *, pci_intr_handle_t *); int pci_intr_map(struct pci_attach_args *, pci_intr_handle_t *); +#define pci_intr_map_msix(p, vec, ihp) (-1) #define pci_intr_line(c, ih) ((ih).line) const char *pci_intr_string(pci_chipset_tag_t, pci_intr_handle_t); void *pci_intr_establish(pci_chipset_tag_t, pci_intr_handle_t, Index: arch/landisk/include/pci_machdep.h =================================================================== RCS file: /cvs/src/sys/arch/landisk/include/pci_machdep.h,v retrieving revision 1.9 diff -u -p -r1.9 pci_machdep.h --- arch/landisk/include/pci_machdep.h 5 Nov 2013 10:12:35 -0000 1.9 +++ arch/landisk/include/pci_machdep.h 3 May 2016 19:26:31 -0000 @@ -76,6 +76,7 @@ void landisk_pci_conf_interrupt(void *v, #define pci_intr_map(pa, ihp) \ landisk_pci_intr_map(pa, ihp) #define pci_intr_map_msi(pa, ihp) (-1) +#define pci_intr_map_msix(pa, vec, ihp) (-1) #define pci_intr_string(v, ih) \ landisk_pci_intr_string(v, ih) #define pci_intr_establish(v, ih, level, ih_fun, ih_arg, ih_name) \ Index: arch/loongson/include/pci_machdep.h =================================================================== RCS file: /cvs/src/sys/arch/loongson/include/pci_machdep.h,v retrieving revision 1.9 diff -u -p -r1.9 pci_machdep.h --- arch/loongson/include/pci_machdep.h 27 Mar 2014 22:16:03 -0000 1.9 +++ arch/loongson/include/pci_machdep.h 3 May 2016 19:26:31 -0000 @@ -76,6 +76,7 @@ struct mips_pci_chipset { #define pci_intr_map(c, ihp) \ (*(c)->pa_pc->pc_intr_map)((c), (ihp)) #define pci_intr_map_msi(c, ihp) (-1) +#define pci_intr_map_msix(c, vec, ihp) (-1) #define pci_intr_string(c, ih) \ (*(c)->pc_intr_string)((c)->pc_intr_v, (ih)) #define pci_intr_establish(c, ih, l, h, a, nm) \ Index: arch/macppc/include/pci_machdep.h =================================================================== RCS file: /cvs/src/sys/arch/macppc/include/pci_machdep.h,v retrieving revision 1.2 diff -u -p -r1.2 pci_machdep.h --- arch/macppc/include/pci_machdep.h 5 Nov 2013 10:12:35 -0000 1.2 +++ arch/macppc/include/pci_machdep.h 3 May 2016 19:26:31 -0000 @@ -85,6 +85,7 @@ pcireg_t pci_conf_read(pci_chipset_tag_t void pci_conf_write(pci_chipset_tag_t, pcitag_t, int, pcireg_t); int pci_intr_map(struct pci_attach_args *, pci_intr_handle_t *); int pci_intr_map_msi(struct pci_attach_args *, pci_intr_handle_t *); +#define pci_intr_map_msix(p, vec, ihp) (-1) int pci_intr_line(pci_chipset_tag_t, pci_intr_handle_t); const char *pci_intr_string(pci_chipset_tag_t, pci_intr_handle_t); void *pci_intr_establish(pci_chipset_tag_t, pci_intr_handle_t, Index: arch/octeon/include/pci_machdep.h =================================================================== RCS file: /cvs/src/sys/arch/octeon/include/pci_machdep.h,v retrieving revision 1.7 diff -u -p -r1.7 pci_machdep.h --- arch/octeon/include/pci_machdep.h 5 Nov 2013 10:12:35 -0000 1.7 +++ arch/octeon/include/pci_machdep.h 3 May 2016 19:26:31 -0000 @@ -104,6 +104,7 @@ static inline void pci_conf_write_db(voi #define pci_intr_map(c, ihp) \ (*(c)->pa_pc->pc_intr_map)((c), (ihp)) #define pci_intr_map_msi(c, ihp) (-1) +#define pci_intr_map_msix(c, vec, ihp) (-1) #define pci_intr_string(c, ih) \ (*(c)->pc_intr_string)((c)->pc_intr_v, (ih)) #define pci_intr_establish(c, ih, l, h, a, nm) \ Index: arch/sgi/pci/pci_machdep.h =================================================================== RCS file: /cvs/src/sys/arch/sgi/pci/pci_machdep.h,v retrieving revision 1.16 diff -u -p -r1.16 pci_machdep.h --- arch/sgi/pci/pci_machdep.h 5 Nov 2013 10:12:35 -0000 1.16 +++ arch/sgi/pci/pci_machdep.h 3 May 2016 19:26:31 -0000 @@ -87,6 +87,7 @@ struct mips_pci_chipset { #define pci_intr_map(c, ihp) \ (*(c)->pa_pc->pc_intr_map)((c), (ihp)) #define pci_intr_map_msi(c, ihp) (-1) +#define pci_intr_map_msix(c, vec, ihp) (-1) #define pci_intr_string(c, ih) \ (*(c)->pc_intr_string)((c)->pc_intr_v, (ih)) #define pci_intr_establish(c, ih, l, h, a, nm) \ Index: arch/socppc/include/pci_machdep.h =================================================================== RCS file: /cvs/src/sys/arch/socppc/include/pci_machdep.h,v retrieving revision 1.2 diff -u -p -r1.2 pci_machdep.h --- arch/socppc/include/pci_machdep.h 5 Nov 2013 10:12:35 -0000 1.2 +++ arch/socppc/include/pci_machdep.h 3 May 2016 19:26:31 -0000 @@ -86,6 +86,7 @@ struct ppc_pci_chipset { (*((pa)->pa_pc)->pc_intr_map)((pa)->pa_pc->pc_intr_v, \ (pa)->pa_intrtag, (pa)->pa_intrpin, (pa)->pa_intrline, (ihp)) #define pci_intr_map_msi(pa, ihp) (-1) +#define pci_intr_map_msix(pa, vec, ihp) (-1) #define pci_intr_string(c, ih) \ (*(c)->pc_intr_string)((c)->pc_intr_v, (ih)) #define pci_intr_line(c, ih) \ Index: arch/sparc64/include/pci_machdep.h =================================================================== RCS file: /cvs/src/sys/arch/sparc64/include/pci_machdep.h,v retrieving revision 1.32 diff -u -p -r1.32 pci_machdep.h --- arch/sparc64/include/pci_machdep.h 5 Nov 2013 10:12:35 -0000 1.32 +++ arch/sparc64/include/pci_machdep.h 3 May 2016 19:26:31 -0000 @@ -88,6 +88,7 @@ void pci_conf_write(pci_chipset_tag_t, pcireg_t); int pci_intr_map(struct pci_attach_args *, pci_intr_handle_t *); int pci_intr_map_msi(struct pci_attach_args *, pci_intr_handle_t *); +#define pci_intr_map_msix(pa, vec, ihp) (-1) int pci_intr_line(pci_chipset_tag_t, pci_intr_handle_t); const char *pci_intr_string(pci_chipset_tag_t, pci_intr_handle_t); void *pci_intr_establish(pci_chipset_tag_t, pci_intr_handle_t, Index: dev/pci/if_em.c =================================================================== RCS file: /cvs/src/sys/dev/pci/if_em.c,v retrieving revision 1.331 diff -u -p -r1.331 if_em.c --- dev/pci/if_em.c 13 Apr 2016 10:34:32 -0000 1.331 +++ dev/pci/if_em.c 3 May 2016 19:26:32 -0000 @@ -1673,7 +1673,7 @@ em_allocate_pci_resources(struct em_soft } sc->legacy_irq = 0; - if (pci_intr_map_msi(pa, &ih)) { + if (pci_intr_map_msix(pa, 0, &ih) && pci_intr_map_msi(pa, &ih)) { if (pci_intr_map(pa, &ih)) { printf(": couldn't map interrupt\n"); return (ENXIO); Index: dev/pci/pcireg.h =================================================================== RCS file: /cvs/src/sys/dev/pci/pcireg.h,v retrieving revision 1.48 diff -u -p -r1.48 pcireg.h --- dev/pci/pcireg.h 2 Jun 2015 15:26:19 -0000 1.48 +++ dev/pci/pcireg.h 3 May 2016 19:26:32 -0000 @@ -594,6 +594,21 @@ typedef u_int8_t pci_revision_t; #define PCI_PCIE_LCAP2 0x2c /* + * Extended Message Signaled Interrups; access via capability pointer. + */ +#define PCI_MSIX_MC_MSIXE 0x80000000 +#define PCI_MSIX_MC_TBLSZ 0x000007ff +#define PCI_MSIX_TABLE 0x04 +#define PCI_MSIX_TABLE_BIR 0x00000007 +#define PCI_MSIX_TABLE_OFF ~(PCI_MSIX_TABLE_BIR) + +#define PCI_MSIX_MA(i) ((i) * 16 + 0) +#define PCI_MSIX_MAU32(i) ((i) * 16 + 0) +#define PCI_MSIX_MD(i) ((i) * 16 + 8) +#define PCI_MSIX_VC(i) ((i) * 16 + 12) +#define PCI_MSIX_VC_MASK 0x00000001 + +/* * Interrupt Configuration Register; contains interrupt pin and line. */ #define PCI_INTERRUPT_REG 0x3c Index: dev/pci/xhci_pci.c =================================================================== RCS file: /cvs/src/sys/dev/pci/xhci_pci.c,v retrieving revision 1.7 diff -u -p -r1.7 xhci_pci.c --- dev/pci/xhci_pci.c 2 Nov 2015 14:53:10 -0000 1.7 +++ dev/pci/xhci_pci.c 3 May 2016 19:26:32 -0000 @@ -158,7 +158,8 @@ xhci_pci_attach(struct device *parent, s } /* Map and establish the interrupt. */ - if (pci_intr_map_msi(pa, &ih) != 0 && pci_intr_map(pa, &ih) != 0) { + if (pci_intr_map_msix(pa, 0, &ih) != 0 && + pci_intr_map_msi(pa, &ih) != 0 && pci_intr_map(pa, &ih) != 0) { printf(": couldn't map interrupt\n"); goto unmap_ret; }