Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 1 Mar 2015 10:39:19 +0000 (UTC)
From:      Konstantin Belousov <kib@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r279486 - stable/10/sys/x86/iommu
Message-ID:  <201503011039.t21AdJ15074563@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Sun Mar  1 10:39:19 2015
New Revision: 279486
URL: https://svnweb.freebsd.org/changeset/base/279486

Log:
  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.

Modified:
  stable/10/sys/x86/iommu/busdma_dmar.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/x86/iommu/busdma_dmar.c
==============================================================================
--- stable/10/sys/x86/iommu/busdma_dmar.c	Sun Mar  1 10:35:54 2015	(r279485)
+++ stable/10/sys/x86/iommu/busdma_dmar.c	Sun Mar  1 10:39:19 2015	(r279486)
@@ -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_
 	 * 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_
 		 * 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_
 				 * 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_
 				 * 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);
 }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201503011039.t21AdJ15074563>