[RFC 2/7] dma-iommu: Add API for DMA request with PASID

Jacob Pan
2021-09-22T05:13:18Z

DMA-API is the standard way for device drivers to perform in-kernel
DMA requests. If security is on, isolation is enforced by the IOMMU at
requester ID (RID) granularity. With the introduction of process address
space ID (PASID), DMA security is enforced per RID+PASID
granularity. On some modern platforms, DMA request with PASID is the
only option available. e.g. The shared work queues in Intel Data
Streaming Accelerators (DSA).
To support DMA with PASID while maintaining the ubiquitous usage of DMA
API, this patch introduces a new IOMMU DMA API that supports two
addressing modes:
1. Physical address or bypass mode. This is similar to DMA direct mode,
where trusted devices can DMA passthrough IOMMU.
2. IOVA mode that abides DMA APIs. Once set up, callers can use DMA API
as-is. DMA requests w/ and w/o PASID will be mapped by the same page
tables. i.e. the default DMA domain will be used by both RID and
RID+PASID.

Signed-off-by: Jacob Pan <jacob.jun.pan@linux.intel.com>
---
drivers/iommu/dma-iommu.c | 54 +++++++++++++++++++++++++++++++++++++++
include/linux/dma-iommu.h | 12 +++++++++
include/linux/iommu.h | 2 ++
3 files changed, 68 insertions(+)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 7bcdd1205535..490731659def 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -174,6 +174,60 @@ void iommu_put_dma_cookie(struct iommu_domain *domain)
}
EXPORT_SYMBOL(iommu_put_dma_cookie);

+/**
+ * iommu_dma_pasid_enable --Enable device DMA request with PASID
+ * @dev: Device to be enabled
+ * @mode: DMA addressing mode
+ *
+ * The following mutually exclusive DMA addressing modes are supported:
+ *
+ * 1. Physical address or bypass mode. This is similar to DMA direct where
+ * trusted devices can DMA pass-through IOMMU.
+ *
+ * 2. IOVA mode. This abides DMA APIs. Once set up, callers can use DMA API
+ * as-is. DMA request w/ and w/o PASID will be mapped by the same page
+ * tables. PCI requester ID (RID) and RID+PASID will be pointed to the same
+ * PGD. i.e. the default DMA domain will be used by both RID and RID+PASID.
+ *
+ * @return the supervisor PASID to be used for DMA. Or INVALID_IOASID upon
+ * failure.
+ */
+int iommu_dma_pasid_enable(struct device *dev,
+ enum iommu_dma_pasid_mode mode)
+{
+ int pasid = INVALID_IOASID;
+ struct iommu_domain *dom = NULL;
+
+ /* TODO: only allow a single mode each time, track PASID DMA enabling
+ * status per device. Perhaps add a flag in struct device.dev_iommu.
+ */
+
+ /* Call vendor drivers to handle IOVA, bypass mode */
+ dom = iommu_get_domain_for_dev(dev);
+ if (dom->ops->enable_pasid_dma(dev, IOASID_DMA_PASID, mode)) {
+ dev_dbg(dev, "Failed to enable DMA pasid in mode %d", mode);
+ goto exit;
+ }
+ pasid = IOASID_DMA_PASID;
+
+ dev_dbg(dev, "Enable DMA pasid %d in mode %d", pasid, mode);
+ goto exit;
+exit:
+ return pasid;
+}
+EXPORT_SYMBOL(iommu_dma_pasid_enable);
+
+int iommu_dma_pasid_disable(struct device *dev)
+{
+ struct iommu_domain *dom;
+
+ /* Call vendor iommu ops to clean up supervisor PASID context */
+ dom = iommu_get_domain_for_dev(dev);
+
+ return dom->ops->disable_pasid_dma(dev);
+}
+EXPORT_SYMBOL(iommu_dma_pasid_disable);
+
/**
* iommu_dma_get_resv_regions - Reserved region driver helper
* @dev: Device from iommu_get_resv_regions()
diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
index 6e75a2d689b4..3c1555e0fd51 100644
--- a/include/linux/dma-iommu.h
+++ b/include/linux/dma-iommu.h
@@ -17,6 +17,18 @@
int iommu_get_dma_cookie(struct iommu_domain *domain);
int iommu_get_msi_cookie(struct iommu_domain *domain, dma_addr_t base);
void iommu_put_dma_cookie(struct iommu_domain *domain);
+enum iommu_dma_pasid_mode {
+ /* Pass-through mode, use physical address */
+ IOMMU_DMA_PASID_BYPASS = 1,
+ /* Compatible with DMA APIs, same mapping as DMA w/o PASID */
+ IOMMU_DMA_PASID_IOVA,
+};
+/* For devices that can do DMA request with PASID, setup the system
+ * based on the address mode selected.
+ */
+int iommu_dma_pasid_enable(struct device *dev,
+ enum iommu_dma_pasid_mode mode);
+int iommu_dma_pasid_disable(struct device *dev);

/* Setup call for arch DMA mapping code */
void iommu_setup_dma_ops(struct device *dev, u64 dma_base, u64 size);
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 32d448050bf7..610cbfd03e6b 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -283,6 +283,8 @@ struct iommu_ops {

int (*def_domain_type)(struct device *dev);

+ int (*enable_pasid_dma)(struct device *dev, u32 pasid, int mode);
+ int (*disable_pasid_dma)(struct device *dev);
unsigned long pgsize_bitmap;
struct module *owner;
};
--
2.25.1