* [PATCH v2 01/11] iommu: Add iommu_teardown_dma_ops
2026-10-16 6:46 [PATCH v2 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
@ 2026-10-16 6:46 ` Mikko Perttunen
2026-10-16 6:46 ` [PATCH v2 02/11] iommu: Add cookie type for ARM_DMA_USE_IOMMU Mikko Perttunen
` (9 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Mikko Perttunen @ 2026-10-16 6:46 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD), Will Deacon,
Russell King, Heiko Stuebner, Thierry Reding, Mikko Perttunen,
David Airlie, Simona Vetter, Jonathan Hunter, Krishna Reddy,
Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
Add a counterpart to iommu_setup_dma_ops, tearing down state set by it.
Currently this teardown is open-coded in iommu_deinit_device; however
when 32-bit ARM gains a parallel implementation of iommu_setup_dma_ops
it becomes nicer to have a matching parallel iommu_teardown_dma_ops as
well.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/iommu/dma-iommu.c | 5 +++++
drivers/iommu/dma-iommu.h | 5 +++++
drivers/iommu/iommu.c | 3 +--
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 68ee57800fef..379019748a5d 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -2161,6 +2161,11 @@ void iommu_setup_dma_ops(struct device *dev, struct iommu_domain *domain)
dev_clear_dma_iommu(dev);
}
+void iommu_teardown_dma_ops(struct device *dev)
+{
+ dev_clear_dma_iommu(dev);
+}
+
static bool has_msi_cookie(const struct iommu_domain *domain)
{
return domain && (domain->cookie_type == IOMMU_COOKIE_DMA_IOVA ||
diff --git a/drivers/iommu/dma-iommu.h b/drivers/iommu/dma-iommu.h
index 040d00252563..349d6a57e5a0 100644
--- a/drivers/iommu/dma-iommu.h
+++ b/drivers/iommu/dma-iommu.h
@@ -10,6 +10,7 @@
#ifdef CONFIG_IOMMU_DMA
void iommu_setup_dma_ops(struct device *dev, struct iommu_domain *domain);
+void iommu_teardown_dma_ops(struct device *dev);
int iommu_get_dma_cookie(struct iommu_domain *domain);
void iommu_put_dma_cookie(struct iommu_domain *domain);
@@ -31,6 +32,10 @@ static inline void iommu_setup_dma_ops(struct device *dev,
{
}
+static inline void iommu_teardown_dma_ops(struct device *dev)
+{
+}
+
static inline int iommu_dma_init_fq(struct iommu_domain *domain)
{
return -EINVAL;
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index cd1bca7ede9a..a5440ed5e235 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -611,8 +611,7 @@ static void iommu_deinit_device(struct device *dev)
dev->iommu_group = NULL;
module_put(ops->owner);
dev_iommu_free(dev);
- if (IS_ENABLED(CONFIG_IOMMU_DMA))
- dev_clear_dma_iommu(dev);
+ iommu_teardown_dma_ops(dev);
}
static struct iommu_domain *pasid_array_entry_to_domain(void *entry)
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 02/11] iommu: Add cookie type for ARM_DMA_USE_IOMMU
2026-10-16 6:46 [PATCH v2 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
2026-10-16 6:46 ` [PATCH v2 01/11] iommu: Add iommu_teardown_dma_ops Mikko Perttunen
@ 2026-10-16 6:46 ` Mikko Perttunen
2026-10-16 6:46 ` [PATCH v2 03/11] ARM: dma-mapping: Split mapping allocation from domain allocation Mikko Perttunen
` (8 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Mikko Perttunen @ 2026-10-16 6:46 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD), Will Deacon,
Russell King, Heiko Stuebner, Thierry Reding, Mikko Perttunen,
David Airlie, Simona Vetter, Jonathan Hunter, Krishna Reddy,
Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
The ARM_DMA_USE_IOMMU DMA API implementation keeps its state in a struct
dma_iommu_mapping. Currently that struct owns an IOMMU domain -- to
allow inverting that relation, add a cookie type pointing to a struct
dma_iommu_mapping, more in line with how other architectures work.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/iommu/iommu.c | 1 +
include/linux/iommu.h | 3 +++
2 files changed, 4 insertions(+)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index a5440ed5e235..5fbb76d44a1e 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -2127,6 +2127,7 @@ void iommu_domain_free(struct iommu_domain *domain)
{
switch (domain->cookie_type) {
case IOMMU_COOKIE_DMA_IOVA:
+ case IOMMU_COOKIE_ARM_DMA:
iommu_put_dma_cookie(domain);
break;
case IOMMU_COOKIE_DMA_MSI:
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index ac43b8b93f14..4b87625892ac 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -43,6 +43,7 @@ struct notifier_block;
struct iommu_sva;
struct iommu_dma_cookie;
struct iommu_dma_msi_cookie;
+struct dma_iommu_mapping;
struct iommu_fault_param;
struct iommufd_ctx;
struct iommufd_viommu;
@@ -174,6 +175,7 @@ enum iommu_domain_cookie_type {
IOMMU_COOKIE_FAULT_HANDLER,
IOMMU_COOKIE_SVA,
IOMMU_COOKIE_IOMMUFD,
+ IOMMU_COOKIE_ARM_DMA,
};
/* Domain feature flags */
@@ -234,6 +236,7 @@ struct iommu_domain {
union { /* cookie */
struct iommu_dma_cookie *iova_cookie;
struct iommu_dma_msi_cookie *msi_cookie;
+ struct dma_iommu_mapping *arm_cookie;
struct iommufd_hw_pagetable *iommufd_hwpt;
struct {
iommu_fault_handler_t handler;
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 03/11] ARM: dma-mapping: Split mapping allocation from domain allocation
2026-10-16 6:46 [PATCH v2 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
2026-10-16 6:46 ` [PATCH v2 01/11] iommu: Add iommu_teardown_dma_ops Mikko Perttunen
2026-10-16 6:46 ` [PATCH v2 02/11] iommu: Add cookie type for ARM_DMA_USE_IOMMU Mikko Perttunen
@ 2026-10-16 6:46 ` Mikko Perttunen
2026-10-16 6:46 ` [PATCH v2 04/11] ARM: dma-mapping: Implement and expose DMA setup functions Mikko Perttunen
` (7 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Mikko Perttunen @ 2026-10-16 6:46 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD), Will Deacon,
Russell King, Heiko Stuebner, Thierry Reding, Mikko Perttunen,
David Airlie, Simona Vetter, Jonathan Hunter, Krishna Reddy,
Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
arm_iommu_create_mapping() allocates two separate things: address space
bookkeeping tracking allocated IOVA ranges; and the corresponding IOMMU
domain.
Split out the former into __arm_iommu_alloc_mapping(), with a matching
__arm_iommu_free_mapping(), so that the bookkeeping can be created
independently when a domain is allocated outside the ARM dma-mapping
code. A later patch uses this to turn struct dma_iommu_mapping into an
IOMMU domain cookie.
No functional change.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
arch/arm/mm/dma-mapping.c | 76 +++++++++++++++++++++++++++++------------------
1 file changed, 47 insertions(+), 29 deletions(-)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 7761099dde9e..4284950a9246 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1470,21 +1470,8 @@ static const struct dma_map_ops iommu_ops = {
.sync_sg_for_device = arm_iommu_sync_sg_for_device,
};
-/**
- * arm_iommu_create_mapping
- * @dev: pointer to the client device (for IOMMU calls)
- * @base: start address of the valid IO address space
- * @size: maximum size of the valid IO address space
- *
- * Creates a mapping structure which holds information about used/unused
- * IO address ranges, which is required to perform memory allocation and
- * mapping with IOMMU aware functions.
- *
- * The client device need to be attached to the mapping with
- * arm_iommu_attach_device function.
- */
-struct dma_iommu_mapping *
-arm_iommu_create_mapping(struct device *dev, dma_addr_t base, u64 size)
+static struct dma_iommu_mapping *
+__arm_iommu_alloc_mapping(dma_addr_t base, u64 size)
{
unsigned int bits = size >> PAGE_SHIFT;
unsigned int bitmap_size = BITS_TO_LONGS(bits) * sizeof(long);
@@ -1525,16 +1512,7 @@ arm_iommu_create_mapping(struct device *dev, dma_addr_t base, u64 size)
spin_lock_init(&mapping->lock);
- mapping->domain = iommu_paging_domain_alloc(dev);
- if (IS_ERR(mapping->domain)) {
- err = PTR_ERR(mapping->domain);
- goto err4;
- }
-
- kref_init(&mapping->kref);
return mapping;
-err4:
- kfree(mapping->bitmaps[0]);
err3:
kfree(mapping->bitmaps);
err2:
@@ -1542,21 +1520,61 @@ arm_iommu_create_mapping(struct device *dev, dma_addr_t base, u64 size)
err:
return ERR_PTR(err);
}
-EXPORT_SYMBOL_GPL(arm_iommu_create_mapping);
-static void release_iommu_mapping(struct kref *kref)
+static void __arm_iommu_free_mapping(struct dma_iommu_mapping *mapping)
{
int i;
- struct dma_iommu_mapping *mapping =
- container_of(kref, struct dma_iommu_mapping, kref);
- iommu_domain_free(mapping->domain);
for (i = 0; i < mapping->nr_bitmaps; i++)
kfree(mapping->bitmaps[i]);
kfree(mapping->bitmaps);
kfree(mapping);
}
+/**
+ * arm_iommu_create_mapping
+ * @dev: pointer to the client device (for IOMMU calls)
+ * @base: start address of the valid IO address space
+ * @size: maximum size of the valid IO address space
+ *
+ * Creates a mapping structure which holds information about used/unused
+ * IO address ranges, which is required to perform memory allocation and
+ * mapping with IOMMU aware functions.
+ *
+ * The client device need to be attached to the mapping with
+ * arm_iommu_attach_device function.
+ */
+struct dma_iommu_mapping *
+arm_iommu_create_mapping(struct device *dev, dma_addr_t base, u64 size)
+{
+ struct dma_iommu_mapping *mapping;
+
+ mapping = __arm_iommu_alloc_mapping(base, size);
+ if (IS_ERR(mapping))
+ return mapping;
+
+ mapping->domain = iommu_paging_domain_alloc(dev);
+ if (IS_ERR(mapping->domain)) {
+ int err = PTR_ERR(mapping->domain);
+
+ __arm_iommu_free_mapping(mapping);
+ return ERR_PTR(err);
+ }
+
+ kref_init(&mapping->kref);
+ return mapping;
+}
+EXPORT_SYMBOL_GPL(arm_iommu_create_mapping);
+
+static void release_iommu_mapping(struct kref *kref)
+{
+ struct dma_iommu_mapping *mapping =
+ container_of(kref, struct dma_iommu_mapping, kref);
+
+ iommu_domain_free(mapping->domain);
+ __arm_iommu_free_mapping(mapping);
+}
+
static int extend_iommu_mapping(struct dma_iommu_mapping *mapping)
{
int next_bitmap;
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 04/11] ARM: dma-mapping: Implement and expose DMA setup functions
2026-10-16 6:46 [PATCH v2 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (2 preceding siblings ...)
2026-10-16 6:46 ` [PATCH v2 03/11] ARM: dma-mapping: Split mapping allocation from domain allocation Mikko Perttunen
@ 2026-10-16 6:46 ` Mikko Perttunen
2026-10-16 7:06 ` sashiko-bot
2026-10-16 6:46 ` [PATCH v2 05/11] ARM: dma-mapping: Bypass the legacy path for core-owned DMA API domains Mikko Perttunen
` (6 subsequent siblings)
10 siblings, 1 reply; 16+ messages in thread
From: Mikko Perttunen @ 2026-10-16 6:46 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD), Will Deacon,
Russell King, Heiko Stuebner, Thierry Reding, Mikko Perttunen,
David Airlie, Simona Vetter, Jonathan Hunter, Krishna Reddy,
Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
ARM DMA domains are currently allocated by the ARM DMA code, with struct
dma_iommu_mapping owning the domain and the IOVA bookkeeping. To allow
the IOMMU core to own DMA domains as is the case with other
architectures, we have to allow that code to allocate the domain and get
the cookie from the ARM DMA code.
Implement ARM versions of iommu_get/put_dma_cookie that create a struct
dma_iommu_mapping that doesn't have its own domain, as well as
iommu_setup/teardown_dma_ops that set the device's archdata.mapping,
allowing ARM DMA ops to seamlessly work on these devices.
The code is protected by CONFIG_ARM_DMA_USE_IOMMU and mutually exclusive
with the same-named functions in the dma-iommu code; and not reachable
yet.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
arch/arm/include/asm/dma-iommu.h | 17 ++++++
arch/arm/mm/dma-mapping.c | 118 ++++++++++++++++++++++++++++++++++-----
drivers/iommu/dma-iommu.h | 50 +++++++++++------
3 files changed, 154 insertions(+), 31 deletions(-)
diff --git a/arch/arm/include/asm/dma-iommu.h b/arch/arm/include/asm/dma-iommu.h
index 2ce4c5683e6d..85cf9b05faab 100644
--- a/arch/arm/include/asm/dma-iommu.h
+++ b/arch/arm/include/asm/dma-iommu.h
@@ -19,6 +19,14 @@ struct dma_iommu_mapping {
size_t bits; /* per bitmap */
dma_addr_t base;
+ /*
+ * Set for mappings created by arm_iommu_create_mapping(), where
+ * @domain is owned by the mapping.
+ * Clear for mappings that are a cookie of a domain owned by the IOMMU
+ * core.
+ */
+ bool owns_domain;
+
spinlock_t lock;
struct kref kref;
};
@@ -32,5 +40,14 @@ int arm_iommu_attach_device(struct device *dev,
struct dma_iommu_mapping *mapping);
void arm_iommu_detach_device(struct device *dev);
+#ifdef CONFIG_ARM_DMA_USE_IOMMU
+struct iommu_domain;
+
+void iommu_setup_dma_ops(struct device *dev, struct iommu_domain *domain);
+void iommu_teardown_dma_ops(struct device *dev);
+int iommu_get_dma_cookie(struct iommu_domain *domain);
+void iommu_put_dma_cookie(struct iommu_domain *domain);
+#endif
+
#endif /* __KERNEL__ */
#endif
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 4284950a9246..f2d3e8ec3487 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1531,6 +1531,79 @@ static void __arm_iommu_free_mapping(struct dma_iommu_mapping *mapping)
kfree(mapping);
}
+static int __arm_iommu_init_cookie(struct iommu_domain *domain,
+ dma_addr_t base, u64 size)
+{
+ struct dma_iommu_mapping *mapping;
+
+ if (domain->cookie_type != IOMMU_COOKIE_NONE)
+ return -EEXIST;
+
+ mapping = __arm_iommu_alloc_mapping(base, size);
+ if (IS_ERR(mapping))
+ return PTR_ERR(mapping);
+
+ mapping->domain = domain;
+ domain->cookie_type = IOMMU_COOKIE_ARM_DMA;
+ domain->arm_cookie = mapping;
+
+ return 0;
+}
+
+/**
+ * iommu_get_dma_cookie - Set up a domain cookie for ARM IOMMU-based DMA API
+ * @domain: IOMMU_DOMAIN_DMA domain being configured
+ *
+ * Allocates IOVA bookkeeping for the domain based on the domain's specified
+ * geometry. The aperture can currently be at most 4GB in size, otherwise
+ * fails with -ERANGE.
+ */
+int iommu_get_dma_cookie(struct iommu_domain *domain)
+{
+ dma_addr_t base = domain->geometry.aperture_start;
+ u64 size;
+
+ if (base > domain->geometry.aperture_end)
+ return -EINVAL;
+
+ size = (u64)domain->geometry.aperture_end - base + 1;
+
+ return __arm_iommu_init_cookie(domain, base, size);
+}
+
+/**
+ * iommu_put_dma_cookie - Free domain cookie for ARM IOMMU-based DMA API
+ * @domain: the domain being freed
+ */
+void iommu_put_dma_cookie(struct iommu_domain *domain)
+{
+ __arm_iommu_free_mapping(domain->arm_cookie);
+}
+
+/**
+ * iommu_setup_dma_ops - Record a device's DMA API domain cookie
+ * @dev: the device
+ * @domain: the group's new default domain
+ *
+ * Records the cookie of @domain on @dev, or clears it if @domain is not a DMA
+ * API domain. The DMA ops themselves are per driver binding and are installed
+ * by arch_setup_dma_ops().
+ */
+void iommu_setup_dma_ops(struct device *dev, struct iommu_domain *domain)
+{
+ to_dma_iommu_mapping(dev) = iommu_is_dma_domain(domain) ?
+ domain->arm_cookie : NULL;
+}
+
+/**
+ * iommu_teardown_dma_ops - Clear a device's DMA API domain cookie
+ * @dev: the device
+ */
+void iommu_teardown_dma_ops(struct device *dev)
+{
+ to_dma_iommu_mapping(dev) = NULL;
+}
+
/**
* arm_iommu_create_mapping
* @dev: pointer to the client device (for IOMMU calls)
@@ -1541,28 +1614,32 @@ static void __arm_iommu_free_mapping(struct dma_iommu_mapping *mapping)
* IO address ranges, which is required to perform memory allocation and
* mapping with IOMMU aware functions.
*
+ * Unlike a default domain set up by the IOMMU core, the returned mapping owns
+ * the domain it describes and releases it along with the last reference.
+ *
* The client device need to be attached to the mapping with
* arm_iommu_attach_device function.
*/
struct dma_iommu_mapping *
arm_iommu_create_mapping(struct device *dev, dma_addr_t base, u64 size)
{
- struct dma_iommu_mapping *mapping;
-
- mapping = __arm_iommu_alloc_mapping(base, size);
- if (IS_ERR(mapping))
- return mapping;
+ struct iommu_domain *domain;
+ int err;
- mapping->domain = iommu_paging_domain_alloc(dev);
- if (IS_ERR(mapping->domain)) {
- int err = PTR_ERR(mapping->domain);
+ domain = iommu_paging_domain_alloc(dev);
+ if (IS_ERR(domain))
+ return ERR_CAST(domain);
- __arm_iommu_free_mapping(mapping);
+ err = __arm_iommu_init_cookie(domain, base, size);
+ if (err) {
+ iommu_domain_free(domain);
return ERR_PTR(err);
}
- kref_init(&mapping->kref);
- return mapping;
+ domain->arm_cookie->owns_domain = true;
+ kref_init(&domain->arm_cookie->kref);
+
+ return domain->arm_cookie;
}
EXPORT_SYMBOL_GPL(arm_iommu_create_mapping);
@@ -1571,8 +1648,8 @@ static void release_iommu_mapping(struct kref *kref)
struct dma_iommu_mapping *mapping =
container_of(kref, struct dma_iommu_mapping, kref);
+ /* Frees mapping too, via iommu_put_dma_cookie(). */
iommu_domain_free(mapping->domain);
- __arm_iommu_free_mapping(mapping);
}
static int extend_iommu_mapping(struct dma_iommu_mapping *mapping)
@@ -1595,8 +1672,13 @@ static int extend_iommu_mapping(struct dma_iommu_mapping *mapping)
void arm_iommu_release_mapping(struct dma_iommu_mapping *mapping)
{
- if (mapping)
- kref_put(&mapping->kref, release_iommu_mapping);
+ if (!mapping)
+ return;
+
+ if (WARN_ON(!mapping->owns_domain))
+ return;
+
+ kref_put(&mapping->kref, release_iommu_mapping);
}
EXPORT_SYMBOL_GPL(arm_iommu_release_mapping);
@@ -1660,6 +1742,14 @@ void arm_iommu_detach_device(struct device *dev)
return;
}
+ /*
+ * This pairs with arm_iommu_attach_device() on a driver-owned mapping.
+ * A mapping which is merely the cookie of a core-owned default domain
+ * must not be torn down from here.
+ */
+ if (WARN_ON(!mapping->owns_domain))
+ return;
+
iommu_detach_device(mapping->domain, dev);
kref_put(&mapping->kref, release_iommu_mapping);
to_dma_iommu_mapping(dev) = NULL;
diff --git a/drivers/iommu/dma-iommu.h b/drivers/iommu/dma-iommu.h
index 349d6a57e5a0..71b23cb698c0 100644
--- a/drivers/iommu/dma-iommu.h
+++ b/drivers/iommu/dma-iommu.h
@@ -7,25 +7,24 @@
#include <linux/iommu.h>
-#ifdef CONFIG_IOMMU_DMA
+/*
+ * Both dma-iommu.c and arch/arm/mm/dma-mapping.c implement functions to set up
+ * IOMMU_DOMAIN_DMA default domains. Only one provider can be compiled in at a
+ * time.
+ */
+#if defined(CONFIG_ARM_DMA_USE_IOMMU)
+
+#include <asm/dma-iommu.h>
+
+#elif defined(CONFIG_IOMMU_DMA)
void iommu_setup_dma_ops(struct device *dev, struct iommu_domain *domain);
void iommu_teardown_dma_ops(struct device *dev);
int iommu_get_dma_cookie(struct iommu_domain *domain);
void iommu_put_dma_cookie(struct iommu_domain *domain);
-void iommu_put_msi_cookie(struct iommu_domain *domain);
-int iommu_dma_init_fq(struct iommu_domain *domain);
-
-void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
-
-int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
- phys_addr_t msi_addr);
-
-extern bool iommu_dma_forcedac;
-
-#else /* CONFIG_IOMMU_DMA */
+#else
static inline void iommu_setup_dma_ops(struct device *dev,
struct iommu_domain *domain)
@@ -36,11 +35,6 @@ static inline void iommu_teardown_dma_ops(struct device *dev)
{
}
-static inline int iommu_dma_init_fq(struct iommu_domain *domain)
-{
- return -EINVAL;
-}
-
static inline int iommu_get_dma_cookie(struct iommu_domain *domain)
{
return -ENODEV;
@@ -50,6 +44,28 @@ static inline void iommu_put_dma_cookie(struct iommu_domain *domain)
{
}
+#endif
+
+#ifdef CONFIG_IOMMU_DMA
+
+void iommu_put_msi_cookie(struct iommu_domain *domain);
+
+int iommu_dma_init_fq(struct iommu_domain *domain);
+
+void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
+
+int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
+ phys_addr_t msi_addr);
+
+extern bool iommu_dma_forcedac;
+
+#else /* CONFIG_IOMMU_DMA */
+
+static inline int iommu_dma_init_fq(struct iommu_domain *domain)
+{
+ return -EINVAL;
+}
+
static inline void iommu_put_msi_cookie(struct iommu_domain *domain)
{
}
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v2 04/11] ARM: dma-mapping: Implement and expose DMA setup functions
2026-10-16 6:46 ` [PATCH v2 04/11] ARM: dma-mapping: Implement and expose DMA setup functions Mikko Perttunen
@ 2026-10-16 7:06 ` sashiko-bot
0 siblings, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-10-16 7:06 UTC (permalink / raw)
To: Mikko Perttunen; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] arm-iommu: missing owns_domain check in arm_iommu_attach_device causes kref warning
--
commit 9a5832f0c5c50cf332fad22584273f026af693a5
Author: Mikko Perttunen <mperttunen@nvidia.com>
ARM: dma-mapping: Implement and expose DMA setup functions
This commit allows the IOMMU core to allocate DMA domains and get the
cookie from the ARM DMA code. It implements ARM versions of
iommu_get_dma_cookie and iommu_put_dma_cookie.
> diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
> index 4284950a92469..f2d3e8ec34872 100644
> --- a/arch/arm/mm/dma-mapping.c
> +++ b/arch/arm/mm/dma-mapping.c
[ ... ]
> @@ -1660,6 +1742,14 @@ void arm_iommu_detach_device(struct device *dev)
> return;
> }
>
> + /*
> + * This pairs with arm_iommu_attach_device() on a driver-owned mapping.
> + * A mapping which is merely the cookie of a core-owned default domain
> + * must not be torn down from here.
> + */
> + if (WARN_ON(!mapping->owns_domain))
> + return;
> +
[Severity: High]
Should __arm_iommu_attach_device() also have a similar check for
!mapping->owns_domain?
Mappings created by the IOMMU core via iommu_get_dma_cookie() are
zero-initialized and bypass kref_init(), relying instead on the owns_domain
flag being false to prevent refcount operations.
If a legacy driver saves a device's original core-owned mapping during
initialization and later attempts to restore it by calling
arm_iommu_attach_device(), __arm_iommu_attach_device() will execute:
arch/arm/mm/dma-mapping.c:__arm_iommu_attach_device() {
...
err = iommu_attach_device(mapping->domain, dev);
if (err)
return err;
kref_get(&mapping->kref);
...
}
Calling kref_get() on a zero-initialized kref would trigger a refcount warning
traceback. Does __arm_iommu_attach_device() need a
WARN_ON(!mapping->owns_domain) check to prevent driver interactions with
core-owned mappings?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261016-b4-arm32-iommu-dma-cookie-v2-0-b06842a01938@nvidia.com?part=4
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 05/11] ARM: dma-mapping: Bypass the legacy path for core-owned DMA API domains
2026-10-16 6:46 [PATCH v2 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (3 preceding siblings ...)
2026-10-16 6:46 ` [PATCH v2 04/11] ARM: dma-mapping: Implement and expose DMA setup functions Mikko Perttunen
@ 2026-10-16 6:46 ` Mikko Perttunen
2026-10-16 7:01 ` sashiko-bot
2026-10-16 6:46 ` [PATCH v2 06/11] ARM: dma-mapping: Handle foreign buffers in arm_iommu_get_sgtable Mikko Perttunen
` (5 subsequent siblings)
10 siblings, 1 reply; 16+ messages in thread
From: Mikko Perttunen @ 2026-10-16 6:46 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD), Will Deacon,
Russell King, Heiko Stuebner, Thierry Reding, Mikko Perttunen,
David Airlie, Simona Vetter, Jonathan Hunter, Krishna Reddy,
Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
ARM's arch_setup_dma_ops / arm_setup_iommu_dma_ops creates a custom
IOMMU domain for DMA API usage, and continues to do so when the IOMMU
driver doesn't explicitly request a DMA default domain type.
When, however, that DMA default domain is requested and the core owns
the domain, we need to skip our own domain management. Add checks to
arm_setup/teardown_iommu_dma_ops for these cases. In such a case IOVAs
can be allocated from anywhere in the domain's geometry, so also add a
check to ensure the device's dma-ranges cover it. Presently, this is the
case for all platforms except some OMAP platforms with a non-zero
dma-ranges base.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
arch/arm/mm/dma-mapping.c | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index f2d3e8ec3487..d6315baeddb9 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1761,13 +1761,33 @@ EXPORT_SYMBOL_GPL(arm_iommu_detach_device);
static void arm_setup_iommu_dma_ops(struct device *dev)
{
- struct dma_iommu_mapping *mapping;
+ struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
u64 dma_base = 0, size = 1ULL << 32;
if (dev->dma_range_map) {
dma_base = dma_range_map_min(dev->dma_range_map);
size = dma_range_map_max(dev->dma_range_map) - dma_base;
}
+
+ /*
+ * An existing cookie means the core owns the domain. Only set the
+ * per-binding DMA ops. IOVAs are allocated from the whole aperture,
+ * so the device's dma-ranges must cover it.
+ */
+ if (mapping) {
+ const struct iommu_domain_geometry *geo =
+ &mapping->domain->geometry;
+
+ if (dma_base > geo->aperture_start ||
+ dma_base + size < geo->aperture_end) {
+ dev_err(dev, "dma-ranges do not cover the IOMMU aperture\n");
+ return;
+ }
+
+ set_dma_ops(dev, &iommu_ops);
+ return;
+ }
+
mapping = arm_iommu_create_mapping(dev, dma_base, size);
if (IS_ERR(mapping)) {
pr_warn("Failed to create %llu-byte IOMMU mapping for device %s\n",
@@ -1792,6 +1812,9 @@ static void arm_teardown_iommu_dma_ops(struct device *dev)
if (!mapping)
return;
+ if (!mapping->owns_domain)
+ return;
+
arm_iommu_detach_device(dev);
arm_iommu_release_mapping(mapping);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v2 05/11] ARM: dma-mapping: Bypass the legacy path for core-owned DMA API domains
2026-10-16 6:46 ` [PATCH v2 05/11] ARM: dma-mapping: Bypass the legacy path for core-owned DMA API domains Mikko Perttunen
@ 2026-10-16 7:01 ` sashiko-bot
0 siblings, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-10-16 7:01 UTC (permalink / raw)
To: Mikko Perttunen; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] arm: dma-mapping: off-by-one size causes mixed semantic in aperture check
--
commit 5535bede81dc83f33cc8029ff49a58ed02614672
Author: Mikko Perttunen <mperttunen@nvidia.com>
ARM: dma-mapping: Bypass the legacy path for core-owned DMA API domains
This commit updates the ARM DMA mapping code to bypass custom IOMMU domain
management when a DMA default domain is requested and owned by the core.
It adds checks in the setup and teardown routines to skip the legacy path
and ensures that the device's dma-ranges cover the IOMMU aperture.
> diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
> index f2d3e8ec34872..d6315baeddb9c 100644
> --- a/arch/arm/mm/dma-mapping.c
> +++ b/arch/arm/mm/dma-mapping.c
> @@ -1761,13 +1761,33 @@ EXPORT_SYMBOL_GPL(arm_iommu_detach_device);
>
> static void arm_setup_iommu_dma_ops(struct device *dev)
> {
> - struct dma_iommu_mapping *mapping;
> + struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
> u64 dma_base = 0, size = 1ULL << 32;
>
> if (dev->dma_range_map) {
> dma_base = dma_range_map_min(dev->dma_range_map);
> size = dma_range_map_max(dev->dma_range_map) - dma_base;
> }
> +
> + /*
> + * An existing cookie means the core owns the domain. Only set the
> + * per-binding DMA ops. IOVAs are allocated from the whole aperture,
> + * so the device's dma-ranges must cover it.
> + */
> + if (mapping) {
> + const struct iommu_domain_geometry *geo =
> + &mapping->domain->geometry;
> +
> + if (dma_base > geo->aperture_start ||
> + dma_base + size < geo->aperture_end) {
[Severity: Medium]
Does the new aperture check incorrectly mix inclusive and exclusive size
semantics?
When the device has a dma_range_map, the size calculation relies on
dma_range_map_max(), which returns an inclusive maximum address:
include/linux/dma-direct.h:dma_range_map_max() {
...
for (; map->size; map++)
ret = max(ret, map->dma_start + map->size - 1);
return ret;
}
Since dma_range_map_max() is inclusive, size = max - min results in a
value 1 byte too small. The new check (dma_base + size < geo->aperture_end)
appears to mathematically rely on this off-by-one size to evaluate correctly
against an inclusive aperture_end.
If the size calculation is ever corrected, would this incorrectly accept
dma-ranges that are 1 byte smaller than the aperture? Furthermore, when
dma_range_map is absent, size defaults to 1ULL << 32 (exclusive). If
aperture_end is exactly 0x100000000, will this check incorrectly fail?
> + dev_err(dev, "dma-ranges do not cover the IOMMU aperture\n");
> + return;
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261016-b4-arm32-iommu-dma-cookie-v2-0-b06842a01938@nvidia.com?part=5
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 06/11] ARM: dma-mapping: Handle foreign buffers in arm_iommu_get_sgtable
2026-10-16 6:46 [PATCH v2 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (4 preceding siblings ...)
2026-10-16 6:46 ` [PATCH v2 05/11] ARM: dma-mapping: Bypass the legacy path for core-owned DMA API domains Mikko Perttunen
@ 2026-10-16 6:46 ` Mikko Perttunen
2026-10-16 7:04 ` sashiko-bot
2026-10-16 6:46 ` [PATCH v2 07/11] iommu: Let 32-bit ARM drivers opt into a DMA API default domain Mikko Perttunen
` (4 subsequent siblings)
10 siblings, 1 reply; 16+ messages in thread
From: Mikko Perttunen @ 2026-10-16 6:46 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD), Will Deacon,
Russell King, Heiko Stuebner, Thierry Reding, Mikko Perttunen,
David Airlie, Simona Vetter, Jonathan Hunter, Krishna Reddy,
Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
arm_iommu_get_sgtable can only describe buffers which came from
arm_iommu_alloc_attrs, and returns -ENXIO for anything else. This is
stricter than iommu_dma_get_sgtable(), which falls back to describing
the buffer as a single chunk.
The difference shows up when a buffer is allocated for one device and
mapped for another, as TegraDRM does. Buffers are allocated on the
host1x logical device, which has no dma_configure and therefore uses
direct DMA ops, and are then mapped for a display controller behind the
SMMU.
Add the same fallback as iommu_dma_get_sgtable().
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
arch/arm/mm/dma-mapping.c | 25 +++++++++++++++++++++----
1 file changed, 21 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index d6315baeddb9..c2ec4f107808 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1169,12 +1169,29 @@ static int arm_iommu_get_sgtable(struct device *dev, struct sg_table *sgt,
{
unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
struct page **pages = __iommu_get_pages(cpu_addr, attrs);
+ struct page *page;
+ int ret;
- if (!pages)
- return -ENXIO;
+ if (pages)
+ return sg_alloc_table_from_pages(sgt, pages, count, 0, size,
+ GFP_KERNEL);
- return sg_alloc_table_from_pages(sgt, pages, count, 0, size,
- GFP_KERNEL);
+ /*
+ * The buffer was not allocated through these DMA ops. It may belong to
+ * a device which is not behind an IOMMU at all, which can happen when a
+ * buffer is allocated on one device and mapped for another. Describe it
+ * as a single chunk, as iommu_dma_get_sgtable() does.
+ */
+ if (is_vmalloc_addr(cpu_addr))
+ page = vmalloc_to_page(cpu_addr);
+ else
+ page = virt_to_page(cpu_addr);
+
+ ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
+ if (!ret)
+ sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
+
+ return ret;
}
/*
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v2 06/11] ARM: dma-mapping: Handle foreign buffers in arm_iommu_get_sgtable
2026-10-16 6:46 ` [PATCH v2 06/11] ARM: dma-mapping: Handle foreign buffers in arm_iommu_get_sgtable Mikko Perttunen
@ 2026-10-16 7:04 ` sashiko-bot
0 siblings, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-10-16 7:04 UTC (permalink / raw)
To: Mikko Perttunen; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] arm/dma-mapping: invalid cast in __atomic_get_pages causes kernel panic on mmap/sgtable
--
commit 0f1d7f693768ddb66d127fa1093856300ee17984
Author: Mikko Perttunen <mperttunen@nvidia.com>
ARM: dma-mapping: Handle foreign buffers in arm_iommu_get_sgtable
This commit updates arm_iommu_get_sgtable() to handle foreign buffers
that were not allocated through arm_iommu_alloc_attrs(). Instead of
returning an error, it falls back to describing the buffer as a single
chunk, similar to how iommu_dma_get_sgtable() operates.
> diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
> index d6315baeddb9c..c2ec4f1078083 100644
> --- a/arch/arm/mm/dma-mapping.c
> +++ b/arch/arm/mm/dma-mapping.c
> @@ -1169,12 +1169,29 @@ static int arm_iommu_get_sgtable(struct device *dev, struct sg_table *sgt,
> {
> unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
> struct page **pages = __iommu_get_pages(cpu_addr, attrs);
> + struct page *page;
> + int ret;
>
> - if (!pages)
> - return -ENXIO;
> + if (pages)
> + return sg_alloc_table_from_pages(sgt, pages, count, 0, size,
> + GFP_KERNEL);
[Severity: High]
Does this code still cause memory corruption when handling atomic pool
allocations?
The underlying __iommu_get_pages() function can return the result of
__atomic_get_pages(), which incorrectly casts a single struct page pointer to
an array of pointers:
arch/arm/mm/dma-mapping.c:__atomic_get_pages() {
...
return (struct page **)page;
}
When arm_iommu_get_sgtable() receives this pointer, it passes it to
sg_alloc_table_from_pages(). That function iterates over the pointer as an
array, reading pages[i]. For pages[0], this reads the internal fields of the
page object (such as flags on 32-bit ARM) and interprets them as a page
pointer. Passing this uninitialized pointer to page_to_pfn() or get_page()
causes memory corruption and kernel panics.
Can this be triggered by userspace exporting a dma-buf or mmapping an
atomic-pool DMA buffer?
The same issue appears to be present in arm_iommu_mmap_attrs() which passes
the bogus array to vm_map_pages():
arch/arm/mm/dma-mapping.c:arm_iommu_mmap_attrs() {
...
return vm_map_pages(vma, pages, nr_pages);
}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261016-b4-arm32-iommu-dma-cookie-v2-0-b06842a01938@nvidia.com?part=6
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 07/11] iommu: Let 32-bit ARM drivers opt into a DMA API default domain
2026-10-16 6:46 [PATCH v2 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (5 preceding siblings ...)
2026-10-16 6:46 ` [PATCH v2 06/11] ARM: dma-mapping: Handle foreign buffers in arm_iommu_get_sgtable Mikko Perttunen
@ 2026-10-16 6:46 ` Mikko Perttunen
2026-10-16 6:46 ` [PATCH v2 08/11] iommu/tegra-smmu: Defer display client translation to driver handover Mikko Perttunen
` (3 subsequent siblings)
10 siblings, 0 replies; 16+ messages in thread
From: Mikko Perttunen @ 2026-10-16 6:46 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD), Will Deacon,
Russell King, Heiko Stuebner, Thierry Reding, Mikko Perttunen,
David Airlie, Simona Vetter, Jonathan Hunter, Krishna Reddy,
Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
iommu_get_default_domain_type() unconditionally forces IDENTITY on
32-bit ARM as ARM_DMA_USE_IOMMU installs its own UNMANAGED domain on top
of whatever the group already had. Now that the ARM DMA API state is an
ordinary domain cookie, IOMMU drivers can ask for a real
IOMMU_DOMAIN_DMA default domain instead. Drop the override so that
->def_domain_type is honoured, and let a driver-requested DMA domain
through the !CONFIG_IOMMU_DMA guard when ARM_DMA_USE_IOMMU can back it.
IOMMU_DOMAIN_DMA stays strictly opt-in on 32-bit ARM and is never the
fallback to avoid unexpected changes, as drivers today return 0 from
->def_domain_type while expecting the legacy behaviour.
Behaviour is therefore unchanged for every existing 32-bit ARM IOMMU
driver:
- exynos-iommu, omap-iommu, rockchip-iommu, msm_iommu, ipmmu-vmsa,
mtk_iommu*, sun50i-iommu and qcom_iommu have no ->def_domain_type,
so iommu_get_def_domain_type() returns the unchanged cur_type of 0,
which is forced to IDENTITY as before.
- arm-smmu returns IDENTITY for legacy bindings and 0 otherwise, which
ends up in the same place.
- tegra-smmu returns IDENTITY explicitly, and keeps doing so until a
later patch.
Also move the static_assert on the two DMA API implementations being
mutually exclusive to drivers/iommu/dma-iommu.h, next to the
declarations that depend on it.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/iommu/dma-iommu.h | 3 +++
drivers/iommu/iommu.c | 20 +++++---------------
2 files changed, 8 insertions(+), 15 deletions(-)
diff --git a/drivers/iommu/dma-iommu.h b/drivers/iommu/dma-iommu.h
index 71b23cb698c0..24fa4165450f 100644
--- a/drivers/iommu/dma-iommu.h
+++ b/drivers/iommu/dma-iommu.h
@@ -12,6 +12,9 @@
* IOMMU_DOMAIN_DMA default domains. Only one provider can be compiled in at a
* time.
*/
+static_assert(!(IS_ENABLED(CONFIG_IOMMU_DMA) &&
+ IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)));
+
#if defined(CONFIG_ARM_DMA_USE_IOMMU)
#include <asm/dma-iommu.h>
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 5fbb76d44a1e..0d812c4b9f08 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1884,18 +1884,6 @@ static int iommu_get_default_domain_type(struct iommu_group *group,
lockdep_assert_held(&group->mutex);
- /*
- * ARM32 drivers supporting CONFIG_ARM_DMA_USE_IOMMU can declare an
- * identity_domain and it will automatically become their default
- * domain. Later on ARM_DMA_USE_IOMMU will install its UNMANAGED domain.
- * Override the selection to IDENTITY.
- */
- if (IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)) {
- static_assert(!(IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU) &&
- IS_ENABLED(CONFIG_IOMMU_DMA)));
- driver_type = IOMMU_DOMAIN_IDENTITY;
- }
-
for_each_group_device(group, gdev) {
driver_type = iommu_get_def_domain_type(group, gdev->dev,
driver_type);
@@ -1913,11 +1901,13 @@ static int iommu_get_default_domain_type(struct iommu_group *group,
/*
* If the common dma ops are not selected in kconfig then we cannot use
- * IOMMU_DOMAIN_DMA at all. Force IDENTITY if nothing else has been
- * selected.
+ * IOMMU_DOMAIN_DMA at all, unless this is ARM32 and the driver asked
+ * for it explicitly, where CONFIG_ARM_DMA_USE_IOMMU provides the
+ * implementation. Force IDENTITY if nothing else has been selected.
*/
if (!IS_ENABLED(CONFIG_IOMMU_DMA)) {
- if (WARN_ON(driver_type == IOMMU_DOMAIN_DMA))
+ if (WARN_ON(driver_type == IOMMU_DOMAIN_DMA &&
+ !IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)))
return -1;
if (!driver_type)
driver_type = IOMMU_DOMAIN_IDENTITY;
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 08/11] iommu/tegra-smmu: Defer display client translation to driver handover
2026-10-16 6:46 [PATCH v2 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (6 preceding siblings ...)
2026-10-16 6:46 ` [PATCH v2 07/11] iommu: Let 32-bit ARM drivers opt into a DMA API default domain Mikko Perttunen
@ 2026-10-16 6:46 ` Mikko Perttunen
2026-10-16 7:01 ` sashiko-bot
2026-10-16 6:46 ` [PATCH v2 09/11] gpu: host1x: Drop release of legacy 32-bit ARM DMA domain Mikko Perttunen
` (2 subsequent siblings)
10 siblings, 1 reply; 16+ messages in thread
From: Mikko Perttunen @ 2026-10-16 6:46 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD), Will Deacon,
Russell King, Heiko Stuebner, Thierry Reding, Mikko Perttunen,
David Airlie, Simona Vetter, Jonathan Hunter, Krishna Reddy,
Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
Bootloaders may leave display controllers scanning out a boot splash
while the kernel boots. Enabling translation for them without special
care during that time would cause translation faults.
Because of this, tegra_smmu_def_domain_type has kept every device on an
identity domain at boot, and TegraDRM has parallel custom IOMMU domain
management code paths for these clients.
Mark the dc and dcb swgroups on Tegra30, Tegra114, Tegra124 and Tegra210
with a new defer_enable flag. tegra_smmu_enable() sets those swgroups up
as usual, ASID and per-client enables and all, but leaves
SMMU_ASID_ENABLE clear, so the clients stay in bypass. tegra_dc_probe()
then calls tegra_smmu_enable_translation() once it has asserted the
display controller's reset (and set up its final IOMMU domain), at which
point the hardware is guaranteed not to be fetching and translation can
be turned on.
If the display driver never probes, the clients simply stay in bypass,
which is what they do today.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/gpu/drm/tegra/dc.c | 8 +++++++
drivers/iommu/tegra-smmu.c | 51 ++++++++++++++++++++++++++++++++++++++++-
drivers/memory/tegra/tegra114.c | 6 +++--
drivers/memory/tegra/tegra124.c | 6 +++--
drivers/memory/tegra/tegra210.c | 6 +++--
drivers/memory/tegra/tegra30.c | 6 +++--
include/soc/tegra/mc.h | 13 +++++++++++
7 files changed, 87 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index fefc3761a4bc..caf898e3db00 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -19,6 +19,7 @@
#include <linux/reset.h>
#include <soc/tegra/common.h>
+#include <soc/tegra/mc.h>
#include <soc/tegra/pmc.h>
#include <drm/drm_atomic.h>
@@ -2757,6 +2758,13 @@ static int tegra_dc_init(struct host1x_client *client)
return err;
}
+ /*
+ * Enable translation now that the final domain is attached.
+ * The hardware was reset at probe and has not been programmed
+ * since.
+ */
+ tegra_smmu_enable_translation(client->dev);
+
if (dc->soc->wgrps)
primary = tegra_dc_add_shared_planes(drm, dc);
else
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 67e7a7b925f0..6779528a9e7c 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -368,7 +368,13 @@ static void tegra_smmu_enable(struct tegra_smmu *smmu, unsigned int swgroup,
value = smmu_readl(smmu, group->reg);
value &= ~SMMU_ASID_MASK;
value |= SMMU_ASID_VALUE(asid);
- value |= SMMU_ASID_ENABLE;
+ /*
+ * A deferred client is set up completely, but with translation
+ * gated off, so it stays in bypass until its driver calls
+ * tegra_smmu_enable_translation().
+ */
+ if (!group->defer_enable)
+ value |= SMMU_ASID_ENABLE;
smmu_writel(smmu, value, group->reg);
} else {
pr_warn("%s group from swgroup %u not found\n", __func__,
@@ -1190,3 +1196,46 @@ void tegra_smmu_remove(struct tegra_smmu *smmu)
if (IS_ENABLED(CONFIG_DEBUG_FS))
tegra_smmu_debugfs_exit(smmu);
}
+
+/**
+ * tegra_smmu_enable_translation - Release a client's swgroups to translation
+ * @dev: memory client device, already quiesced by its driver
+ *
+ * Enables translation for swgroups which were left in bypass at attach time
+ * because the client could still have been running from a pre-kernel
+ * configuration. Must be called after @dev has been attached to the domain it
+ * will use. Safe to call for any device, including ones behind other IOMMUs;
+ * clients which were not deferred are unaffected.
+ */
+void tegra_smmu_enable_translation(struct device *dev)
+{
+ struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+ struct iommu_domain *domain;
+ struct tegra_smmu *smmu;
+ unsigned int i;
+
+ domain = iommu_get_domain_for_dev(dev);
+ if (!fwspec || !domain || domain->owner != &tegra_smmu_ops ||
+ !(domain->type & __IOMMU_DOMAIN_PAGING))
+ return;
+
+ smmu = dev_iommu_priv_get(dev);
+
+ mutex_lock(&smmu->lock);
+
+ for (i = 0; i < fwspec->num_ids; i++) {
+ const struct tegra_smmu_swgroup *group;
+ u32 value;
+
+ group = tegra_smmu_find_swgroup(smmu, fwspec->ids[i]);
+ if (!group || !group->defer_enable)
+ continue;
+
+ value = smmu_readl(smmu, group->reg);
+ value |= SMMU_ASID_ENABLE;
+ smmu_writel(smmu, value, group->reg);
+ }
+
+ mutex_unlock(&smmu->lock);
+}
+EXPORT_SYMBOL_GPL(tegra_smmu_enable_translation);
diff --git a/drivers/memory/tegra/tegra114.c b/drivers/memory/tegra/tegra114.c
index 2cb153091228..2883c4f91144 100644
--- a/drivers/memory/tegra/tegra114.c
+++ b/drivers/memory/tegra/tegra114.c
@@ -1028,8 +1028,10 @@ static const struct tegra_mc_client tegra114_mc_clients[] = {
};
static const struct tegra_smmu_swgroup tegra114_swgroups[] = {
- { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
- { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
+ { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
+ .defer_enable = true },
+ { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
+ .defer_enable = true },
{ .name = "epp", .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 },
{ .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c
index df87c5038625..a38fbf2baf6c 100644
--- a/drivers/memory/tegra/tegra124.c
+++ b/drivers/memory/tegra/tegra124.c
@@ -1070,8 +1070,10 @@ static const struct tegra_mc_client tegra124_mc_clients[] = {
};
static const struct tegra_smmu_swgroup tegra124_swgroups[] = {
- { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
- { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
+ { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
+ .defer_enable = true },
+ { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
+ .defer_enable = true },
{ .name = "afi", .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
diff --git a/drivers/memory/tegra/tegra210.c b/drivers/memory/tegra/tegra210.c
index f58f3ef6f681..92e809cd7c26 100644
--- a/drivers/memory/tegra/tegra210.c
+++ b/drivers/memory/tegra/tegra210.c
@@ -1166,8 +1166,10 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
static const struct tegra_smmu_swgroup tegra210_swgroups[] = {
{ .name = "afi", .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
- { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
- { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
+ { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
+ .defer_enable = true },
+ { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
+ .defer_enable = true },
{ .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
{ .name = "isp2", .swgroup = TEGRA_SWGROUP_ISP2, .reg = 0x258 },
diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c
index 8389e3af0121..0ee4715413ba 100644
--- a/drivers/memory/tegra/tegra30.c
+++ b/drivers/memory/tegra/tegra30.c
@@ -1139,8 +1139,10 @@ static const struct tegra_mc_client tegra30_mc_clients[] = {
};
static const struct tegra_smmu_swgroup tegra30_swgroups[] = {
- { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
- { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
+ { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
+ .defer_enable = true },
+ { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
+ .defer_enable = true },
{ .name = "epp", .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 },
{ .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c },
{ .name = "mpe", .swgroup = TEGRA_SWGROUP_MPE, .reg = 0x264 },
diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h
index 25d465d70493..ea67972fd585 100644
--- a/include/soc/tegra/mc.h
+++ b/include/soc/tegra/mc.h
@@ -70,6 +70,14 @@ struct tegra_smmu_swgroup {
const char *name;
unsigned int swgroup;
unsigned int reg;
+
+ /*
+ * Set for clients which may already be running when the kernel takes
+ * over, e.g. display controllers scanning out a boot splash. They are
+ * left in bypass until their driver calls
+ * tegra_smmu_enable_translation().
+ */
+ bool defer_enable;
};
struct tegra_smmu_group_soc {
@@ -103,6 +111,7 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,
const struct tegra_smmu_soc *soc,
struct tegra_mc *mc);
void tegra_smmu_remove(struct tegra_smmu *smmu);
+void tegra_smmu_enable_translation(struct device *dev);
#else
static inline struct tegra_smmu *
tegra_smmu_probe(struct device *dev, const struct tegra_smmu_soc *soc,
@@ -114,6 +123,10 @@ tegra_smmu_probe(struct device *dev, const struct tegra_smmu_soc *soc,
static inline void tegra_smmu_remove(struct tegra_smmu *smmu)
{
}
+
+static inline void tegra_smmu_enable_translation(struct device *dev)
+{
+}
#endif
struct tegra_mc_reset {
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v2 08/11] iommu/tegra-smmu: Defer display client translation to driver handover
2026-10-16 6:46 ` [PATCH v2 08/11] iommu/tegra-smmu: Defer display client translation to driver handover Mikko Perttunen
@ 2026-10-16 7:01 ` sashiko-bot
0 siblings, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-10-16 7:01 UTC (permalink / raw)
To: Mikko Perttunen; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] iommu: tegra SMMU isolation bypass for display controllers via defer_enable
--
commit d4d42b6a74ebf50531a4c2f625710578bc863c34
Author: Mikko Perttunen <mperttunen@nvidia.com>
iommu/tegra-smmu: Defer display client translation to driver handover
Bootloaders may leave display controllers scanning out a boot splash
while the kernel boots. Enabling translation for them without special
care during that time would cause translation faults.
Because of this, tegra_smmu_def_domain_type has kept every device on an
identity domain at boot, and TegraDRM has parallel custom IOMMU domain
management code paths for these clients.
> diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
> index 67e7a7b925f0c..6779528a9e7cc 100644
> --- a/drivers/iommu/tegra-smmu.c
> +++ b/drivers/iommu/tegra-smmu.c
> @@ -368,7 +368,13 @@ static void tegra_smmu_enable(struct tegra_smmu *smmu, unsigned int swgroup,
> value = smmu_readl(smmu, group->reg);
> value &= ~SMMU_ASID_MASK;
> value |= SMMU_ASID_VALUE(asid);
> - value |= SMMU_ASID_ENABLE;
> + /*
> + * A deferred client is set up completely, but with translation
> + * gated off, so it stays in bypass until its driver calls
> + * tegra_smmu_enable_translation().
> + */
> + if (!group->defer_enable)
> + value |= SMMU_ASID_ENABLE;
[Severity: Critical]
If a generic driver like vfio-platform binds to this device (for example, to
pass the display controller through to a VM), it attaches an unmanaged IOMMU
domain and expects isolation to be enforced.
Since tegra_smmu_enable() completes without setting SMMU_ASID_ENABLE for
deferred clients, does this silently leave the hardware in bypass mode even
after the IOMMU domain is successfully attached?
This could allow an untrusted guest VM to program the DMA engine to read or
write arbitrary host physical memory, bypassing IOMMU isolation.
> smmu_writel(smmu, value, group->reg);
> } else {
> pr_warn("%s group from swgroup %u not found\n", __func__,
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261016-b4-arm32-iommu-dma-cookie-v2-0-b06842a01938@nvidia.com?part=8
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 09/11] gpu: host1x: Drop release of legacy 32-bit ARM DMA domain
2026-10-16 6:46 [PATCH v2 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (7 preceding siblings ...)
2026-10-16 6:46 ` [PATCH v2 08/11] iommu/tegra-smmu: Defer display client translation to driver handover Mikko Perttunen
@ 2026-10-16 6:46 ` Mikko Perttunen
2026-10-16 6:46 ` [PATCH v2 10/11] drm/tegra: " Mikko Perttunen
2026-10-16 6:46 ` [PATCH v2 11/11] iommu/tegra-smmu: Use a DMA API default domain per swgroup Mikko Perttunen
10 siblings, 0 replies; 16+ messages in thread
From: Mikko Perttunen @ 2026-10-16 6:46 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD), Will Deacon,
Russell King, Heiko Stuebner, Thierry Reding, Mikko Perttunen,
David Airlie, Simona Vetter, Jonathan Hunter, Krishna Reddy,
Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
host1x_iommu_attach currently detaches a possible ARM DMA mapping on the
device, since it would get in the way of the shared domain host1x wants.
We are about to enable default domain based DMA domains for Host1x and
TegraDRM devices. These will take the same path as on 64-bit ARM, but
we need to avoid trying to detach the now perfectly functional mapping
on the device, so get rid of that code.
Strictly speaking, the code removed here would not be harmful but would
cause WARNs to happen.
nouveau and tegra-vde have the same construct, but their clients stay on
identity domains for now, so those copies are left alone.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/gpu/host1x/dev.c | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
index d2c64728f804..926553b2326f 100644
--- a/drivers/gpu/host1x/dev.c
+++ b/drivers/gpu/host1x/dev.c
@@ -23,10 +23,6 @@
#include <trace/events/host1x.h>
#undef CREATE_TRACE_POINTS
-#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
-#include <asm/dma-iommu.h>
-#endif
-
#include "bus.h"
#include "channel.h"
#include "context.h"
@@ -420,17 +416,6 @@ static struct iommu_domain *host1x_iommu_attach(struct host1x *host)
struct iommu_domain *domain = iommu_get_domain_for_dev(host->dev);
int err;
-#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
- if (host->dev->archdata.mapping) {
- struct dma_iommu_mapping *mapping =
- to_dma_iommu_mapping(host->dev);
- arm_iommu_detach_device(host->dev);
- arm_iommu_release_mapping(mapping);
-
- domain = iommu_get_domain_for_dev(host->dev);
- }
-#endif
-
/*
* We may not always want to enable IOMMU support (for example if the
* host1x firewall is already enabled and we don't support addressing
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 10/11] drm/tegra: Drop release of legacy 32-bit ARM DMA domain
2026-10-16 6:46 [PATCH v2 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (8 preceding siblings ...)
2026-10-16 6:46 ` [PATCH v2 09/11] gpu: host1x: Drop release of legacy 32-bit ARM DMA domain Mikko Perttunen
@ 2026-10-16 6:46 ` Mikko Perttunen
2026-10-16 6:46 ` [PATCH v2 11/11] iommu/tegra-smmu: Use a DMA API default domain per swgroup Mikko Perttunen
10 siblings, 0 replies; 16+ messages in thread
From: Mikko Perttunen @ 2026-10-16 6:46 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD), Will Deacon,
Russell King, Heiko Stuebner, Thierry Reding, Mikko Perttunen,
David Airlie, Simona Vetter, Jonathan Hunter, Krishna Reddy,
Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
host1x_client_iommu_attach currently detaches a possible ARM DMA mapping
on the device, since it would get in the way of the shared domain
TegraDRM wants.
We are about to enable default domain based DMA domains for Host1x and
TegraDRM devices. These will take the same path as on 64-bit ARM, but
we need to avoid trying to detach the now perfectly functional mapping
on the device, so get rid of that code.
Strictly speaking, the code removed here would not be harmful but would
cause WARNs to happen.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/gpu/drm/tegra/drm.c | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 324480c87fb1..627fb7f04f39 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -25,10 +25,6 @@
#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
-#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
-#include <asm/dma-iommu.h>
-#endif
-
#include "dc.h"
#include "drm.h"
#include "gem.h"
@@ -947,17 +943,6 @@ int host1x_client_iommu_attach(struct host1x_client *client)
struct iommu_group *group = NULL;
int err;
-#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
- if (client->dev->archdata.mapping) {
- struct dma_iommu_mapping *mapping =
- to_dma_iommu_mapping(client->dev);
- arm_iommu_detach_device(client->dev);
- arm_iommu_release_mapping(mapping);
-
- domain = iommu_get_domain_for_dev(client->dev);
- }
-#endif
-
/*
* If the host1x client is already attached to an IOMMU domain that is
* not the shared IOMMU domain, don't try to attach it to a different
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 11/11] iommu/tegra-smmu: Use a DMA API default domain per swgroup
2026-10-16 6:46 [PATCH v2 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (9 preceding siblings ...)
2026-10-16 6:46 ` [PATCH v2 10/11] drm/tegra: " Mikko Perttunen
@ 2026-10-16 6:46 ` Mikko Perttunen
10 siblings, 0 replies; 16+ messages in thread
From: Mikko Perttunen @ 2026-10-16 6:46 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD), Will Deacon,
Russell King, Heiko Stuebner, Thierry Reding, Mikko Perttunen,
David Airlie, Simona Vetter, Jonathan Hunter, Krishna Reddy,
Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
Everything is now in place for the Tegra SMMU to give devices a real
IOMMU_DOMAIN_DMA default domain.
Some device drivers still rely on explicit domains (and being able to
detach a legacy ARM DMA domain if any), so DMA default domains are
enabled on a per-swgroup basis via a use_dma_api flag. As drivers are
converted to DMA API usage we can eventually get rid of this. Initially
the flag is enabled for TegraDRM and Host1x devices.
On multi_v7_defconfig (where CONFIG_ARM_DMA_USE_IOMMU was always
enabled), this will fix TegraDRM probing with certain probe orderings
where the legacy implementation's lack of support for multiple devices
in one IOMMU group would cause breakage.
On tegra_defconfig, as this now enables CONFIG_ARM_DMA_USE_IOMMU, all
devices move to either the legacy ARM DMA path or the new one. This
should in principle not cause issues as the drivers would already have
been verified on multi_v7_defconfig.
Also set .release_domain to the identity domain to ensure the AS is
released appropriately now that the domain can be non-static.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/iommu/Kconfig | 1 +
drivers/iommu/tegra-smmu.c | 59 ++++++++++++++++++++++++++++++++++++++---
drivers/memory/tegra/tegra114.c | 13 +++++----
drivers/memory/tegra/tegra124.c | 10 ++++---
drivers/memory/tegra/tegra30.c | 16 ++++++-----
include/soc/tegra/mc.h | 7 +++++
6 files changed, 87 insertions(+), 19 deletions(-)
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 6e07bd69467a..817e84e0e6ab 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -252,6 +252,7 @@ config TEGRA_IOMMU_SMMU
depends on TEGRA_AHB
depends on TEGRA_MC
select IOMMU_API
+ select ARM_DMA_USE_IOMMU if ARM
help
This driver supports the IOMMU hardware (SMMU) found on NVIDIA Tegra
SoCs (Tegra30 up to Tegra210).
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 6779528a9e7c..1c1910f25553 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -994,16 +994,31 @@ static int tegra_smmu_of_xlate(struct device *dev,
static int tegra_smmu_def_domain_type(struct device *dev)
{
+ struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+ struct tegra_smmu *smmu = dev_iommu_priv_get(dev);
+ unsigned int i;
+
+ if (!fwspec || !smmu)
+ return IOMMU_DOMAIN_IDENTITY;
+
/*
- * FIXME: For now we want to run all translation in IDENTITY mode, due
- * to some device quirks. Better would be to just quirk the troubled
- * devices.
+ * Only clients known to work with the DMA API get a DMA default domain.
+ * The rest keep running in identity mode.
*/
- return IOMMU_DOMAIN_IDENTITY;
+ for (i = 0; i < fwspec->num_ids; i++) {
+ const struct tegra_smmu_swgroup *group;
+
+ group = tegra_smmu_find_swgroup(smmu, fwspec->ids[i]);
+ if (!group || !group->use_dma_api)
+ return IOMMU_DOMAIN_IDENTITY;
+ }
+
+ return IOMMU_DOMAIN_DMA;
}
static const struct iommu_ops tegra_smmu_ops = {
.identity_domain = &tegra_smmu_identity_domain,
+ .release_domain = &tegra_smmu_identity_domain,
.def_domain_type = &tegra_smmu_def_domain_type,
.domain_alloc_paging = tegra_smmu_domain_alloc_paging,
.probe_device = tegra_smmu_probe_device,
@@ -1108,6 +1123,40 @@ static void tegra_smmu_debugfs_exit(struct tegra_smmu *smmu)
debugfs_remove_recursive(smmu->debugfs);
}
+/*
+ * The default domain type is a property of the IOMMU group, so all swgroups in
+ * one group must agree on use_dma_api. Otherwise the core sees conflicting
+ * def_domain_type and falls back to identity for the whole group.
+ */
+static void tegra_smmu_check_groups(struct tegra_smmu *smmu)
+{
+ const struct tegra_smmu_soc *soc = smmu->soc;
+ unsigned int i, j;
+
+ for (i = 0; i < soc->num_groups; i++) {
+ const struct tegra_smmu_group_soc *group = &soc->groups[i];
+ int dma_api = -1;
+
+ for (j = 0; j < group->num_swgroups; j++) {
+ const struct tegra_smmu_swgroup *swgrp;
+
+ swgrp = tegra_smmu_find_swgroup(smmu,
+ group->swgroups[j]);
+ if (!swgrp)
+ continue;
+
+ if (dma_api < 0)
+ dma_api = swgrp->use_dma_api;
+ else if (swgrp->use_dma_api != !!dma_api)
+ break;
+ }
+
+ WARN(j < group->num_swgroups,
+ "SMMU group %s mixes DMA API and identity swgroups\n",
+ group->name);
+ }
+}
+
struct tegra_smmu *tegra_smmu_probe(struct device *dev,
const struct tegra_smmu_soc *soc,
struct tegra_mc *mc)
@@ -1142,6 +1191,8 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,
smmu->dev = dev;
smmu->mc = mc;
+ tegra_smmu_check_groups(smmu);
+
smmu->pfn_mask =
BIT_MASK(mc->soc->num_address_bits - SMMU_PTE_SHIFT) - 1;
dev_dbg(dev, "address bits: %u, PFN mask: %#lx\n",
diff --git a/drivers/memory/tegra/tegra114.c b/drivers/memory/tegra/tegra114.c
index 2883c4f91144..024c63bdd480 100644
--- a/drivers/memory/tegra/tegra114.c
+++ b/drivers/memory/tegra/tegra114.c
@@ -1029,15 +1029,18 @@ static const struct tegra_mc_client tegra114_mc_clients[] = {
static const struct tegra_smmu_swgroup tegra114_swgroups[] = {
{ .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "epp", .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 },
- { .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c },
+ { .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c,
+ .use_dma_api = true },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
- { .name = "nv", .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 },
+ { .name = "nv", .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268,
+ .use_dma_api = true },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
- { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
+ { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250,
+ .use_dma_api = true },
{ .name = "msenc", .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 },
{ .name = "ppcs", .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
{ .name = "vde", .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c },
diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c
index a38fbf2baf6c..17068aaf163f 100644
--- a/drivers/memory/tegra/tegra124.c
+++ b/drivers/memory/tegra/tegra124.c
@@ -1071,13 +1071,14 @@ static const struct tegra_mc_client tegra124_mc_clients[] = {
static const struct tegra_smmu_swgroup tegra124_swgroups[] = {
{ .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "afi", .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
- { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
+ { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250,
+ .use_dma_api = true },
{ .name = "msenc", .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 },
{ .name = "ppcs", .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
{ .name = "sata", .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x274 },
@@ -1093,7 +1094,8 @@ static const struct tegra_smmu_swgroup tegra124_swgroups[] = {
{ .name = "sdmmc2a", .swgroup = TEGRA_SWGROUP_SDMMC2A, .reg = 0xa98 },
{ .name = "sdmmc3a", .swgroup = TEGRA_SWGROUP_SDMMC3A, .reg = 0xa9c },
{ .name = "sdmmc4a", .swgroup = TEGRA_SWGROUP_SDMMC4A, .reg = 0xaa0 },
- { .name = "vic", .swgroup = TEGRA_SWGROUP_VIC, .reg = 0x284 },
+ { .name = "vic", .swgroup = TEGRA_SWGROUP_VIC, .reg = 0x284,
+ .use_dma_api = true },
{ .name = "vi", .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
};
diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c
index 0ee4715413ba..df6b5a499ccc 100644
--- a/drivers/memory/tegra/tegra30.c
+++ b/drivers/memory/tegra/tegra30.c
@@ -1140,19 +1140,23 @@ static const struct tegra_mc_client tegra30_mc_clients[] = {
static const struct tegra_smmu_swgroup tegra30_swgroups[] = {
{ .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "epp", .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 },
- { .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c },
+ { .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c,
+ .use_dma_api = true },
{ .name = "mpe", .swgroup = TEGRA_SWGROUP_MPE, .reg = 0x264 },
{ .name = "vi", .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
{ .name = "afi", .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
- { .name = "nv", .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 },
- { .name = "nv2", .swgroup = TEGRA_SWGROUP_NV2, .reg = 0x26c },
+ { .name = "nv", .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268,
+ .use_dma_api = true },
+ { .name = "nv2", .swgroup = TEGRA_SWGROUP_NV2, .reg = 0x26c,
+ .use_dma_api = true },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
- { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
+ { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250,
+ .use_dma_api = true },
{ .name = "ppcs", .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
{ .name = "sata", .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x278 },
{ .name = "vde", .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c },
diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h
index ea67972fd585..1cbd990fc0f4 100644
--- a/include/soc/tegra/mc.h
+++ b/include/soc/tegra/mc.h
@@ -78,6 +78,13 @@ struct tegra_smmu_swgroup {
* tegra_smmu_enable_translation().
*/
bool defer_enable;
+
+ /*
+ * Set once the clients behind this swgroup are known to work with an
+ * IOMMU_DOMAIN_DMA default domain. All swgroups of a
+ * tegra_smmu_group_soc must agree.
+ */
+ bool use_dma_api;
};
struct tegra_smmu_group_soc {
--
2.55.0
^ permalink raw reply related [flat|nested] 16+ messages in thread