Re: [PATCH v5 04/13] iommu: of_iommu: Add support for "iommu-ranges" on a device node

From: Vikash Garodia

Date: Mon Oct 26 2026 - 08:41:28 EST



On 10/23/2026 9:11 PM, Bryan O'Donoghue wrote:
On 26/09/2026 07:34, Vikash Garodia wrote:
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.
Introduce a new property, "iommu-ranges", to specify the device
specific IOVA ranges when there is no backing "reg".

Suggested-by: Rob Herring <robh@xxxxxxxxxx>
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 | 128 +++++++++++++++++++++++++++++++++++++ +++++++---
  1 file changed, 121 insertions(+), 7 deletions(-)

diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index a18bb60f6f3dfdba853b6e16dd2a8616f6a731c7..9766a6944d0e11a6cd8b74b1b89428acffe3dfdf 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -190,6 +190,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;
+        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);
+            break;
+        }
+
+        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);
+            continue;
+        }
+
+        prev = &ranges;
+        list_for_each_entry(pos, &ranges, node) {
+            if (pos->start > iova)
+                break;
+            prev = &pos->node;
+        }
+
+        new = kmalloc_obj(*new);
+        if (!new)
+            continue;
+
+        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)
+            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 + 1,
+                     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
@@ -214,10 +319,14 @@ void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
          memset(&phys, 0, sizeof(phys));
+        maps = of_get_property(it.node, "iommu-addresses", &size);
+        if (!maps)
+            continue;
+
          /*
-         * The "reg" property is optional and can be omitted by reserved-memory regions
-         * that represent reservations in the IOVA space, which are regions that should
-         * not be mapped.
+         * "iommu-addresses" must be used in combination with a "reg" that provides
+         * the physical address and size of this memory region, for an identity 1:1
+         * IOVA mapping to that physical memory.
           */
          if (of_property_present(it.node, "reg")) {
              err = of_address_to_resource(it.node, 0, &phys);
@@ -226,11 +335,11 @@ void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
                      it.node, err);
                  continue;
              }
-        }
-
-        maps = of_get_property(it.node, "iommu-addresses", &size);
-        if (!maps)
+        } else {
+            dev_err(dev, "%pOF: iommu-addresses requires a reg property\n",
+                it.node);
              continue;
+        }

The if/else doesn't belong in a path entitled 'Add support for "iommu- ranges"' as this changes he behaviour of iommu-addresses independently of whatever iommu-ranges does.

The reg update part basically has nothing to do with this series at all i.e. the code you depend on later in this series doesn't declare iommu- addresses so... this is an update to align with a DT schema change.

https://github.com/devicetree-org/dt-schema/pull/207

Perhaps splitting this into two patches as an iommu-series would be best.

Intention was to keep the patch to add iommu-ranges property as part of iris driver patches to show the usage of this new property.

I am fine either ways, whatever it takes for the maintainers of this file to review.

Regards,
Vikash

          end = maps + size / sizeof(__be32);
@@ -258,6 +367,9 @@ void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
                  }
                  type = iommu_resv_region_get_type(dev, &phys, iova, length);
+                if (type != IOMMU_RESV_DIRECT)
+                    continue;
+

If you look in iommu_resv_region_get_type() you can emit

        dev_warn(dev, "treating non-direct mapping [%pr] -> [%pap-%pap] as reservation\n", phys,
                 &start, &end);
        return IOMMU_RESV_RESERVED;

on a mismatch - which is not what the code will do now. Mismatch will warn and then not map.

So maybe dev_err() saying you won't map - in the new patch for reg. Either way this isn't a change specific to iommu-ranges.

                  region = iommu_alloc_resv_region(iova, length, prot, type,
                                   GFP_KERNEL);
                  if (region)
@@ -265,6 +377,8 @@ void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
              }
          }
      }
+
+    of_iommu_derive_resv_regions(dev, list);
  #endif
  }
  EXPORT_SYMBOL(of_iommu_get_resv_regions);