[PATCH v7 5/5] iommu/of: Add support for "iommu-ranges" on a device node

From: Vikash Garodia

Date: Fri Nov 06 2026 - 12:16:32 EST


Reserving an IOVA address using "iommu-addresses" requires it to be
placed in the reserved-memory node. But when "iommu-addresses" is the
only property being described and there is no backing "reg" (i.e. no
actual reserved system memory), it does not really belong under
/reserved-memory. A device IOVA range is specific to its own address
space, and does not describe the system physical memory to make it
qualify under reserved-memory. Given this, place "iommu-addresses"
inside reserved-memory only when it is paired with a "reg", otherwise,
define it within the device own node. It was discussed here:
https://lore.kernel.org/all/662f7093-fb0a-4564-9ca0-98e03e68ba9a@xxxxxxxxxx

Existing "iommu-addresses" property expects a phandle, which does
not make sense when the property is defined within the device node.
"iommu-ranges" was introduced to specify the device specific IOVA ranges
when there is no backing "reg".

Suggested-by: Rob Herring <robh@xxxxxxxxxx>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxxxxxxxx>
Co-developed-by: Vishnu Reddy <busanna.reddy@xxxxxxxxxxxxxxxx>
Signed-off-by: Vishnu Reddy <busanna.reddy@xxxxxxxxxxxxxxxx>
Signed-off-by: Vikash Garodia <vikash.garodia@xxxxxxxxxxxxxxxx>
---
drivers/iommu/of_iommu.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 107 insertions(+)

diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 00b02c19f27e195fc36c4bc1d91253255147c9ab..e133d3b346feecac7e2bfaec44e4882060ee89ec 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -183,6 +183,111 @@ iommu_resv_region_get_type(struct device *dev,
return IOMMU_RESV_RESERVED;
}

+/**
+ * of_iommu_derive_resv_regions - derive reserved regions which
+ * are outside of iommu-ranges
+ * @dev: device for which to get reserved regions
+ * @list: reserved region list
+ *
+ * A device can describe its own usable IOVA ranges directly on its node
+ * via "iommu-ranges". Everything not under those ranges is derived
+ * as a reserved region so the IOMMU allocator won't use it. Entries may
+ * appear in any order in the property.
+ */
+static void of_iommu_derive_resv_regions(struct device *dev, struct list_head *list)
+{
+ struct of_iommu_range {
+ struct list_head node;
+ phys_addr_t start;
+ phys_addr_t end;
+ } *pos, *new, *next_range;
+ int size, prot = IOMMU_READ | IOMMU_WRITE;
+ struct iommu_resv_region *region;
+ const __be32 *maps, *end;
+ struct list_head *prev;
+ phys_addr_t next = 0;
+ LIST_HEAD(ranges);
+
+ maps = of_get_property(dev->of_node, "iommu-ranges", &size);
+ if (!maps)
+ return;
+
+ end = maps + size / sizeof(__be32);
+
+ while (maps < end) {
+ phys_addr_t iova, iova_end;
+ resource_size_t length;
+
+ maps = of_translate_dma_region(dev->of_node, maps, &iova, &length);
+ if (!maps) {
+ dev_err(dev, "%pOF: failed to parse iommu-ranges\n",
+ dev->of_node);
+ goto exit;
+ }
+
+ if (!length)
+ continue;
+
+ iova_end = iova + length - 1;
+
+ if (iova_end < iova) {
+ dev_err(dev, "%pOF: iommu-ranges overflows address space\n",
+ dev->of_node);
+ goto exit;
+ }
+
+ prev = &ranges;
+ list_for_each_entry(pos, &ranges, node) {
+ if (pos->start > iova)
+ break;
+ prev = &pos->node;
+ }
+
+ new = kmalloc_obj(*new);
+ if (!new)
+ goto exit;
+
+ new->start = iova;
+ new->end = iova_end;
+ list_add(&new->node, prev);
+ }
+
+ if (list_empty(&ranges))
+ return;
+
+ if (of_dma_is_coherent(dev->of_node))
+ prot |= IOMMU_CACHE;
+
+ list_for_each_entry_safe(pos, next_range, &ranges, node) {
+ if (pos->start > next) {
+ region = iommu_alloc_resv_region(next, pos->start - next, prot,
+ IOMMU_RESV_RESERVED, GFP_KERNEL);
+ if (region)
+ list_add_tail(&region->list, list);
+ }
+
+ if (pos->end >= PHYS_ADDR_MAX - 1)
+ goto exit;
+
+ if (pos->end >= next)
+ next = pos->end + 1;
+
+ list_del(&pos->node);
+ kfree(pos);
+ }
+
+ region = iommu_alloc_resv_region(next, PHYS_ADDR_MAX - next,
+ prot, IOMMU_RESV_RESERVED, GFP_KERNEL);
+ if (region)
+ list_add_tail(&region->list, list);
+
+exit:
+ list_for_each_entry_safe(pos, next_range, &ranges, node) {
+ list_del(&pos->node);
+ kfree(pos);
+ }
+}
+
/**
* of_iommu_get_resv_regions - reserved region driver helper for device tree
* @dev: device for which to get reserved regions
@@ -262,5 +367,7 @@ void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
}
}
}
+
+ of_iommu_derive_resv_regions(dev, list);
}
EXPORT_SYMBOL(of_iommu_get_resv_regions);

--
2.34.1