summaryrefslogtreecommitdiffstats
path: root/sys/dev/pci/pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/pci/pci.c')
-rw-r--r--sys/dev/pci/pci.c150
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)
{
OpenPOWER on IntegriCloud