svn commit: r279117 - head/sys/x86/iommu

Konstantin Belousov kib at FreeBSD.org
Sat Feb 21 22:38:33 UTC 2015


Author: kib
Date: Sat Feb 21 22:38:32 2015
New Revision: 279117
URL: https://svnweb.freebsd.org/changeset/base/279117

Log:
  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 at gmail.com>
  MFC after:	1 week

Modified:
  head/sys/x86/iommu/busdma_dmar.c

Modified: head/sys/x86/iommu/busdma_dmar.c
==============================================================================
--- head/sys/x86/iommu/busdma_dmar.c	Sat Feb 21 22:27:57 2015	(r279116)
+++ head/sys/x86/iommu/busdma_dmar.c	Sat Feb 21 22:38:32 2015	(r279117)
@@ -98,6 +98,8 @@ dmar_get_requester(device_t dev, uint16_
 	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_
 		} 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_
 				 * 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_
 				 * requester ID.
 				 */
 				*rid = pci_get_rid(pcib);
+				l = pcib;
 			}
-			l = pcibp;
 		}
 	}
 	return (requester);


More information about the svn-src-all mailing list