* [PATCH v7 1/5] iommu/dma: Keep reserved regions below the IOVA rbtree anchor
2026-11-06 17:14 [PATCH v7 0/5] of_iommu: add support for "iommu-ranges" Vikash Garodia
@ 2026-11-06 17:14 ` Vikash Garodia
2026-11-06 17:14 ` [PATCH v7 2/5] iommu: Use resource_size_t for reserved region length Vikash Garodia
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Vikash Garodia @ 2026-11-06 17:14 UTC (permalink / raw)
To: Joerg Roedel (AMD),
Vasant Hegde, Suravee Suthikulpanit, Will Deacon, Robin Murphy,
David Woodhouse, Lu Baolu, Jean-Philippe Brucker, Rob Herring,
Saravana Kannan, Tomasz Nowicki, Eric Auger,
Bryan O'Donoghue, Joerg Roedel, Srinath Mannam,
Oza Pawandeep, Lorenzo Pieralisi, Bjorn Helgaas, Alex Williamson
Cc: iommu, linux-kernel, virtualization, devicetree, kvm,
linux-arm-msm, linux-media, Vikash Garodia, Sashiko, stable,
Vishnu Reddy
The IOVA allocator tracks address ranges by page frame number (PFN).
The highest node in its rbtree is a fixed anchor at PFN ~0UL, which is
embedded in the DMA cookie. dma-iommu converts reserved regions to PFNs
with iova_pfn(), which returns unsigned long. On x86-32 with PAE,
addresses are 64-bit but unsigned long is 32-bit, so the upper bits of
the PFN are lost.
A reserved region that ends at the top of the address space then gets
the anchor's PFN. reserve_iova() merges it into the anchor and lowers
the anchor's pfn_lo. At teardown, free_iova_mem() no longer recognises
the anchor and frees it to iova_cache, which corrupts the slab. PCI host
bridge windows above 16TiB and the reservation after the last PCI DMA
window hit the same problem.
Compute the PFNs in u64. Skip ranges that start above the PFN space and
clamp the end one PFN below the anchor. The allocator never hands out
those IOVAs, so nothing is lost. On 64-bit, PFNs never reach ~0UL, so
nothing changes there.
Fixes: fade1ec055dc ("iommu/dma: Avoid PCI host bridge windows")
Fixes: 7c1b058c8b5a ("iommu/dma: Handle IOMMU API reserved regions")
Fixes: aadad097cd46 ("iommu/dma: Reserve IOVA for PCIe inaccessible DMA address")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/sashiko-outbox-195658@kernel.org/
Cc: stable@vger.kernel.org
Co-developed-by: Vishnu Reddy <busanna.reddy@oss.qualcomm.com>
Signed-off-by: Vishnu Reddy <busanna.reddy@oss.qualcomm.com>
Signed-off-by: Vikash Garodia <vikash.garodia@oss.qualcomm.com>
---
drivers/iommu/dma-iommu.c | 32 ++++++++++++++++++++------------
1 file changed, 20 insertions(+), 12 deletions(-)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 78742aeba932de1054fbf394c4fae86e9b1be20c..fbf788ac055b6d1e87482c3de043ef57bb12d9e6 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -505,21 +505,34 @@ static int iommu_dma_ranges_sort(void *priv, const struct list_head *a,
return res_a->res->start > res_b->res->start;
}
+/*
+ * IOVA PFNs are unsigned long, and ~0UL is the rbtree anchor. On 32-bit
+ * with a 64-bit dma_addr_t, iova_pfn() would truncate the range. Compute
+ * the PFNs in u64 instead, skip a range that starts beyond the PFN space
+ * and clamp its end below the anchor. Such IOVAs cannot be allocated.
+ */
+static void iova_reserve_range(struct iova_domain *iovad, u64 start, u64 end)
+{
+ u64 lo = start >> iova_shift(iovad);
+ u64 hi = end >> iova_shift(iovad);
+
+ if (lo < ULONG_MAX)
+ reserve_iova(iovad, lo, min(hi, ULONG_MAX - 1));
+}
+
static int iova_reserve_pci_windows(struct pci_dev *dev,
struct iova_domain *iovad)
{
struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
struct resource_entry *window;
- unsigned long lo, hi;
phys_addr_t start = 0, end;
resource_list_for_each_entry(window, &bridge->windows) {
if (resource_type(window->res) != IORESOURCE_MEM)
continue;
- lo = iova_pfn(iovad, window->res->start - window->offset);
- hi = iova_pfn(iovad, window->res->end - window->offset);
- reserve_iova(iovad, lo, hi);
+ iova_reserve_range(iovad, window->res->start - window->offset,
+ window->res->end - window->offset);
}
/* Get reserved DMA windows from host bridge */
@@ -528,9 +541,7 @@ static int iova_reserve_pci_windows(struct pci_dev *dev,
end = window->res->start - window->offset;
resv_iova:
if (end > start) {
- lo = iova_pfn(iovad, start);
- hi = iova_pfn(iovad, end);
- reserve_iova(iovad, lo, hi);
+ iova_reserve_range(iovad, start, end);
} else if (end < start) {
/* DMA ranges should be non-overlapping */
dev_err(&dev->dev,
@@ -568,15 +579,12 @@ static int iova_reserve_iommu_regions(struct device *dev,
iommu_get_resv_regions(dev, &resv_regions);
list_for_each_entry(region, &resv_regions, list) {
- unsigned long lo, hi;
-
/* We ARE the software that manages these! */
if (region->type == IOMMU_RESV_SW_MSI)
continue;
- lo = iova_pfn(iovad, region->start);
- hi = iova_pfn(iovad, region->start + region->length - 1);
- reserve_iova(iovad, lo, hi);
+ iova_reserve_range(iovad, region->start,
+ region->start + region->length - 1);
if (region->type == IOMMU_RESV_MSI)
ret = cookie_init_hw_msi_region(cookie, region->start,
--
2.34.1
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v7 2/5] iommu: Use resource_size_t for reserved region length
2026-11-06 17:14 [PATCH v7 0/5] of_iommu: add support for "iommu-ranges" Vikash Garodia
2026-11-06 17:14 ` [PATCH v7 1/5] iommu/dma: Keep reserved regions below the IOVA rbtree anchor Vikash Garodia
@ 2026-11-06 17:14 ` Vikash Garodia
2026-11-10 17:28 ` Rob Herring (Arm)
2026-11-06 17:14 ` [PATCH v7 3/5] iommu/of: Make OF_IOMMU depend on OF_ADDRESS Vikash Garodia
` (2 subsequent siblings)
4 siblings, 1 reply; 7+ messages in thread
From: Vikash Garodia @ 2026-11-06 17:14 UTC (permalink / raw)
To: Joerg Roedel (AMD),
Vasant Hegde, Suravee Suthikulpanit, Will Deacon, Robin Murphy,
David Woodhouse, Lu Baolu, Jean-Philippe Brucker, Rob Herring,
Saravana Kannan, Tomasz Nowicki, Eric Auger,
Bryan O'Donoghue, Joerg Roedel, Srinath Mannam,
Oza Pawandeep, Lorenzo Pieralisi, Bjorn Helgaas, Alex Williamson
Cc: iommu, linux-kernel, virtualization, devicetree, kvm,
linux-arm-msm, linux-media, Vikash Garodia, Sashiko, stable,
Vishnu Reddy
On 32-bit systems with LPAE, physical addresses are 64-bit. A reserved
region can be larger than 4GiB, but its length is stored as size_t,
which is only 32-bit there. The length gets truncated.
Use resource_size_t for the length so it matches the width of
phys_addr_t and holds the full size of the region.
Fixes: 2b20cbba3390 ("iommu: iommu_alloc_resv_region")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/all/d223ee9f-b262-408a-a201-8b16d310bbe0@oss.qualcomm.com
Cc: stable@vger.kernel.org
Co-developed-by: Vishnu Reddy <busanna.reddy@oss.qualcomm.com>
Signed-off-by: Vishnu Reddy <busanna.reddy@oss.qualcomm.com>
Signed-off-by: Vikash Garodia <vikash.garodia@oss.qualcomm.com>
---
drivers/iommu/amd/iommu.c | 2 +-
drivers/iommu/intel/iommu.c | 2 +-
drivers/iommu/iommu.c | 2 +-
drivers/iommu/of_iommu.c | 4 ++--
drivers/iommu/virtio-iommu.c | 2 +-
drivers/of/address.c | 2 +-
drivers/vfio/vfio_iommu_type1.c | 2 +-
include/linux/iommu.h | 4 ++--
include/linux/of_address.h | 2 +-
9 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 960e1dc05ec69b346d1af73241dfa51393b0851a..179e6c28afe6f86c233d9d7e64abb0870095e985 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -3327,7 +3327,7 @@ static void amd_iommu_get_resv_regions(struct device *dev,
list_for_each_entry(entry, &pci_seg->unity_map, list) {
int type, prot = 0;
- size_t length;
+ resource_size_t length;
if (devid < entry->devid_start || devid > entry->devid_end)
continue;
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index 5a2e705a2f2e025245d4b42c4a9f765c67d708d4..70e210ca085ff52519376358a3e3b7a01619d7a8 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -3535,7 +3535,7 @@ static void intel_iommu_get_resv_regions(struct device *device,
i, i_dev) {
struct iommu_resv_region *resv;
enum iommu_resv_type type;
- size_t length;
+ resource_size_t length;
if (i_dev != device &&
!is_downstream_to_pci_bridge(device, i_dev))
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b486b8bbd1fcdc359d936234bb3f8631306c8786..18f3efb94043ace9f1f4abc7c6ac8974dc25b0c0 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -3010,7 +3010,7 @@ void iommu_put_resv_regions(struct device *dev, struct list_head *list)
EXPORT_SYMBOL(iommu_put_resv_regions);
struct iommu_resv_region *iommu_alloc_resv_region(phys_addr_t start,
- size_t length, int prot,
+ resource_size_t length, int prot,
enum iommu_resv_type type,
gfp_t gfp)
{
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index a18bb60f6f3dfdba853b6e16dd2a8616f6a731c7..5c266511db78910d6fd221af5437cecff4931c22 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -170,7 +170,7 @@ int of_iommu_configure(struct device *dev, struct device_node *master_np,
static enum iommu_resv_type __maybe_unused
iommu_resv_region_get_type(struct device *dev,
struct resource *phys,
- phys_addr_t start, size_t length)
+ phys_addr_t start, resource_size_t length)
{
phys_addr_t end = start + length - 1;
@@ -246,7 +246,7 @@ void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
struct iommu_resv_region *region;
enum iommu_resv_type type;
phys_addr_t iova;
- size_t length;
+ resource_size_t length;
if (of_dma_is_coherent(dev->of_node))
prot |= IOMMU_CACHE;
diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c
index 587fc13197f12227e69aedbc721cfd49cebde2ef..a37e713b0678a516a777436d616f0d3df54eae38 100644
--- a/drivers/iommu/virtio-iommu.c
+++ b/drivers/iommu/virtio-iommu.c
@@ -483,7 +483,7 @@ static int viommu_add_resv_mem(struct viommu_endpoint *vdev,
struct virtio_iommu_probe_resv_mem *mem,
size_t len)
{
- size_t size;
+ resource_size_t size;
u64 start64, end64;
phys_addr_t start, end;
struct iommu_resv_region *region = NULL, *next;
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 499d37ceae210ac844eef853aeecbfabaaa5e138..f665432638a6d1cc1ebfc0d2356b813c8c584910 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -655,7 +655,7 @@ EXPORT_SYMBOL(of_translate_dma_address);
* Returns a pointer to the cell immediately following the translated DMA region.
*/
const __be32 *of_translate_dma_region(struct device_node *dev, const __be32 *prop,
- phys_addr_t *start, size_t *length)
+ phys_addr_t *start, resource_size_t *length)
{
struct device_node *parent __free(device_node) = __of_get_dma_parent(dev);
u64 address, size;
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index f7addfbe1aab4f6fb662b8b2fa66bc02d98b5f03..9f508143505ad9e603af1730e811c30bd5edce16 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -164,7 +164,7 @@ vfio_iommu_find_iommu_group(struct vfio_iommu *iommu,
*/
static struct vfio_dma *vfio_find_dma(struct vfio_iommu *iommu,
- dma_addr_t start, size_t size)
+ dma_addr_t start, resource_size_t size)
{
struct rb_node *node = iommu->dma_list.rb_node;
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index ef5043752a3651419679316c5a20cdf049efeaba..1d8f9381a358ec2309503bfe2866f08a1f236b2b 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -306,7 +306,7 @@ enum iommu_resv_type {
struct iommu_resv_region {
struct list_head list;
phys_addr_t start;
- size_t length;
+ resource_size_t length;
int prot;
enum iommu_resv_type type;
void (*free)(struct device *dev, struct iommu_resv_region *region);
@@ -968,7 +968,7 @@ extern void iommu_set_default_passthrough(bool cmd_line);
extern void iommu_set_default_translated(bool cmd_line);
extern bool iommu_default_passthrough(void);
extern struct iommu_resv_region *
-iommu_alloc_resv_region(phys_addr_t start, size_t length, int prot,
+iommu_alloc_resv_region(phys_addr_t start, resource_size_t length, int prot,
enum iommu_resv_type type, gfp_t gfp);
extern int iommu_get_group_resv_regions(struct iommu_group *group,
struct list_head *head);
diff --git a/include/linux/of_address.h b/include/linux/of_address.h
index 0cff9036539168b0cf2826ee34417738a4836ace..ae6d96c8c110adad966f2c1bad2cbefaf55e5c57 100644
--- a/include/linux/of_address.h
+++ b/include/linux/of_address.h
@@ -56,7 +56,7 @@ static inline int of_range_count(const struct of_range_parser *parser)
extern u64 of_translate_dma_address(struct device_node *dev,
const __be32 *in_addr);
extern const __be32 *of_translate_dma_region(struct device_node *dev, const __be32 *addr,
- phys_addr_t *start, size_t *length);
+ phys_addr_t *start, resource_size_t *length);
#ifdef CONFIG_OF_ADDRESS
extern u64 of_translate_address(struct device_node *np, const __be32 *addr);
--
2.34.1
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH v7 2/5] iommu: Use resource_size_t for reserved region length
2026-11-06 17:14 ` [PATCH v7 2/5] iommu: Use resource_size_t for reserved region length Vikash Garodia
@ 2026-11-10 17:28 ` Rob Herring (Arm)
0 siblings, 0 replies; 7+ messages in thread
From: Rob Herring (Arm) @ 2026-11-10 17:28 UTC (permalink / raw)
To: Vikash Garodia
Cc: stable, Lu Baolu, Tomasz Nowicki, virtualization, devicetree,
kvm, linux-media, iommu, Bryan O'Donoghue, Sashiko,
Robin Murphy, Suravee Suthikulpanit, Eric Auger, Vasant Hegde,
Will Deacon, Oza Pawandeep, linux-arm-msm, Jean-Philippe Brucker,
Bjorn Helgaas, Joerg Roedel (AMD),
Srinath Mannam, linux-kernel, Joerg Roedel, Saravana Kannan,
Lorenzo Pieralisi, Vishnu Reddy, David Woodhouse,
Alex Williamson
On Fri, 06 Nov 2026 22:44:25 +0530, Vikash Garodia wrote:
> On 32-bit systems with LPAE, physical addresses are 64-bit. A reserved
> region can be larger than 4GiB, but its length is stored as size_t,
> which is only 32-bit there. The length gets truncated.
> Use resource_size_t for the length so it matches the width of
> phys_addr_t and holds the full size of the region.
>
> Fixes: 2b20cbba3390 ("iommu: iommu_alloc_resv_region")
> Reported-by: Sashiko <sashiko-bot@kernel.org>
> Closes: https://lore.kernel.org/all/d223ee9f-b262-408a-a201-8b16d310bbe0@oss.qualcomm.com
> Cc: stable@vger.kernel.org
> Co-developed-by: Vishnu Reddy <busanna.reddy@oss.qualcomm.com>
> Signed-off-by: Vishnu Reddy <busanna.reddy@oss.qualcomm.com>
> Signed-off-by: Vikash Garodia <vikash.garodia@oss.qualcomm.com>
> ---
> drivers/iommu/amd/iommu.c | 2 +-
> drivers/iommu/intel/iommu.c | 2 +-
> drivers/iommu/iommu.c | 2 +-
> drivers/iommu/of_iommu.c | 4 ++--
> drivers/iommu/virtio-iommu.c | 2 +-
> drivers/of/address.c | 2 +-
> drivers/vfio/vfio_iommu_type1.c | 2 +-
> include/linux/iommu.h | 4 ++--
> include/linux/of_address.h | 2 +-
> 9 files changed, 11 insertions(+), 11 deletions(-)
>
Acked-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v7 3/5] iommu/of: Make OF_IOMMU depend on OF_ADDRESS
2026-11-06 17:14 [PATCH v7 0/5] of_iommu: add support for "iommu-ranges" Vikash Garodia
2026-11-06 17:14 ` [PATCH v7 1/5] iommu/dma: Keep reserved regions below the IOVA rbtree anchor Vikash Garodia
2026-11-06 17:14 ` [PATCH v7 2/5] iommu: Use resource_size_t for reserved region length Vikash Garodia
@ 2026-11-06 17:14 ` Vikash Garodia
2026-11-06 17:14 ` [PATCH v7 4/5] iommu/of: Require reg for iommu-addresses Vikash Garodia
2026-11-06 17:14 ` [PATCH v7 5/5] iommu/of: Add support for "iommu-ranges" on a device node Vikash Garodia
4 siblings, 0 replies; 7+ messages in thread
From: Vikash Garodia @ 2026-11-06 17:14 UTC (permalink / raw)
To: Joerg Roedel (AMD),
Vasant Hegde, Suravee Suthikulpanit, Will Deacon, Robin Murphy,
David Woodhouse, Lu Baolu, Jean-Philippe Brucker, Rob Herring,
Saravana Kannan, Tomasz Nowicki, Eric Auger,
Bryan O'Donoghue, Joerg Roedel, Srinath Mannam,
Oza Pawandeep, Lorenzo Pieralisi, Bjorn Helgaas, Alex Williamson
Cc: iommu, linux-kernel, virtualization, devicetree, kvm,
linux-arm-msm, linux-media, Vikash Garodia, Vishnu Reddy
The OF_IOMMU code relies on OF address parsing helpers, but the
dependency on OF_ADDRESS is not expressed in Kconfig. As a result,
of_iommu.c contains CONFIG_OF_ADDRESS guards and a __maybe_unused
annotation to handle configurations where OF_ADDRESS is disabled.
Make OF_IOMMU depend on OF_ADDRESS and remove the now unnecessary
preprocessor guards and __maybe_unused annotation.
The only architecture supporting OF without OF_ADDRESS is SPARC, which
does not use OF_IOMMU, so no functional change is intended.
Suggested-by: Rob Herring <robh@kernel.org>
Co-developed-by: Vishnu Reddy <busanna.reddy@oss.qualcomm.com>
Signed-off-by: Vishnu Reddy <busanna.reddy@oss.qualcomm.com>
Signed-off-by: Vikash Garodia <vikash.garodia@oss.qualcomm.com>
---
drivers/iommu/Kconfig | 2 +-
drivers/iommu/of_iommu.c | 4 +---
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 85a8d7fdfa30b64e21b8db0db1d5bc268c04f24b..c1e97bd885f933cae45fb7806183477f2d64cd5d 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -147,7 +147,7 @@ endchoice
config OF_IOMMU
def_bool y
- depends on OF && IOMMU_API
+ depends on OF_ADDRESS && IOMMU_API
# IOMMU-agnostic DMA-mapping layer
config IOMMU_DMA
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 5c266511db78910d6fd221af5437cecff4931c22..6675fc900e6c2d55b34c61dc9effaeb48e24f18f 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -167,7 +167,7 @@ int of_iommu_configure(struct device *dev, struct device_node *master_np,
return err;
}
-static enum iommu_resv_type __maybe_unused
+static enum iommu_resv_type
iommu_resv_region_get_type(struct device *dev,
struct resource *phys,
phys_addr_t start, resource_size_t length)
@@ -203,7 +203,6 @@ iommu_resv_region_get_type(struct device *dev,
*/
void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
{
-#if IS_ENABLED(CONFIG_OF_ADDRESS)
struct of_phandle_iterator it;
int err;
@@ -265,6 +264,5 @@ void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
}
}
}
-#endif
}
EXPORT_SYMBOL(of_iommu_get_resv_regions);
--
2.34.1
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v7 4/5] iommu/of: Require reg for iommu-addresses
2026-11-06 17:14 [PATCH v7 0/5] of_iommu: add support for "iommu-ranges" Vikash Garodia
` (2 preceding siblings ...)
2026-11-06 17:14 ` [PATCH v7 3/5] iommu/of: Make OF_IOMMU depend on OF_ADDRESS Vikash Garodia
@ 2026-11-06 17:14 ` Vikash Garodia
2026-11-06 17:14 ` [PATCH v7 5/5] iommu/of: Add support for "iommu-ranges" on a device node Vikash Garodia
4 siblings, 0 replies; 7+ messages in thread
From: Vikash Garodia @ 2026-11-06 17:14 UTC (permalink / raw)
To: Joerg Roedel (AMD),
Vasant Hegde, Suravee Suthikulpanit, Will Deacon, Robin Murphy,
David Woodhouse, Lu Baolu, Jean-Philippe Brucker, Rob Herring,
Saravana Kannan, Tomasz Nowicki, Eric Auger,
Bryan O'Donoghue, Joerg Roedel, Srinath Mannam,
Oza Pawandeep, Lorenzo Pieralisi, Bjorn Helgaas, Alex Williamson
Cc: iommu, linux-kernel, virtualization, devicetree, kvm,
linux-arm-msm, linux-media, Vikash Garodia, Vishnu Reddy
The "iommu-addresses" property describes an identity 1:1 mapping between
an IOVA range and the physical memory specified by "reg" in the same
reserved-memory node. Without "reg", there is no physical memory to map.
The DT schema now requires "reg" whenever "iommu-addresses" is present.
Align the kernel implementation with this requirement.
https://github.com/devicetree-org/dt-schema/pull/207
An identity mapping requires matching IOVA and "reg" ranges. Reject
mismatched ranges and report an error.
Suggested-by: Rob Herring <robh@kernel.org>
Co-developed-by: Vishnu Reddy <busanna.reddy@oss.qualcomm.com>
Signed-off-by: Vishnu Reddy <busanna.reddy@oss.qualcomm.com>
Signed-off-by: Vikash Garodia <vikash.garodia@oss.qualcomm.com>
---
drivers/iommu/of_iommu.c | 32 +++++++++++++++-----------------
1 file changed, 15 insertions(+), 17 deletions(-)
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 6675fc900e6c2d55b34c61dc9effaeb48e24f18f..00b02c19f27e195fc36c4bc1d91253255147c9ab 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -174,19 +174,12 @@ iommu_resv_region_get_type(struct device *dev,
{
phys_addr_t end = start + length - 1;
- /*
- * IOMMU regions without an associated physical region cannot be
- * mapped and are simply reservations.
- */
- if (phys->start >= phys->end)
- return IOMMU_RESV_RESERVED;
-
/* may be IOMMU_RESV_DIRECT_RELAXABLE for certain cases */
if (start == phys->start && end == phys->end)
return IOMMU_RESV_DIRECT;
- dev_warn(dev, "treating non-direct mapping [%pr] -> [%pap-%pap] as reservation\n", phys,
- &start, &end);
+ dev_err(dev, "non-direct mapping [%pr] -> [%pap-%pap] is not supported\n", phys,
+ &start, &end);
return IOMMU_RESV_RESERVED;
}
@@ -211,12 +204,14 @@ void of_iommu_get_resv_regions(struct device *dev, struct list_head *list)
struct resource phys;
int size;
- 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);
@@ -225,11 +220,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;
+ }
end = maps + size / sizeof(__be32);
@@ -257,6 +252,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;
+
region = iommu_alloc_resv_region(iova, length, prot, type,
GFP_KERNEL);
if (region)
--
2.34.1
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v7 5/5] iommu/of: Add support for "iommu-ranges" on a device node
2026-11-06 17:14 [PATCH v7 0/5] of_iommu: add support for "iommu-ranges" Vikash Garodia
` (3 preceding siblings ...)
2026-11-06 17:14 ` [PATCH v7 4/5] iommu/of: Require reg for iommu-addresses Vikash Garodia
@ 2026-11-06 17:14 ` Vikash Garodia
4 siblings, 0 replies; 7+ messages in thread
From: Vikash Garodia @ 2026-11-06 17:14 UTC (permalink / raw)
To: Joerg Roedel (AMD),
Vasant Hegde, Suravee Suthikulpanit, Will Deacon, Robin Murphy,
David Woodhouse, Lu Baolu, Jean-Philippe Brucker, Rob Herring,
Saravana Kannan, Tomasz Nowicki, Eric Auger,
Bryan O'Donoghue, Joerg Roedel, Srinath Mannam,
Oza Pawandeep, Lorenzo Pieralisi, Bjorn Helgaas, Alex Williamson
Cc: iommu, linux-kernel, virtualization, devicetree, kvm,
linux-arm-msm, linux-media, Vikash Garodia, Dmitry Baryshkov,
Vishnu Reddy
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@kernel.org
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@kernel.org>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Co-developed-by: Vishnu Reddy <busanna.reddy@oss.qualcomm.com>
Signed-off-by: Vishnu Reddy <busanna.reddy@oss.qualcomm.com>
Signed-off-by: Vikash Garodia <vikash.garodia@oss.qualcomm.com>
---
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(®ion->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(®ion->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
^ permalink raw reply [flat|nested] 7+ messages in thread