summaryrefslogtreecommitdiffstats
path: root/sys/x86/iommu
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2015-02-21 22:38:32 +0000
committerkib <kib@FreeBSD.org>2015-02-21 22:38:32 +0000
commit32241f2de462a3c017304705d414b9dcc9725984 (patch)
tree9ca98790553a62cc17929d6ea9b05f29e18e378c /sys/x86/iommu
parent031e6e287952c6756df62ff13ea7d6a7e8db6120 (diff)
downloadFreeBSD-src-32241f2de462a3c017304705d414b9dcc9725984.zip
FreeBSD-src-32241f2de462a3c017304705d414b9dcc9725984.tar.gz
Revert r276949 and redo the fix for PCIe/PCI bridges, which do not
follow specification and do not provide PCIe capability. Verify if the port above such bridge is downstream PCIe (or root port) and treat the bridge as PCIe/PCI then. This allows to avoid maintaining the table of device ids for bridges without capability, while still calculate correct request originator for devices behind the bridge. Submitted by: Jason Harmening <jason.harmening@gmail.com> MFC after: 1 week
Diffstat (limited to 'sys/x86/iommu')
-rw-r--r--sys/x86/iommu/busdma_dmar.c32
1 files changed, 26 insertions, 6 deletions
diff --git a/sys/x86/iommu/busdma_dmar.c b/sys/x86/iommu/busdma_dmar.c
index 698a791..4efd7b8 100644
--- a/sys/x86/iommu/busdma_dmar.c
+++ b/sys/x86/iommu/busdma_dmar.c
@@ -98,6 +98,8 @@ dmar_get_requester(device_t dev, uint16_t *rid)
devclass_t pci_class;
device_t l, pci, pcib, pcip, pcibp, requester;
int cap_offset;
+ uint16_t pcie_flags;
+ bool bridge_is_pcie;
pci_class = devclass_find("pci");
l = requester = dev;
@@ -144,13 +146,30 @@ dmar_get_requester(device_t dev, uint16_t *rid)
} 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.
*/
- requester = pcibp;
+ bridge_is_pcie = pci_find_cap(pcib, PCIY_EXPRESS,
+ &cap_offset) == 0;
+ requester = pcib;
- /* Check whether the bus above the bridge is PCIe. */
- if (pci_find_cap(pcibp, 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
@@ -168,6 +187,7 @@ dmar_get_requester(device_t dev, uint16_t *rid)
* non-taken transactions.
*/
*rid = PCI_RID(pci_get_bus(l), 0, 0);
+ l = pcibp;
} else {
/*
* Neither the device nor the bridge
@@ -177,8 +197,8 @@ dmar_get_requester(device_t dev, uint16_t *rid)
* requester ID.
*/
*rid = pci_get_rid(pcib);
+ l = pcib;
}
- l = pcibp;
}
}
return (requester);
OpenPOWER on IntegriCloud