diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 4d8837f..f0c8df8 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -332,6 +332,11 @@ SYSCTL_INT(_hw_pci, OID_AUTO, usb_early_takeover, CTLFLAG_RDTUN, Disable this if you depend on BIOS emulation of USB devices, that is\n\ you use USB devices (like keyboard or mouse) but do not load USB drivers"); +static int pci_enable_ari = 1; +TUNABLE_INT("hw.pci.enable_ari", &pci_enable_ari); +SYSCTL_INT(_hw_pci, OID_AUTO, enable_ari, CTLFLAG_RDTUN, &pci_enable_ari, + 0, "Enable support for PCIe Alternative RID Interpretation"); + static int pci_has_quirk(uint32_t devid, int quirk) { @@ -3252,6 +3257,19 @@ pci_add_resources(device_t bus, device_t dev, int force, uint32_t prefetchmask) } } +static struct pci_devinfo * +pci_identify_function(device_t pcib, device_t dev, int domain, int busno, + int slot, int func, size_t dinfo_size) +{ + struct pci_devinfo *dinfo; + + dinfo = pci_read_device(pcib, domain, busno, slot, func, dinfo_size); + if (dinfo != NULL) + pci_add_child(dev, dinfo); + + return (dinfo); +} + void pci_add_children(device_t dev, int domain, int busno, size_t dinfo_size) { @@ -3261,6 +3279,24 @@ pci_add_children(device_t dev, int domain, int busno, size_t dinfo_size) int maxslots; int s, f, pcifunchigh; uint8_t hdrtype; + int first_func; + + /* + * Try to detect a device at slot 0, function 0. If it exists, try to + * enable ARI. We must enable ARI before detecting the rest of the + * functions on this bus as ARI changes the set of slots and functions + * that are legal on this bus. + */ + dinfo = pci_identify_function(pcib, dev, domain, busno, 0, 0, + dinfo_size); + if (dinfo != NULL && pci_enable_ari) + PCIB_TRY_ENABLE_ARI(pcib, dinfo->cfg.dev); + + /* + * Start looking for new devices on slot 0 at function 1 because we + * just identified the device at slot 0, function 0. + */ + first_func = 1; KASSERT(dinfo_size >= sizeof(struct pci_devinfo), ("dinfo_size too small")); @@ -3273,14 +3309,13 @@ pci_add_children(device_t dev, int domain, int busno, size_t dinfo_size) if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) continue; if (hdrtype & PCIM_MFDEV) - pcifunchigh = PCI_FUNCMAX; - for (f = 0; f <= pcifunchigh; f++) { - dinfo = pci_read_device(pcib, domain, busno, s, f, + pcifunchigh = PCIB_MAXFUNCS(pcib); + for (f = first_func; f <= pcifunchigh; f++) + pci_identify_function(pcib, dev, domain, busno, s, f, dinfo_size); - if (dinfo != NULL) { - pci_add_child(dev, dinfo); - } - } + + /* For slots after slot 0 we need to check for function 0. */ + first_func = 0; } #undef REG } diff --git a/sys/dev/pci/pci_pci.c b/sys/dev/pci/pci_pci.c index 3fa3d5a..bf0cb18 100644 --- a/sys/dev/pci/pci_pci.c +++ b/sys/dev/pci/pci_pci.c @@ -56,6 +56,14 @@ static int pcib_suspend(device_t dev); static int pcib_resume(device_t dev); static int pcib_power_for_sleep(device_t pcib, device_t dev, int *pstate); +static uint32_t pcib_read_config(device_t dev, u_int b, u_int s, + u_int f, u_int reg, int width); +static void pcib_write_config(device_t dev, u_int b, u_int s, + u_int f, u_int reg, uint32_t val, int width); +static int pcib_ari_maxslots(device_t dev); +static int pcib_ari_maxfuncs(device_t dev); +static int pcib_try_enable_ari(device_t pcib, device_t dev); + static device_method_t pcib_methods[] = { /* Device interface */ @@ -83,7 +91,8 @@ static device_method_t pcib_methods[] = { DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), /* pcib interface */ - DEVMETHOD(pcib_maxslots, pcib_maxslots), + DEVMETHOD(pcib_maxslots, pcib_ari_maxslots), + DEVMETHOD(pcib_maxfuncs, pcib_ari_maxfuncs), DEVMETHOD(pcib_read_config, pcib_read_config), DEVMETHOD(pcib_write_config, pcib_write_config), DEVMETHOD(pcib_route_interrupt, pcib_route_interrupt), @@ -93,6 +102,7 @@ static device_method_t pcib_methods[] = { DEVMETHOD(pcib_release_msix, pcib_release_msix), DEVMETHOD(pcib_map_msi, pcib_map_msi), DEVMETHOD(pcib_power_for_sleep, pcib_power_for_sleep), + DEVMETHOD(pcib_try_enable_ari, pcib_try_enable_ari), DEVMETHOD_END }; @@ -1609,27 +1619,103 @@ pcib_alloc_resource(device_t dev, device_t child, int type, int *rid, #endif /* + * If ARI is enabled on this downstream port, translate the function number + * to the non-ARI slot/function. The downstream port will convert it back in + * hardware. If ARI is not enabled slot and func are not modified. + */ +static __inline void +pcib_xlate_ari(device_t pcib, int bus, int *slot, int *func) +{ + struct pcib_softc *sc; + int ari_func; + + sc = device_get_softc(pcib); + ari_func = *func; + + if (sc->flags & PCIB_ENABLE_ARI) { + KASSERT(*slot == 0, + ("Non-zero slot number with ARI enabled!")); + *slot = PCIE_ARI_SLOT(ari_func); + *func = PCIE_ARI_FUNC(ari_func); + } +} + + +static void +pcib_enable_ari(struct pcib_softc *sc, uint32_t pcie_pos) +{ + uint32_t ctl2; + + ctl2 = pci_read_config(sc->dev, pcie_pos + PCIER_DEVICE_CTL2, 4); + ctl2 |= PCIEM_CTL2_ARI; + pci_write_config(sc->dev, pcie_pos + PCIER_DEVICE_CTL2, ctl2, 4); + + sc->flags |= PCIB_ENABLE_ARI; + + if (bootverbose) + device_printf(sc->dev, "Enable ARI\n"); +} + +/* * PCIB interface. */ int pcib_maxslots(device_t dev) { - return(PCI_SLOTMAX); + return (PCI_SLOTMAX); +} + +int +pcib_maxfuncs(device_t dev) +{ + return (PCI_FUNCMAX); +} + +static int +pcib_ari_maxslots(device_t dev) +{ + struct pcib_softc *sc; + + sc = device_get_softc(dev); + + if (sc->flags & PCIB_ENABLE_ARI) + return (PCIE_ARI_SLOTMAX); + else + return (PCI_SLOTMAX); +} + +static int +pcib_ari_maxfuncs(device_t dev) +{ + struct pcib_softc *sc; + + sc = device_get_softc(dev); + + if (sc->flags & PCIB_ENABLE_ARI) + return (PCIE_ARI_FUNCMAX); + else + return (PCI_FUNCMAX); } /* * Since we are a child of a PCI bus, its parent must support the pcib interface. */ -uint32_t +static uint32_t pcib_read_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, int width) { - return(PCIB_READ_CONFIG(device_get_parent(device_get_parent(dev)), b, s, f, reg, width)); + + pcib_xlate_ari(dev, b, &s, &f); + return(PCIB_READ_CONFIG(device_get_parent(device_get_parent(dev)), b, s, + f, reg, width)); } -void +static void pcib_write_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, uint32_t val, int width) { - PCIB_WRITE_CONFIG(device_get_parent(device_get_parent(dev)), b, s, f, reg, val, width); + + pcib_xlate_ari(dev, b, &s, &f); + PCIB_WRITE_CONFIG(device_get_parent(device_get_parent(dev)), b, s, f, + reg, val, width); } /* @@ -1741,3 +1827,60 @@ pcib_power_for_sleep(device_t pcib, device_t dev, int *pstate) bus = device_get_parent(pcib); return (PCIB_POWER_FOR_SLEEP(bus, dev, pstate)); } + +/* + * Check that the downstream port (pcib) and the endpoint device (dev) both + * support ARI. If so, enable it and return 0, otherwise return an error. + */ +static int +pcib_try_enable_ari(device_t pcib, device_t dev) +{ + struct pcib_softc *sc; + int error; + uint32_t cap2; + int ari_cap_off; + uint32_t ari_ver; + uint32_t pcie_pos; + + sc = device_get_softc(pcib); + + /* + * ARI is controlled in a register in the PCIe capability structure. + * If the downstream port does not have the PCIe capability structure + * then it does not support ARI. + */ + error = pci_find_cap(pcib, PCIY_EXPRESS, &pcie_pos); + if (error != 0) + return (ENODEV); + + /* Check that the PCIe port advertises ARI support. */ + cap2 = pci_read_config(pcib, pcie_pos + PCIER_DEVICE_CAP2, 4); + if (!(cap2 & PCIEM_CAP2_ARI)) + return (ENODEV); + + /* + * Check that the endpoint device advertises ARI support via the ARI + * extended capability structure. + */ + error = pci_find_extcap(dev, PCIZ_ARI, &ari_cap_off); + if (error != 0) + return (ENODEV); + + /* + * Finally, check that the endpoint device supports the same version + * of ARI that we do. + */ + ari_ver = pci_read_config(dev, ari_cap_off, 4); + if (PCI_EXTCAP_VER(ari_ver) != PCIB_SUPPORTED_ARI_VER) { + if (bootverbose) + device_printf(pcib, + "Unsupported version of ARI (%d) detected\n", + PCI_EXTCAP_VER(ari_ver)); + + return (ENXIO); + } + + pcib_enable_ari(sc, pcie_pos); + + return (0); +} diff --git a/sys/dev/pci/pcib_if.m b/sys/dev/pci/pcib_if.m index b946c0f..b91b9c8 100644 --- a/sys/dev/pci/pcib_if.m +++ b/sys/dev/pci/pcib_if.m @@ -47,6 +47,14 @@ METHOD int maxslots { }; # +# +# Return the number of functions on the attached PCI bus. +# +METHOD int maxfuncs { + device_t dev; +} default pcib_maxfuncs; + +# # Read configuration space on the PCI bus. The bus, slot and func # arguments determine the device which is being read and the reg # argument is a byte offset into configuration space for that @@ -154,3 +162,12 @@ METHOD int power_for_sleep { device_t dev; int *pstate; }; + +# +# Enable Alternative RID Interpretation if both the downstream port (pcib) +# and the endpoint device (dev) both support it. +# +METHOD int try_enable_ari { + device_t pcib; + device_t dev; +}; diff --git a/sys/dev/pci/pcib_private.h b/sys/dev/pci/pcib_private.h index e9d4c4b..15a84e8 100644 --- a/sys/dev/pci/pcib_private.h +++ b/sys/dev/pci/pcib_private.h @@ -93,6 +93,7 @@ struct pcib_softc #define PCIB_SUBTRACTIVE 0x1 #define PCIB_DISABLE_MSI 0x2 #define PCIB_DISABLE_MSIX 0x4 +#define PCIB_ENABLE_ARI 0x8 uint16_t command; /* command register */ u_int domain; /* domain number */ u_int pribus; /* primary bus number */ @@ -115,6 +116,8 @@ struct pcib_softc uint8_t seclat; /* secondary bus latency timer */ }; +#define PCIB_SUPPORTED_ARI_VER 1 + typedef uint32_t pci_read_config_fn(int b, int s, int f, int reg, int width); #ifdef NEW_PCIB @@ -135,8 +138,7 @@ int pcib_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r); #endif int pcib_maxslots(device_t dev); -uint32_t pcib_read_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, int width); -void pcib_write_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, uint32_t val, int width); +int pcib_maxfuncs(device_t dev); int pcib_route_interrupt(device_t pcib, device_t dev, int pin); int pcib_alloc_msi(device_t pcib, device_t dev, int count, int maxcount, int *irqs); int pcib_release_msi(device_t pcib, device_t dev, int count, int *irqs); diff --git a/sys/dev/pci/pcireg.h b/sys/dev/pci/pcireg.h index afd140f..b67cd1f 100644 --- a/sys/dev/pci/pcireg.h +++ b/sys/dev/pci/pcireg.h @@ -48,6 +48,12 @@ #define PCIE_REGMAX 4095 /* highest supported config register addr. */ #define PCI_MAXHDRTYPE 2 +#define PCIE_ARI_SLOTMAX 0 +#define PCIE_ARI_FUNCMAX 255 + +#define PCIE_ARI_SLOT(func) (((func) >> 3) & PCI_SLOTMAX) +#define PCIE_ARI_FUNC(func) ((func) & PCI_FUNCMAX) + /* PCI config header registers for all devices */ #define PCIR_DEVVENDOR 0x00 @@ -774,6 +780,7 @@ #define PCIEM_ROOT_STA_PME_STATUS 0x00010000 #define PCIEM_ROOT_STA_PME_PEND 0x00020000 #define PCIER_DEVICE_CAP2 0x24 +#define PCIEM_CAP2_ARI 0x20 #define PCIER_DEVICE_CTL2 0x28 #define PCIEM_CTL2_COMP_TIMEOUT_VAL 0x000f #define PCIEM_CTL2_COMP_TIMEOUT_DIS 0x0010 @@ -894,3 +901,4 @@ /* Serial Number definitions */ #define PCIR_SERIAL_LOW 0x04 #define PCIR_SERIAL_HIGH 0x08 + diff --git a/usr.sbin/pciconf/cap.c b/usr.sbin/pciconf/cap.c index d7296a3..bdcbe1d 100644 --- a/usr.sbin/pciconf/cap.c +++ b/usr.sbin/pciconf/cap.c @@ -410,7 +410,7 @@ aspm_string(uint8_t aspm) static void cap_express(int fd, struct pci_conf *p, uint8_t ptr) { - uint32_t cap; + uint32_t cap, cap2; uint16_t ctl, flags, sta; flags = read_config(fd, &p->pc_sel, ptr + PCIER_FLAGS, 2); @@ -452,6 +452,7 @@ cap_express(int fd, struct pci_conf *p, uint8_t ptr) if (flags & PCIEM_FLAGS_IRQ) printf(" IRQ %d", (flags & PCIEM_FLAGS_IRQ) >> 9); cap = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CAP, 4); + cap2 = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CAP2, 4); ctl = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CTL, 2); printf(" max data %d(%d)", MAX_PAYLOAD((ctl & PCIEM_CTL_MAX_PAYLOAD) >> 5), @@ -474,6 +475,11 @@ cap_express(int fd, struct pci_conf *p, uint8_t ptr) printf(" ASPM %s(%s)", aspm_string(ctl & PCIEM_LINK_CTL_ASPMC), aspm_string((cap & PCIEM_LINK_CAP_ASPM) >> 10)); } + if ((cap2 & PCIEM_CAP2_ARI) != 0) { + ctl = read_config(fd, &p->pc_sel, ptr + PCIER_DEVICE_CTL2, 4); + printf(" ARI %s", + (ctl & PCIEM_CTL2_ARI) ? "enabled" : "disabled"); + } } static void