diff options
Diffstat (limited to 'sys/dev/pci/pci.c')
-rw-r--r-- | sys/dev/pci/pci.c | 150 |
1 files changed, 112 insertions, 38 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 091d02c..701f1ea 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -78,6 +78,7 @@ __FBSDID("$FreeBSD$"); (((cfg)->hdrtype == PCIM_HDRTYPE_NORMAL && reg == PCIR_BIOS) || \ ((cfg)->hdrtype == PCIM_HDRTYPE_BRIDGE && reg == PCIR_BIOS_1)) +static int pci_has_quirk(uint32_t devid, int quirk); static pci_addr_t pci_mapbase(uint64_t mapreg); static const char *pci_maptype(uint64_t mapreg); static int pci_mapsize(uint64_t testval); @@ -119,6 +120,7 @@ static void pci_enable_msix(device_t dev, u_int index, static void pci_mask_msix(device_t dev, u_int index); static void pci_unmask_msix(device_t dev, u_int index); static int pci_msi_blacklisted(void); +static int pci_msix_blacklisted(void); static void pci_resume_msi(device_t dev); static void pci_resume_msix(device_t dev); static int pci_remap_intr_method(device_t bus, device_t dev, @@ -149,7 +151,7 @@ static device_method_t pci_methods[] = { DEVMETHOD(bus_delete_resource, pci_delete_resource), DEVMETHOD(bus_alloc_resource, pci_alloc_resource), DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), - DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), + DEVMETHOD(bus_release_resource, pci_release_resource), DEVMETHOD(bus_activate_resource, pci_activate_resource), DEVMETHOD(bus_deactivate_resource, pci_deactivate_resource), DEVMETHOD(bus_child_detached, pci_child_detached), @@ -185,7 +187,7 @@ static device_method_t pci_methods[] = { DEFINE_CLASS_0(pci, pci_driver, pci_methods, sizeof(struct pci_softc)); static devclass_t pci_devclass; -DRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, 0); +DRIVER_MODULE(pci, pcib, pci_driver, pci_devclass, pci_modevent, NULL); MODULE_VERSION(pci, 1); static char *pci_vendordata; @@ -195,15 +197,16 @@ struct pci_quirk { uint32_t devid; /* Vendor/device of the card */ int type; #define PCI_QUIRK_MAP_REG 1 /* PCI map register in weird place */ -#define PCI_QUIRK_DISABLE_MSI 2 /* MSI/MSI-X doesn't work */ +#define PCI_QUIRK_DISABLE_MSI 2 /* Neither MSI nor MSI-X work */ #define PCI_QUIRK_ENABLE_MSI_VM 3 /* Older chipset in VM where MSI works */ #define PCI_QUIRK_UNMAP_REG 4 /* Ignore PCI map register */ +#define PCI_QUIRK_DISABLE_MSIX 5 /* MSI-X doesn't work */ int arg1; int arg2; }; static const struct pci_quirk pci_quirks[] = { - /* The Intel 82371AB and 82443MX has a map register at offset 0x90. */ + /* The Intel 82371AB and 82443MX have a map register at offset 0x90. */ { 0x71138086, PCI_QUIRK_MAP_REG, 0x90, 0 }, { 0x719b8086, PCI_QUIRK_MAP_REG, 0x90, 0 }, /* As does the Serverworks OSB4 (the SMBus mapping register) */ @@ -238,8 +241,8 @@ static const struct pci_quirk pci_quirks[] = { * MSI-X allocation doesn't work properly for devices passed through * by VMware up to at least ESXi 5.1. */ - { 0x079015ad, PCI_QUIRK_DISABLE_MSI, 0, 0 }, /* PCI/PCI-X */ - { 0x07a015ad, PCI_QUIRK_DISABLE_MSI, 0, 0 }, /* PCIe */ + { 0x079015ad, PCI_QUIRK_DISABLE_MSIX, 0, 0 }, /* PCI/PCI-X */ + { 0x07a015ad, PCI_QUIRK_DISABLE_MSIX, 0, 0 }, /* PCIe */ /* * Some virtualization environments emulate an older chipset @@ -321,7 +324,7 @@ SYSCTL_INT(_hw_pci, OID_AUTO, enable_msix, CTLFLAG_RW, &pci_do_msix, 1, static int pci_honor_msi_blacklist = 1; TUNABLE_INT("hw.pci.honor_msi_blacklist", &pci_honor_msi_blacklist); SYSCTL_INT(_hw_pci, OID_AUTO, honor_msi_blacklist, CTLFLAG_RD, - &pci_honor_msi_blacklist, 1, "Honor chipset blacklist for MSI"); + &pci_honor_msi_blacklist, 1, "Honor chipset blacklist for MSI/MSI-X"); #if defined(__i386__) || defined(__amd64__) static int pci_usb_takeover = 1; @@ -334,6 +337,18 @@ 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_has_quirk(uint32_t devid, int quirk) +{ + const struct pci_quirk *q; + + for (q = &pci_quirks[0]; q->devid; q++) { + if (q->devid == devid && q->type == quirk) + return (1); + } + return (0); +} + /* Find a device_t by bus/slot/function in domain 0 */ device_t @@ -1426,8 +1441,8 @@ pci_alloc_msix_method(device_t dev, device_t child, int *count) if (cfg->msi.msi_alloc != 0 || cfg->msix.msix_alloc != 0) return (ENXIO); - /* If MSI is blacklisted for this system, fail. */ - if (pci_msi_blacklisted()) + /* If MSI-X is blacklisted for this system, fail. */ + if (pci_msix_blacklisted()) return (ENXIO); /* MSI-X capability present? */ @@ -1983,38 +1998,15 @@ pci_remap_intr_method(device_t bus, device_t dev, u_int irq) int pci_msi_device_blacklisted(device_t dev) { - const struct pci_quirk *q; if (!pci_honor_msi_blacklist) return (0); - for (q = &pci_quirks[0]; q->devid; q++) { - if (q->devid == pci_get_devid(dev) && - q->type == PCI_QUIRK_DISABLE_MSI) - return (1); - } - return (0); -} - -/* - * Returns true if a specified chipset supports MSI when it is - * emulated hardware in a virtual machine. - */ -static int -pci_msi_vm_chipset(device_t dev) -{ - const struct pci_quirk *q; - - for (q = &pci_quirks[0]; q->devid; q++) { - if (q->devid == pci_get_devid(dev) && - q->type == PCI_QUIRK_ENABLE_MSI_VM) - return (1); - } - return (0); + return (pci_has_quirk(pci_get_devid(dev), PCI_QUIRK_DISABLE_MSI)); } /* - * Determine if MSI is blacklisted globally on this sytem. Currently, + * Determine if MSI is blacklisted globally on this system. Currently, * we just check for blacklisted chipsets as represented by the * host-PCI bridge at device 0:0:0. In the future, it may become * necessary to check other system attributes, such as the kenv values @@ -2031,9 +2023,14 @@ pci_msi_blacklisted(void) /* Blacklist all non-PCI-express and non-PCI-X chipsets. */ if (!(pcie_chipset || pcix_chipset)) { if (vm_guest != VM_GUEST_NO) { + /* + * Whitelist older chipsets in virtual + * machines known to support MSI. + */ dev = pci_find_bsf(0, 0, 0); if (dev != NULL) - return (pci_msi_vm_chipset(dev) == 0); + return (!pci_has_quirk(pci_get_devid(dev), + PCI_QUIRK_ENABLE_MSI_VM)); } return (1); } @@ -2045,6 +2042,45 @@ pci_msi_blacklisted(void) } /* + * Returns true if the specified device is blacklisted because MSI-X + * doesn't work. Note that this assumes that if MSI doesn't work, + * MSI-X doesn't either. + */ +int +pci_msix_device_blacklisted(device_t dev) +{ + + if (!pci_honor_msi_blacklist) + return (0); + + if (pci_has_quirk(pci_get_devid(dev), PCI_QUIRK_DISABLE_MSIX)) + return (1); + + return (pci_msi_device_blacklisted(dev)); +} + +/* + * Determine if MSI-X is blacklisted globally on this system. If MSI + * is blacklisted, assume that MSI-X is as well. Check for additional + * chipsets where MSI works but MSI-X does not. + */ +static int +pci_msix_blacklisted(void) +{ + device_t dev; + + if (!pci_honor_msi_blacklist) + return (0); + + dev = pci_find_bsf(0, 0, 0); + if (dev != NULL && pci_has_quirk(pci_get_devid(dev), + PCI_QUIRK_DISABLE_MSIX)) + return (1); + + return (pci_msi_blacklisted()); +} + +/* * Attempt to allocate *count MSI messages. The actual number allocated is * returned in *count. After this function returns, each message will be * available to the driver as SYS_RES_IRQ resources starting at a rid 1. @@ -4236,11 +4272,11 @@ struct resource * pci_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { - struct pci_devinfo *dinfo = device_get_ivars(child); - struct resource_list *rl = &dinfo->resources; + struct pci_devinfo *dinfo; + struct resource_list *rl; struct resource_list_entry *rle; struct resource *res; - pcicfgregs *cfg = &dinfo->cfg; + pcicfgregs *cfg; if (device_get_parent(child) != dev) return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child, @@ -4249,6 +4285,9 @@ pci_alloc_resource(device_t dev, device_t child, int type, int *rid, /* * Perform lazy resource allocation */ + dinfo = device_get_ivars(child); + rl = &dinfo->resources; + cfg = &dinfo->cfg; switch (type) { case SYS_RES_IRQ: /* @@ -4304,6 +4343,41 @@ pci_alloc_resource(device_t dev, device_t child, int type, int *rid, } int +pci_release_resource(device_t dev, device_t child, int type, int rid, + struct resource *r) +{ + struct pci_devinfo *dinfo; + struct resource_list *rl; + pcicfgregs *cfg; + + if (device_get_parent(child) != dev) + return (BUS_RELEASE_RESOURCE(device_get_parent(dev), child, + type, rid, r)); + + dinfo = device_get_ivars(child); + cfg = &dinfo->cfg; +#ifdef NEW_PCIB + /* + * PCI-PCI bridge I/O window resources are not BARs. For + * those allocations just pass the request up the tree. + */ + if (cfg->hdrtype == PCIM_HDRTYPE_BRIDGE && + (type == SYS_RES_IOPORT || type == SYS_RES_MEMORY)) { + switch (rid) { + case PCIR_IOBASEL_1: + case PCIR_MEMBASE_1: + case PCIR_PMBASEL_1: + return (bus_generic_release_resource(dev, child, type, + rid, r)); + } + } +#endif + + rl = &dinfo->resources; + return (resource_list_release(rl, dev, child, type, rid, r)); +} + +int pci_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { |