[PATCH v3 1/2] iommu: Add iommu_get_pci_resv_windows() helper
From: Guanghui Feng
Date: Sun Oct 04 2026 - 12:23:05 EST
Add a common helper that walks a PCI device's host bridge MMIO windows
and returns them as IOMMU_RESV_RESERVED regions in a list. This
provides a reusable interface for subsystems that need visibility into
PCI host bridge MMIO address ranges, such as iommufd which wants to
expose these ranges to userspace for opt-in IOVA avoidance.
The helper builds its results in a local list using
iommu_insert_resv_region() so entries are sorted by start address and
overlapping same-type regions are merged. On allocation failure the
partial list is freed internally, leaving the caller's list head
unmodified.
Note: commit cd2c9fcf5c66 ("iommu/dma: Move PCI window region reservation back into dma specific path.")
intentionally keeps PCI window reservation out of the IOMMU group-level
reserved region API. This helper respects that boundary: it does not
inject windows into iommu_get_group_resv_regions(). Consumers that need
this information call the helper directly.
Signed-off-by: Guanghui Feng <guanghuifeng@xxxxxxxxxxxxxxxxx>
---
drivers/iommu/iommu-priv.h | 12 ++++++++
drivers/iommu/iommu.c | 57 ++++++++++++++++++++++++++++++++++++++
2 files changed, 69 insertions(+)
diff --git a/drivers/iommu/iommu-priv.h b/drivers/iommu/iommu-priv.h
index aaffad5854fc..17f2234dabd4 100644
--- a/drivers/iommu/iommu-priv.h
+++ b/drivers/iommu/iommu-priv.h
@@ -123,4 +123,16 @@ static inline void iommu_debug_init(void)
#endif /* CONFIG_IOMMU_DEBUG_PAGEALLOC */
+struct pci_dev;
+
+#ifdef CONFIG_PCI
+int iommu_get_pci_resv_windows(struct pci_dev *dev, struct list_head *head);
+#else
+static inline int iommu_get_pci_resv_windows(struct pci_dev *dev,
+ struct list_head *head)
+{
+ return 0;
+}
+#endif /* CONFIG_PCI */
+
#endif /* __LINUX_IOMMU_PRIV_H */
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index cd1bca7ede9a..25bc99114e1e 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -945,6 +945,63 @@ iommu_insert_device_resv_regions(struct list_head *dev_resv_regions,
return ret;
}
+#ifdef CONFIG_PCI
+/**
+ * iommu_get_pci_resv_windows - collect PCI host bridge MMIO windows as
+ * reserved regions
+ * @dev: PCI device whose host bridge to scan
+ * @head: list head to append iommu_resv_region entries to
+ *
+ * Walks the MMIO windows of @dev's PCI host bridge and inserts an
+ * IOMMU_RESV_RESERVED region for each one using iommu_insert_resv_region(),
+ * keeping the entries sorted by start address and merging overlapping
+ * regions of the same type. On success the inserted entries are appended
+ * to @head and the caller must free them with kfree() when done. On
+ * failure any entries built so far are freed internally and @head is left
+ * unmodified.
+ *
+ * Returns 0 on success, negative errno on failure.
+ */
+int iommu_get_pci_resv_windows(struct pci_dev *dev, struct list_head *head)
+{
+ struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
+ struct iommu_resv_region *region, *next;
+ struct resource_entry *window;
+ LIST_HEAD(resv_windows);
+ int ret;
+
+ if (!bridge)
+ return -ENODEV;
+
+ resource_list_for_each_entry(window, &bridge->windows) {
+ struct iommu_resv_region tmp = {
+ .type = IOMMU_RESV_RESERVED,
+ };
+
+ if (resource_type(window->res) != IORESOURCE_MEM)
+ continue;
+
+ tmp.start = window->res->start - window->offset;
+ tmp.length = window->res->end - window->res->start + 1;
+
+ ret = iommu_insert_resv_region(&tmp, &resv_windows);
+ if (ret)
+ goto err_free;
+ }
+
+ list_splice_tail(&resv_windows, head);
+ return 0;
+
+err_free:
+ list_for_each_entry_safe(region, next, &resv_windows, list) {
+ list_del(®ion->list);
+ kfree(region);
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iommu_get_pci_resv_windows);
+#endif /* CONFIG_PCI */
+
int iommu_get_group_resv_regions(struct iommu_group *group,
struct list_head *head)
{
--
2.43.7