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);
 }
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to