From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1161549AbdKQSzy (ORCPT ); Fri, 17 Nov 2017 13:55:54 -0500 Received: from mga03.intel.com ([134.134.136.65]:54137 "EHLO mga03.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S934518AbdKQSy0 (ORCPT ); Fri, 17 Nov 2017 13:54:26 -0500 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.44,410,1505804400"; d="scan'208";a="3461162" From: Jacob Pan To: iommu@lists.linux-foundation.org, LKML , Joerg Roedel , David Woodhouse , Greg Kroah-Hartman , Rafael Wysocki , Alex Williamson Cc: "Liu, Yi L" , Lan Tianyu , "Tian, Kevin" , Raj Ashok , Jean Delvare , "Christoph Hellwig" , Jacob Pan , Liu@vger.kernel.org, Yi L Subject: [PATCH v3 02/16] iommu/vt-d: add bind_pasid_table function Date: Fri, 17 Nov 2017 10:55:00 -0800 Message-Id: <1510944914-54430-3-git-send-email-jacob.jun.pan@linux.intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1510944914-54430-1-git-send-email-jacob.jun.pan@linux.intel.com> References: <1510944914-54430-1-git-send-email-jacob.jun.pan@linux.intel.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add Intel VT-d ops to the generic iommu_bind_pasid_table API functions. The primary use case is for direct assignment of SVM capable device. Originated from emulated IOMMU in the guest, the request goes through many layers (e.g. VFIO). Upon calling host IOMMU driver, caller passes guest PASID table pointer (GPA) and size. Device context table entry is modified by Intel IOMMU specific bind_pasid_table function. This will turn on nesting mode and matching translation type. The unbind operation restores default context mapping. Signed-off-by: Jacob Pan Signed-off-by: Liu, Yi L Signed-off-by: Ashok Raj --- drivers/iommu/intel-iommu.c | 107 ++++++++++++++++++++++++++++++++++++++++++ include/linux/dma_remapping.h | 1 + 2 files changed, 108 insertions(+) diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 2087cd8..3d1901d 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -5176,6 +5176,7 @@ static void intel_iommu_put_resv_regions(struct device *dev, #ifdef CONFIG_INTEL_IOMMU_SVM #define MAX_NR_PASID_BITS (20) +#define MIN_NR_PASID_BITS (5) static inline unsigned long intel_iommu_get_pts(struct intel_iommu *iommu) { /* @@ -5302,6 +5303,108 @@ struct intel_iommu *intel_svm_device_to_iommu(struct device *dev) return iommu; } + +static int intel_iommu_bind_pasid_table(struct iommu_domain *domain, + struct device *dev, struct pasid_table_config *pasidt_binfo) +{ + struct intel_iommu *iommu; + struct context_entry *context; + struct dmar_domain *dmar_domain = to_dmar_domain(domain); + struct device_domain_info *info; + struct pci_dev *pdev; + u8 bus, devfn, host_table_pasid_bits; + u16 did, sid; + int ret = 0; + unsigned long flags; + u64 ctx_lo; + + iommu = device_to_iommu(dev, &bus, &devfn); + if (!iommu) + return -ENODEV; + /* VT-d spec 9.4 says pasid table size is encoded as 2^(x+5) */ + host_table_pasid_bits = intel_iommu_get_pts(iommu) + MIN_NR_PASID_BITS; + if (!pasidt_binfo || pasidt_binfo->pasid_bits > host_table_pasid_bits || + pasidt_binfo->pasid_bits < MIN_NR_PASID_BITS) { + pr_err("Invalid gPASID bits %d, host range %d - %d\n", + pasidt_binfo->pasid_bits, + MIN_NR_PASID_BITS, host_table_pasid_bits); + return -ERANGE; + } + + pdev = to_pci_dev(dev); + sid = PCI_DEVID(bus, devfn); + info = dev->archdata.iommu; + + if (!info) { + dev_err(dev, "Invalid device domain info\n"); + ret = -EINVAL; + goto out; + } + if (!info->pasid_enabled) { + ret = pci_enable_pasid(pdev, info->pasid_supported & ~1); + if (ret) { + dev_err(dev, "Failed to enable PASID\n"); + goto out; + } + } + if (!device_context_mapped(iommu, bus, devfn)) { + pr_warn("ctx not mapped for bus devfn %x:%x\n", bus, devfn); + ret = -EINVAL; + goto out; + } + spin_lock_irqsave(&iommu->lock, flags); + context = iommu_context_addr(iommu, bus, devfn, 0); + if (!context) { + ret = -EINVAL; + goto out_unlock; + } + + /* Anticipate guest to use SVM and owns the first level, so we turn + * nested mode on + */ + ctx_lo = context[0].lo; + ctx_lo |= CONTEXT_NESTE | CONTEXT_PRS | CONTEXT_PASIDE; + ctx_lo &= ~CONTEXT_TT_MASK; + ctx_lo |= CONTEXT_TT_DEV_IOTLB << 2; + context[0].lo = ctx_lo; + + /* Assign guest PASID table pointer and size order */ + ctx_lo = (pasidt_binfo->base_ptr & VTD_PAGE_MASK) | + (pasidt_binfo->pasid_bits - MIN_NR_PASID_BITS); + context[1].lo = ctx_lo; + /* make sure context entry is updated before flushing */ + wmb(); + did = dmar_domain->iommu_did[iommu->seq_id]; + iommu->flush.flush_context(iommu, did, + (((u16)bus) << 8) | devfn, + DMA_CCMD_MASK_NOBIT, + DMA_CCMD_DEVICE_INVL); + iommu->flush.flush_iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH); + +out_unlock: + spin_unlock_irqrestore(&iommu->lock, flags); +out: + return ret; +} + +static void intel_iommu_unbind_pasid_table(struct iommu_domain *domain, + struct device *dev) +{ + struct intel_iommu *iommu; + struct dmar_domain *dmar_domain = to_dmar_domain(domain); + u8 bus, devfn; + + assert_spin_locked(&device_domain_lock); + iommu = device_to_iommu(dev, &bus, &devfn); + if (!iommu) { + dev_err(dev, "No IOMMU for device to unbind PASID table\n"); + return; + } + + domain_context_clear(iommu, dev); + + domain_context_mapping_one(dmar_domain, iommu, bus, devfn); +} #endif /* CONFIG_INTEL_IOMMU_SVM */ const struct iommu_ops intel_iommu_ops = { @@ -5310,6 +5413,10 @@ const struct iommu_ops intel_iommu_ops = { .domain_free = intel_iommu_domain_free, .attach_dev = intel_iommu_attach_device, .detach_dev = intel_iommu_detach_device, +#ifdef CONFIG_INTEL_IOMMU_SVM + .bind_pasid_table = intel_iommu_bind_pasid_table, + .unbind_pasid_table = intel_iommu_unbind_pasid_table, +#endif .map = intel_iommu_map, .unmap = intel_iommu_unmap, .map_sg = default_iommu_map_sg, diff --git a/include/linux/dma_remapping.h b/include/linux/dma_remapping.h index 21b3e7d..db290b2 100644 --- a/include/linux/dma_remapping.h +++ b/include/linux/dma_remapping.h @@ -28,6 +28,7 @@ #define CONTEXT_DINVE (1ULL << 8) #define CONTEXT_PRS (1ULL << 9) +#define CONTEXT_NESTE (1ULL << 10) #define CONTEXT_PASIDE (1ULL << 11) struct intel_iommu; -- 2.7.4