Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 25 Mar 2014 20:17:58 +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: r263747 - stable/10/sys/x86/iommu
Message-ID:  <201403252017.s2PKHwMG073406@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kib
Date: Tue Mar 25 20:17:57 2014
New Revision: 263747
URL: http://svnweb.freebsd.org/changeset/base/263747

Log:
  MFC r263306:
  Add some support for the PCI(e)-PCI bridges to the Intel VT-d driver.

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

Modified: stable/10/sys/x86/iommu/busdma_dmar.c
==============================================================================
--- stable/10/sys/x86/iommu/busdma_dmar.c	Tue Mar 25 20:16:28 2014	(r263746)
+++ stable/10/sys/x86/iommu/busdma_dmar.c	Tue Mar 25 20:17:57 2014	(r263747)
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/taskqueue.h>
 #include <sys/tree.h>
 #include <sys/uio.h>
+#include <dev/pci/pcireg.h>
 #include <dev/pci/pcivar.h>
 #include <vm/vm.h>
 #include <vm/vm_extern.h>
@@ -69,15 +70,10 @@ __FBSDID("$FreeBSD$");
  */
 
 static bool
-dmar_bus_dma_is_dev_disabled(device_t dev)
+dmar_bus_dma_is_dev_disabled(int domain, int bus, int slot, int func)
 {
 	char str[128], *env;
-	int domain, bus, slot, func;
 
-	domain = pci_get_domain(dev);
-	bus = pci_get_bus(dev);
-	slot = pci_get_slot(dev);
-	func = pci_get_function(dev);
 	snprintf(str, sizeof(str), "hw.busdma.pci%d.%d.%d.%d.bounce",
 	    domain, bus, slot, func);
 	env = getenv(str);
@@ -87,11 +83,119 @@ dmar_bus_dma_is_dev_disabled(device_t de
 	return (true);
 }
 
+/*
+ * Given original device, find the requester ID that will be seen by
+ * the DMAR unit and used for page table lookup.  PCI bridges may take
+ * ownership of transactions from downstream devices, so it may not be
+ * the same as the BSF of the target device.  In those cases, all
+ * devices downstream of the bridge must share a single mapping
+ * domain, and must collectively be assigned to use either DMAR or
+ * bounce mapping.
+ */
+static device_t
+dmar_get_requester(device_t dev, int *bus, int *slot, int *func)
+{
+	devclass_t pci_class;
+	device_t pci, pcib, requester;
+	int cap_offset;
+
+	pci_class = devclass_find("pci");
+	requester = dev;
+
+	*bus = pci_get_bus(dev);
+	*slot = pci_get_slot(dev);
+	*func = pci_get_function(dev);
+
+	/*
+	 * Walk the bridge hierarchy from the target device to the
+	 * host port to find the translating bridge nearest the DMAR
+	 * 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)));
+		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)));
+
+		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)));
+
+		/*
+		 * The parent of our "bridge" isn't another PCI bus,
+		 * so pcib isn't a PCI->PCI bridge but rather a host
+		 * port, and the requester ID won't be translated
+		 * further.
+		 */
+		if (device_get_devclass(device_get_parent(pcib)) != pci_class)
+			break;
+
+		if (pci_find_cap(dev, PCIY_EXPRESS, &cap_offset) != 0) {
+			/*
+			 * Device is not PCIe, it cannot be seen as a
+			 * requester by DMAR unit.
+			 */
+			requester = pcib;
+
+			/* Check whether the bus above is PCIe. */
+			if (pci_find_cap(pcib, PCIY_EXPRESS,
+			    &cap_offset) == 0) {
+				/*
+				 * The current device is not PCIe, but
+				 * the bridge above it is.  This is a
+				 * PCIe->PCI bridge.  Assume that the
+				 * requester ID will be the secondary
+				 * bus number with slot and function
+				 * set to zero.
+				 *
+				 * XXX: Doesn't handle the case where
+				 * the bridge is PCIe->PCI-X, and the
+				 * bridge will only take ownership of
+				 * requests in some cases.  We should
+				 * provide context entries with the
+				 * same page tables for taken and
+				 * non-taken transactions.
+				 */
+				*bus = pci_get_bus(dev);
+				*slot = *func = 0;
+			} else {
+				/*
+				 * Neither the device nor the bridge
+				 * above it are PCIe.  This is a
+				 * conventional PCI->PCI bridge, which
+				 * will use the bridge's BSF as the
+				 * requester ID.
+				 */
+				*bus = pci_get_bus(pcib);
+				*slot = pci_get_slot(pcib);
+				*func = pci_get_function(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);
+}
+
 struct dmar_ctx *
 dmar_instantiate_ctx(struct dmar_unit *dmar, device_t dev, bool rmrr)
 {
+	device_t requester;
 	struct dmar_ctx *ctx;
 	bool disabled;
+	int bus, slot, func;
+
+	requester = dmar_get_requester(dev, &bus, &slot, &func);
 
 	/*
 	 * If the user requested the IOMMU disabled for the device, we
@@ -100,11 +204,11 @@ dmar_instantiate_ctx(struct dmar_unit *d
 	 * Instead provide the identity mapping for the device
 	 * context.
 	 */
-	disabled = dmar_bus_dma_is_dev_disabled(dev);
-	ctx = dmar_get_ctx(dmar, dev, disabled, rmrr);
+	disabled = dmar_bus_dma_is_dev_disabled(pci_get_domain(dev), bus,
+	    slot, func);
+	ctx = dmar_get_ctx(dmar, requester, bus, slot, func, disabled, rmrr);
 	if (ctx == NULL)
 		return (NULL);
-	ctx->ctx_tag.owner = dev;
 	if (disabled) {
 		/*
 		 * Keep the first reference on context, release the

Modified: stable/10/sys/x86/iommu/intel_ctx.c
==============================================================================
--- stable/10/sys/x86/iommu/intel_ctx.c	Tue Mar 25 20:16:28 2014	(r263746)
+++ stable/10/sys/x86/iommu/intel_ctx.c	Tue Mar 25 20:17:57 2014	(r263747)
@@ -262,17 +262,15 @@ dmar_ctx_dtr(struct dmar_ctx *ctx, bool 
 }
 
 struct dmar_ctx *
-dmar_get_ctx(struct dmar_unit *dmar, device_t dev, bool id_mapped, bool rmrr_init)
+dmar_get_ctx(struct dmar_unit *dmar, device_t dev, int bus, int slot, int func,
+    bool id_mapped, bool rmrr_init)
 {
 	struct dmar_ctx *ctx, *ctx1;
 	dmar_ctx_entry_t *ctxp;
 	struct sf_buf *sf;
-	int bus, slot, func, error, mgaw;
+	int error, mgaw;
 	bool enable;
 
-	bus = pci_get_bus(dev);
-	slot = pci_get_slot(dev);
-	func = pci_get_function(dev);
 	enable = false;
 	TD_PREP_PINNED_ASSERT;
 	DMAR_LOCK(dmar);
@@ -356,6 +354,7 @@ dmar_get_ctx(struct dmar_unit *dmar, dev
 		ctx = dmar_find_ctx_locked(dmar, bus, slot, func);
 		if (ctx == NULL) {
 			ctx = ctx1;
+			ctx->ctx_tag.owner = dev;
 			ctx->domain = alloc_unrl(dmar->domids);
 			if (ctx->domain == -1) {
 				DMAR_UNLOCK(dmar);
@@ -376,9 +375,11 @@ dmar_get_ctx(struct dmar_unit *dmar, dev
 			LIST_INSERT_HEAD(&dmar->contexts, ctx, link);
 			ctx_id_entry_init(ctx, ctxp);
 			device_printf(dev,
-			    "dmar%d pci%d:%d:%d:%d domain %d mgaw %d agaw %d\n",
+			    "dmar%d pci%d:%d:%d:%d domain %d mgaw %d "
+			    "agaw %d %s-mapped\n",
 			    dmar->unit, dmar->segment, bus, slot,
-			    func, ctx->domain, ctx->mgaw, ctx->agaw);
+			    func, ctx->domain, ctx->mgaw, ctx->agaw,
+			    id_mapped ? "id" : "re");
 		} else {
 			dmar_ctx_dtr(ctx1, true, true);
 		}

Modified: stable/10/sys/x86/iommu/intel_dmar.h
==============================================================================
--- stable/10/sys/x86/iommu/intel_dmar.h	Tue Mar 25 20:16:28 2014	(r263746)
+++ stable/10/sys/x86/iommu/intel_dmar.h	Tue Mar 25 20:17:57 2014	(r263747)
@@ -270,7 +270,7 @@ void ctx_free_pgtbl(struct dmar_ctx *ctx
 struct dmar_ctx *dmar_instantiate_ctx(struct dmar_unit *dmar, device_t dev,
     bool rmrr);
 struct dmar_ctx *dmar_get_ctx(struct dmar_unit *dmar, device_t dev,
-    bool id_mapped, bool rmrr_init);
+    int bus, int slot, int func, bool id_mapped, bool rmrr_init);
 void dmar_free_ctx_locked(struct dmar_unit *dmar, struct dmar_ctx *ctx);
 void dmar_free_ctx(struct dmar_ctx *ctx);
 struct dmar_ctx *dmar_find_ctx_locked(struct dmar_unit *dmar, int bus,



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