From owner-svn-src-all@FreeBSD.ORG Tue Mar 25 20:17:58 2014 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id C3623584; Tue, 25 Mar 2014 20:17:58 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id A42EDEC4; Tue, 25 Mar 2014 20:17:58 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.8/8.14.8) with ESMTP id s2PKHwaF073409; Tue, 25 Mar 2014 20:17:58 GMT (envelope-from kib@svn.freebsd.org) Received: (from kib@localhost) by svn.freebsd.org (8.14.8/8.14.8/Submit) id s2PKHwMG073406; Tue, 25 Mar 2014 20:17:58 GMT (envelope-from kib@svn.freebsd.org) Message-Id: <201403252017.s2PKHwMG073406@svn.freebsd.org> From: Konstantin Belousov Date: Tue, 25 Mar 2014 20:17:58 +0000 (UTC) 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 X-SVN-Group: stable-10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.17 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 25 Mar 2014 20:17:58 -0000 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 #include #include +#include #include #include #include @@ -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,