diff options
author | kib <kib@FreeBSD.org> | 2015-03-01 10:39:19 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2015-03-01 10:39:19 +0000 |
commit | 76ac5da69f30c9df1e4e3077e1e693000fb18510 (patch) | |
tree | f8b9eeb5c05dcf4530a1868fa5310b26f16cb1b3 /sys/x86 | |
parent | 861937e4a4b854b301c73a7ecd4f481401612410 (diff) | |
download | FreeBSD-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.c | 74 |
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); } |