summaryrefslogtreecommitdiffstats
path: root/sys/x86
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2015-03-01 10:39:19 +0000
committerkib <kib@FreeBSD.org>2015-03-01 10:39:19 +0000
commit76ac5da69f30c9df1e4e3077e1e693000fb18510 (patch)
treef8b9eeb5c05dcf4530a1868fa5310b26f16cb1b3 /sys/x86
parent861937e4a4b854b301c73a7ecd4f481401612410 (diff)
downloadFreeBSD-src-76ac5da69f30c9df1e4e3077e1e693000fb18510.zip
FreeBSD-src-76ac5da69f30c9df1e4e3077e1e693000fb18510.tar.gz
MFC r276949:
(only to ease merging of r279117). MFC r279117: Revert r276949 and redo the fix for PCIe/PCI bridges, which do not follow specification and do not provide PCIe capability.
Diffstat (limited to 'sys/x86')
-rw-r--r--sys/x86/iommu/busdma_dmar.c74
1 files changed, 48 insertions, 26 deletions
diff --git a/sys/x86/iommu/busdma_dmar.c b/sys/x86/iommu/busdma_dmar.c
index a397f42..d3ba834 100644
--- a/sys/x86/iommu/busdma_dmar.c
+++ b/sys/x86/iommu/busdma_dmar.c
@@ -96,11 +96,13 @@ static device_t
dmar_get_requester(device_t dev, uint16_t *rid)
{
devclass_t pci_class;
- device_t pci, pcib, requester;
+ device_t l, pci, pcib, pcip, pcibp, requester;
int cap_offset;
+ uint16_t pcie_flags;
+ bool bridge_is_pcie;
pci_class = devclass_find("pci");
- requester = dev;
+ l = requester = dev;
*rid = pci_get_rid(dev);
@@ -110,19 +112,17 @@ dmar_get_requester(device_t dev, uint16_t *rid)
* unit.
*/
for (;;) {
- pci = device_get_parent(dev);
- KASSERT(pci != NULL, ("NULL parent for pci%d:%d:%d:%d",
- pci_get_domain(dev), pci_get_bus(dev), pci_get_slot(dev),
- pci_get_function(dev)));
+ pci = device_get_parent(l);
+ KASSERT(pci != NULL, ("dmar_get_requester(%s): NULL parent "
+ "for %s", device_get_name(dev), device_get_name(l)));
KASSERT(device_get_devclass(pci) == pci_class,
- ("Non-pci parent for pci%d:%d:%d:%d",
- pci_get_domain(dev), pci_get_bus(dev), pci_get_slot(dev),
- pci_get_function(dev)));
+ ("dmar_get_requester(%s): non-pci parent %s for %s",
+ device_get_name(dev), device_get_name(pci),
+ device_get_name(l)));
pcib = device_get_parent(pci);
- KASSERT(pcib != NULL, ("NULL bridge for pci%d:%d:%d:%d",
- pci_get_domain(dev), pci_get_bus(dev), pci_get_slot(dev),
- pci_get_function(dev)));
+ KASSERT(pcib != NULL, ("dmar_get_requester(%s): NULL bridge "
+ "for %s", device_get_name(dev), device_get_name(pci)));
/*
* The parent of our "bridge" isn't another PCI bus,
@@ -130,19 +130,46 @@ dmar_get_requester(device_t dev, uint16_t *rid)
* port, and the requester ID won't be translated
* further.
*/
- if (device_get_devclass(device_get_parent(pcib)) != pci_class)
+ pcip = device_get_parent(pcib);
+ if (device_get_devclass(pcip) != pci_class)
break;
+ pcibp = device_get_parent(pcip);
- if (pci_find_cap(dev, PCIY_EXPRESS, &cap_offset) != 0) {
+ if (pci_find_cap(l, PCIY_EXPRESS, &cap_offset) == 0) {
+ /*
+ * Do not stop the loop even if the target
+ * device is PCIe, because it is possible (but
+ * unlikely) to have a PCI->PCIe bridge
+ * somewhere in the hierarchy.
+ */
+ l = pcib;
+ } else {
/*
* Device is not PCIe, it cannot be seen as a
- * requester by DMAR unit.
+ * requester by DMAR unit. Check whether the
+ * bridge is PCIe.
*/
+ bridge_is_pcie = pci_find_cap(pcib, PCIY_EXPRESS,
+ &cap_offset) == 0;
requester = pcib;
- /* Check whether the bus above is PCIe. */
- if (pci_find_cap(pcib, PCIY_EXPRESS,
- &cap_offset) == 0) {
+ /*
+ * Check for a buggy PCIe/PCI bridge that
+ * doesn't report the express capability. If
+ * the bridge above it is express but isn't a
+ * PCI bridge, then we know pcib is actually a
+ * PCIe/PCI bridge.
+ */
+ if (!bridge_is_pcie && pci_find_cap(pcibp,
+ PCIY_EXPRESS, &cap_offset) == 0) {
+ pcie_flags = pci_read_config(pcibp,
+ cap_offset + PCIER_FLAGS, 2);
+ if ((pcie_flags & PCIEM_FLAGS_TYPE) !=
+ PCIEM_TYPE_PCI_BRIDGE)
+ bridge_is_pcie = true;
+ }
+
+ if (bridge_is_pcie) {
/*
* The current device is not PCIe, but
* the bridge above it is. This is a
@@ -159,7 +186,8 @@ dmar_get_requester(device_t dev, uint16_t *rid)
* same page tables for taken and
* non-taken transactions.
*/
- *rid = PCI_RID(pci_get_bus(dev), 0, 0);
+ *rid = PCI_RID(pci_get_bus(l), 0, 0);
+ l = pcibp;
} else {
/*
* Neither the device nor the bridge
@@ -169,15 +197,9 @@ dmar_get_requester(device_t dev, uint16_t *rid)
* requester ID.
*/
*rid = pci_get_rid(pcib);
+ l = pcib;
}
}
- /*
- * Do not stop the loop even if the target device is
- * PCIe, because it is possible (but unlikely) to have
- * a PCI->PCIe bridge somewhere in the hierarchy.
- */
-
- dev = pcib;
}
return (requester);
}
OpenPOWER on IntegriCloud