mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] iommu: Reserve PCI host bridge MMIO windows in group reserved regions
@ 2026-09-21  7:02 Guanghui Feng
  2026-09-21 10:39 ` [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA Guanghui Feng
  0 siblings, 1 reply; 9+ messages in thread
From: Guanghui Feng @ 2026-09-21  7:02 UTC (permalink / raw)
  To: robin.murphy, joro, will; +Cc: iommu, linux-kernel, alex, kvm

The DMA IOVA layer reserves PCI host bridge MMIO windows via
iova_reserve_pci_windows() to prevent IOVA allocations from
overlapping with those address ranges.  As commit fade1ec055dc
("iommu/dma: Avoid PCI host bridge windows") explains, a host
bridge may interpret addresses falling within its MMIO windows as
peer-to-peer DMA, leading to faults, data corruption, or DMA
transactions being misrouted to the wrong PCIe device.

However, VFIO type1 obtains reserved regions through
iommu_get_group_resv_regions() and reports the available IOVA
ranges to userspace via VFIO_IOMMU_GET_INFO.  Previously
iommu_get_group_resv_regions() only returned IOMMU driver-level
reserved regions (RMRR, Unity Maps, MSI regions, etc.) and did
not include PCI host bridge MMIO windows.  This allowed userspace
to choose IOVA addresses that overlap with bridge windows.

The problem is especially dangerous when a device sits behind a
PCIe switch and ACS Upstream Forwarding is not fully enabled: the
switch may route DMA TLPs as peer-to-peer traffic to other
downstream devices instead of forwarding them upstream to the
root complex for IOMMU translation.

Fix this by:

  - Introducing iommu_get_pci_resv_windows(), a common helper
    that walks a PCI host bridge's MMIO windows and creates
    IOMMU_RESV_RESERVED region entries for each one.

  - Calling iommu_resv_pci_windows() from
    iommu_get_group_resv_regions() so that all group-level
    consumers (VFIO type1, sysfs reserved_regions) automatically
    receive the PCI window reservations.

  - Refactoring iova_reserve_pci_windows() to reuse
    iommu_get_pci_resv_windows(), eliminating the duplicated
    bridge window enumeration logic.

Signed-off-by: Guanghui Feng <guanghuifeng@linux.alibaba.com>
---
 drivers/iommu/dma-iommu.c  | 21 +++++++----
 drivers/iommu/iommu-priv.h | 11 ++++++
 drivers/iommu/iommu.c      | 73 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 99 insertions(+), 6 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 58c624513cd4..bccb53043462 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -37,6 +37,7 @@
 
 #include "dma-iommu.h"
 #include "iommu-pages.h"
+#include "iommu-priv.h"
 
 struct iommu_dma_msi_page {
 	struct list_head	list;
@@ -508,18 +509,26 @@ 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 iommu_resv_region *region, *next;
 	struct resource_entry *window;
 	unsigned long lo, hi;
 	phys_addr_t start = 0, end;
+	LIST_HEAD(pci_windows);
+	int ret;
 
-	resource_list_for_each_entry(window, &bridge->windows) {
-		if (resource_type(window->res) != IORESOURCE_MEM)
-			continue;
+	ret = iommu_get_pci_resv_windows(dev, &pci_windows);
 
-		lo = iova_pfn(iovad, window->res->start - window->offset);
-		hi = iova_pfn(iovad, window->res->end - window->offset);
-		reserve_iova(iovad, lo, hi);
+	list_for_each_entry_safe(region, next, &pci_windows, list) {
+		if (!ret) {
+			lo = iova_pfn(iovad, region->start);
+			hi = iova_pfn(iovad, region->start + region->length - 1);
+			reserve_iova(iovad, lo, hi);
+		}
+		list_del(&region->list);
+		kfree(region);
 	}
+	if (ret)
+		return ret;
 
 	/* Get reserved DMA windows from host bridge */
 	list_sort(NULL, &bridge->dma_ranges, iommu_dma_ranges_sort);
diff --git a/drivers/iommu/iommu-priv.h b/drivers/iommu/iommu-priv.h
index aaffad5854fc..bc7fab0c827e 100644
--- a/drivers/iommu/iommu-priv.h
+++ b/drivers/iommu/iommu-priv.h
@@ -123,4 +123,15 @@ static inline void iommu_debug_init(void)
 
 #endif /* CONFIG_IOMMU_DEBUG_PAGEALLOC */
 
+#ifdef CONFIG_PCI
+struct pci_dev;
+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..de675ed27753 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -945,6 +945,72 @@ 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 creates an
+ * IOMMU_RESV_RESERVED region for each one.  The caller must free the
+ * returned entries with kfree() when done.
+ *
+ * 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 resource_entry *window;
+
+	resource_list_for_each_entry(window, &bridge->windows) {
+		struct iommu_resv_region *region;
+		phys_addr_t start;
+		size_t length;
+
+		if (resource_type(window->res) != IORESOURCE_MEM)
+			continue;
+
+		start = window->res->start - window->offset;
+		length = window->res->end - window->res->start + 1;
+
+		region = iommu_alloc_resv_region(start, length, 0,
+						 IOMMU_RESV_RESERVED,
+						 GFP_KERNEL);
+		if (!region)
+			return -ENOMEM;
+
+		list_add_tail(&region->list, head);
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_get_pci_resv_windows);
+
+/*
+ * Reserve PCI host bridge MMIO windows as IOMMU_RESV_RESERVED regions.
+ * This prevents IOVA allocations from overlapping with PCI MMIO address
+ * ranges, which could cause PCIe switches to misroute DMA transactions.
+ *
+ * All PCI devices within the same IOMMU group share the same host bridge,
+ * so we only need to find the first PCI device.
+ *
+ * Caller must hold group->mutex.
+ */
+static int iommu_resv_pci_windows(struct iommu_group *group,
+				  struct list_head *head)
+{
+	struct group_device *gdev;
+
+	for_each_group_device(group, gdev) {
+		if (!dev_is_pci(gdev->dev))
+			continue;
+		return iommu_get_pci_resv_windows(to_pci_dev(gdev->dev),
+						  head);
+	}
+	return 0;
+}
+#endif /* CONFIG_PCI */
+
 int iommu_get_group_resv_regions(struct iommu_group *group,
 				 struct list_head *head)
 {
@@ -969,6 +1035,13 @@ int iommu_get_group_resv_regions(struct iommu_group *group,
 		if (ret)
 			break;
 	}
+
+	/* Reserve PCI host bridge MMIO windows to prevent IOVA conflicts */
+#ifdef CONFIG_PCI
+	if (!ret)
+		ret = iommu_resv_pci_windows(group, head);
+#endif
+
 	mutex_unlock(&group->mutex);
 	return ret;
 }
-- 
2.43.7


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA
  2026-09-21  7:02 [PATCH] iommu: Reserve PCI host bridge MMIO windows in group reserved regions Guanghui Feng
@ 2026-09-21 10:39 ` Guanghui Feng
  2026-09-21 10:39   ` [PATCH v2 1/2] iommu: Reserve PCI host bridge MMIO windows in group reserved regions Guanghui Feng
                     ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Guanghui Feng @ 2026-09-21 10:39 UTC (permalink / raw)
  To: jgg, kevin.tian, alex, joro, robin.murphy, will; +Cc: iommu, kvm, linux-kernel

The DMA IOVA layer reserves PCI host bridge MMIO windows via
iova_reserve_pci_windows() to keep IOVA allocations from overlapping
those address ranges.  As commit fade1ec055dc ("iommu/dma: Avoid PCI
host bridge windows") explains, a host bridge may interpret addresses
falling within its MMIO windows as peer-to-peer DMA, leading to
faults, data corruption, or DMA transactions being misrouted to the
wrong PCIe device.  This is especially dangerous when a device sits
behind a PCIe switch and ACS Upstream Forwarding is not fully enabled.

Passthrough paths that let userspace pick IOVAs did not honour these
windows: VFIO type1 and iommufd only excluded IOMMU driver-level
reserved regions (RMRR, unity maps, MSI), not PCI host bridge MMIO
windows.

This series closes that gap:

  Patch 1 adds a common helper, iommu_get_pci_resv_windows(), and
  reserves PCI host bridge MMIO windows in
  iommu_get_group_resv_regions() so all group-level consumers (VFIO
  type1, sysfs reserved_regions) avoid them; iova_reserve_pci_windows()
  is refactored to reuse the helper.

  Patch 2 switches iommufd's iopt_table_enforce_dev_resv_regions() to
  iommu_get_group_resv_regions() so IOMMU_IOAS_IOVA_RANGES also avoids
  PCI host bridge MMIO windows, aligning iommufd with VFIO type1.

v1: https://lore.kernel.org/all/20260921070234.897736-1-guanghuifeng@linux.alibaba.com/

Changes since v1:
- v1 was a single patch; v2 is a two-patch series.
- Patch 1: build the reservations with iommu_insert_resv_region()
  instead of list_add_tail(), so the group reserved list stays sorted
  by start address and overlapping same-type regions are merged.  The
  helper now frees its partial list itself on error and leaves the
  caller's list untouched.
- Patch 2 (new): reserve PCI host bridge MMIO windows for the iommufd
  IOAS as well, so IOMMU_IOAS_IOVA_RANGES no longer reports IOVAs that
  overlap PCI MMIO windows.

Notes on earlier automated review feedback:
- The size_t length of each window matches struct iommu_resv_region's
  existing field type; no new truncation is introduced on 64-bit.
- An IOMMU group cannot span multiple PCI host bridges:
  pci_device_group() only groups devices within a single host bridge
  hierarchy, so scanning the first PCI device of the group is enough.

Guanghui Feng (2):
  iommu: Reserve PCI host bridge MMIO windows in group reserved regions
  iommufd: Reserve PCI host bridge MMIO windows in IOAS reserved regions

 drivers/iommu/dma-iommu.c            | 17 ++++--
 drivers/iommu/iommu-priv.h           | 11 ++++
 drivers/iommu/iommu.c                | 85 ++++++++++++++++++++++++++++
 drivers/iommu/iommufd/io_pagetable.c | 15 ++++-
 4 files changed, 121 insertions(+), 7 deletions(-)

-- 
2.43.7


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v2 1/2] iommu: Reserve PCI host bridge MMIO windows in group reserved regions
  2026-09-21 10:39 ` [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA Guanghui Feng
@ 2026-09-21 10:39   ` Guanghui Feng
  2026-09-21 10:39   ` [PATCH v2 2/2] iommufd: Reserve PCI host bridge MMIO windows in IOAS " Guanghui Feng
  2026-09-21 11:24   ` [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA Robin Murphy
  2 siblings, 0 replies; 9+ messages in thread
From: Guanghui Feng @ 2026-09-21 10:39 UTC (permalink / raw)
  To: jgg, kevin.tian, alex, joro, robin.murphy, will; +Cc: iommu, kvm, linux-kernel

The DMA IOVA layer reserves PCI host bridge MMIO windows via
iova_reserve_pci_windows() to prevent IOVA allocations from
overlapping with those address ranges.  As commit fade1ec055dc
("iommu/dma: Avoid PCI host bridge windows") explains, a host
bridge may interpret addresses falling within its MMIO windows as
peer-to-peer DMA, leading to faults, data corruption, or DMA
transactions being misrouted to the wrong PCIe device.

However, VFIO type1 obtains reserved regions through
iommu_get_group_resv_regions() and reports the available IOVA
ranges to userspace via VFIO_IOMMU_GET_INFO.  Previously
iommu_get_group_resv_regions() only returned IOMMU driver-level
reserved regions (RMRR, Unity Maps, MSI regions, etc.) and did
not include PCI host bridge MMIO windows.  This allowed userspace
to choose IOVA addresses that overlap with bridge windows.

The problem is especially dangerous when a device sits behind a
PCIe switch and ACS Upstream Forwarding is not fully enabled: the
switch may route DMA TLPs as peer-to-peer traffic to other
downstream devices instead of forwarding them upstream to the
root complex for IOMMU translation.

Fix this by:

  - Introducing iommu_get_pci_resv_windows(), a common helper
    that walks a PCI host bridge's MMIO windows and creates
    IOMMU_RESV_RESERVED region entries for each one.

  - Calling iommu_resv_pci_windows() from
    iommu_get_group_resv_regions() so that all group-level
    consumers (VFIO type1, sysfs reserved_regions) automatically
    receive the PCI window reservations.

  - Refactoring iova_reserve_pci_windows() to reuse
    iommu_get_pci_resv_windows(), eliminating the duplicated
    bridge window enumeration logic.

Signed-off-by: Guanghui Feng <guanghuifeng@linux.alibaba.com>
---
 drivers/iommu/dma-iommu.c  | 17 +++++---
 drivers/iommu/iommu-priv.h | 11 +++++
 drivers/iommu/iommu.c      | 85 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 108 insertions(+), 5 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 58c624513cd4..c97622826d9d 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -37,6 +37,7 @@
 
 #include "dma-iommu.h"
 #include "iommu-pages.h"
+#include "iommu-priv.h"
 
 struct iommu_dma_msi_page {
 	struct list_head	list;
@@ -508,17 +509,23 @@ 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 iommu_resv_region *region, *next;
 	struct resource_entry *window;
 	unsigned long lo, hi;
 	phys_addr_t start = 0, end;
+	LIST_HEAD(pci_windows);
+	int ret;
 
-	resource_list_for_each_entry(window, &bridge->windows) {
-		if (resource_type(window->res) != IORESOURCE_MEM)
-			continue;
+	ret = iommu_get_pci_resv_windows(dev, &pci_windows);
+	if (ret)
+		return ret;
 
-		lo = iova_pfn(iovad, window->res->start - window->offset);
-		hi = iova_pfn(iovad, window->res->end - window->offset);
+	list_for_each_entry_safe(region, next, &pci_windows, list) {
+		lo = iova_pfn(iovad, region->start);
+		hi = iova_pfn(iovad, region->start + region->length - 1);
 		reserve_iova(iovad, lo, hi);
+		list_del(&region->list);
+		kfree(region);
 	}
 
 	/* Get reserved DMA windows from host bridge */
diff --git a/drivers/iommu/iommu-priv.h b/drivers/iommu/iommu-priv.h
index aaffad5854fc..bc7fab0c827e 100644
--- a/drivers/iommu/iommu-priv.h
+++ b/drivers/iommu/iommu-priv.h
@@ -123,4 +123,15 @@ static inline void iommu_debug_init(void)
 
 #endif /* CONFIG_IOMMU_DEBUG_PAGEALLOC */
 
+#ifdef CONFIG_PCI
+struct pci_dev;
+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..b77e9818e4a4 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -945,6 +945,84 @@ 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;
+
+	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(&region->list);
+		kfree(region);
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(iommu_get_pci_resv_windows);
+
+/*
+ * Reserve PCI host bridge MMIO windows as IOMMU_RESV_RESERVED regions.
+ * This prevents IOVA allocations from overlapping with PCI MMIO address
+ * ranges, which could cause PCIe switches to misroute DMA transactions.
+ *
+ * All PCI devices within the same IOMMU group share the same host bridge,
+ * so we only need to find the first PCI device.
+ *
+ * Caller must hold group->mutex.
+ */
+static int iommu_resv_pci_windows(struct iommu_group *group,
+				  struct list_head *head)
+{
+	struct group_device *gdev;
+
+	for_each_group_device(group, gdev) {
+		if (!dev_is_pci(gdev->dev))
+			continue;
+		return iommu_get_pci_resv_windows(to_pci_dev(gdev->dev),
+						  head);
+	}
+	return 0;
+}
+#endif /* CONFIG_PCI */
+
 int iommu_get_group_resv_regions(struct iommu_group *group,
 				 struct list_head *head)
 {
@@ -969,6 +1047,13 @@ int iommu_get_group_resv_regions(struct iommu_group *group,
 		if (ret)
 			break;
 	}
+
+	/* Reserve PCI host bridge MMIO windows to prevent IOVA conflicts */
+#ifdef CONFIG_PCI
+	if (!ret)
+		ret = iommu_resv_pci_windows(group, head);
+#endif
+
 	mutex_unlock(&group->mutex);
 	return ret;
 }
-- 
2.43.7


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v2 2/2] iommufd: Reserve PCI host bridge MMIO windows in IOAS reserved regions
  2026-09-21 10:39 ` [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA Guanghui Feng
  2026-09-21 10:39   ` [PATCH v2 1/2] iommu: Reserve PCI host bridge MMIO windows in group reserved regions Guanghui Feng
@ 2026-09-21 10:39   ` Guanghui Feng
  2026-09-21 11:24   ` [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA Robin Murphy
  2 siblings, 0 replies; 9+ messages in thread
From: Guanghui Feng @ 2026-09-21 10:39 UTC (permalink / raw)
  To: jgg, kevin.tian, alex, joro, robin.murphy, will; +Cc: iommu, kvm, linux-kernel

iommufd builds the reserved IOVA ranges of an IOAS in
iopt_table_enforce_dev_resv_regions() by calling the per-device
iommu_get_resv_regions(), which only returns IOMMU driver-level
reserved regions (RMRR, unity maps, MSI windows, etc.) and does
not include PCI host bridge MMIO windows.  As a result, the ranges
reported to userspace by IOMMU_IOAS_IOVA_RANGES may overlap with a
host bridge's MMIO windows, allowing userspace to map IOVAs that a
PCIe switch might misinterpret as peer-to-peer DMA and misroute to
the wrong device.

VFIO type1 avoids this because it collects reserved regions through
iommu_get_group_resv_regions(), which already reserves PCI host
bridge MMIO windows.

Switch iommufd to iommu_get_group_resv_regions() as well so that
the IOAS reserved set includes PCI host bridge MMIO windows and
IOMMU_IOAS_IOVA_RANGES no longer hands out IOVAs overlapping them,
aligning iommufd behaviour with VFIO type1.  The per-device @dev is
kept as the reservation owner so that per-device detach cleanup via
__iopt_remove_reserved_iova() continues to work unchanged.

Signed-off-by: Guanghui Feng <guanghuifeng@linux.alibaba.com>
---
 drivers/iommu/iommufd/io_pagetable.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/iommufd/io_pagetable.c b/drivers/iommu/iommufd/io_pagetable.c
index 4e447ce74cf6..7198deba378e 100644
--- a/drivers/iommu/iommufd/io_pagetable.c
+++ b/drivers/iommu/iommufd/io_pagetable.c
@@ -1582,6 +1582,7 @@ int iopt_table_enforce_dev_resv_regions(struct io_pagetable *iopt,
 					phys_addr_t *sw_msi_start)
 {
 	struct iommu_resv_region *resv;
+	struct iommu_group *group;
 	LIST_HEAD(resv_regions);
 	unsigned int num_hw_msi = 0;
 	unsigned int num_sw_msi = 0;
@@ -1591,8 +1592,16 @@ int iopt_table_enforce_dev_resv_regions(struct io_pagetable *iopt,
 		return -EINVAL;
 
 	down_write(&iopt->iova_rwsem);
-	/* FIXME: drivers allocate memory but there is no failure propagated */
-	iommu_get_resv_regions(dev, &resv_regions);
+
+	group = iommu_group_get(dev);
+	if (!group) {
+		rc = -ENODEV;
+		goto out_unlock;
+	}
+
+	rc = iommu_get_group_resv_regions(group, &resv_regions);
+	if (rc)
+		goto out_free_resv;
 
 	list_for_each_entry(resv, &resv_regions, list) {
 		if (resv->type == IOMMU_RESV_DIRECT_RELAXABLE)
@@ -1624,6 +1633,8 @@ int iopt_table_enforce_dev_resv_regions(struct io_pagetable *iopt,
 	__iopt_remove_reserved_iova(iopt, dev);
 out_free_resv:
 	iommu_put_resv_regions(dev, &resv_regions);
+	iommu_group_put(group);
+out_unlock:
 	up_write(&iopt->iova_rwsem);
 	return rc;
 }
-- 
2.43.7


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA
  2026-09-21 10:39 ` [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA Guanghui Feng
  2026-09-21 10:39   ` [PATCH v2 1/2] iommu: Reserve PCI host bridge MMIO windows in group reserved regions Guanghui Feng
  2026-09-21 10:39   ` [PATCH v2 2/2] iommufd: Reserve PCI host bridge MMIO windows in IOAS " Guanghui Feng
@ 2026-09-21 11:24   ` Robin Murphy
  2026-09-21 11:55     ` Jason Gunthorpe
  2 siblings, 1 reply; 9+ messages in thread
From: Robin Murphy @ 2026-09-21 11:24 UTC (permalink / raw)
  To: Guanghui Feng, jgg, kevin.tian, alex, joro, will; +Cc: iommu, kvm, linux-kernel

On 21/09/2026 11:39 am, Guanghui Feng wrote:
> The DMA IOVA layer reserves PCI host bridge MMIO windows via
> iova_reserve_pci_windows() to keep IOVA allocations from overlapping
> those address ranges.  As commit fade1ec055dc ("iommu/dma: Avoid PCI
> host bridge windows") explains, a host bridge may interpret addresses
> falling within its MMIO windows as peer-to-peer DMA, leading to
> faults, data corruption, or DMA transactions being misrouted to the
> wrong PCIe device.  This is especially dangerous when a device sits
> behind a PCIe switch and ACS Upstream Forwarding is not fully enabled.
> 
> Passthrough paths that let userspace pick IOVAs did not honour these
> windows: VFIO type1 and iommufd only excluded IOMMU driver-level
> reserved regions (RMRR, unity maps, MSI), not PCI host bridge MMIO
> windows.

This was intentional - I forget where the exact discussion happened, but 
at the time we decided that exposing reserved regions for entire windows 
was the wrong thing to do, as being pessimistically inaccurate might be 
OK for the internal DMA API, but restricting userspace is another 
matter, and I think the consensus was that it wasn't really the IOMMU 
layer's job to get deep into PCI details anyway. Such "accidental" P2P 
can only happen within a group that's assigned to VFIO as a whole, so 
IIRC the conclusion was that users should already have enough 
information at the VFIO level to avoid BAR addresses if they need to.

In fact we did briefly do this once before, and it was explicitly undone 
again by cd2c9fcf5c66 ("iommu/dma: Move PCI window region reservation 
back into dma specific path."). I think there may also have been a 
practical issue with it breaking some standard Qemu setups.

Thanks,
Robin.

> 
> This series closes that gap:
> 
>    Patch 1 adds a common helper, iommu_get_pci_resv_windows(), and
>    reserves PCI host bridge MMIO windows in
>    iommu_get_group_resv_regions() so all group-level consumers (VFIO
>    type1, sysfs reserved_regions) avoid them; iova_reserve_pci_windows()
>    is refactored to reuse the helper.
> 
>    Patch 2 switches iommufd's iopt_table_enforce_dev_resv_regions() to
>    iommu_get_group_resv_regions() so IOMMU_IOAS_IOVA_RANGES also avoids
>    PCI host bridge MMIO windows, aligning iommufd with VFIO type1.
> 
> v1: https://lore.kernel.org/all/20260921070234.897736-1-guanghuifeng@linux.alibaba.com/
> 
> Changes since v1:
> - v1 was a single patch; v2 is a two-patch series.
> - Patch 1: build the reservations with iommu_insert_resv_region()
>    instead of list_add_tail(), so the group reserved list stays sorted
>    by start address and overlapping same-type regions are merged.  The
>    helper now frees its partial list itself on error and leaves the
>    caller's list untouched.
> - Patch 2 (new): reserve PCI host bridge MMIO windows for the iommufd
>    IOAS as well, so IOMMU_IOAS_IOVA_RANGES no longer reports IOVAs that
>    overlap PCI MMIO windows.
> 
> Notes on earlier automated review feedback:
> - The size_t length of each window matches struct iommu_resv_region's
>    existing field type; no new truncation is introduced on 64-bit.
> - An IOMMU group cannot span multiple PCI host bridges:
>    pci_device_group() only groups devices within a single host bridge
>    hierarchy, so scanning the first PCI device of the group is enough.
> 
> Guanghui Feng (2):
>    iommu: Reserve PCI host bridge MMIO windows in group reserved regions
>    iommufd: Reserve PCI host bridge MMIO windows in IOAS reserved regions
> 
>   drivers/iommu/dma-iommu.c            | 17 ++++--
>   drivers/iommu/iommu-priv.h           | 11 ++++
>   drivers/iommu/iommu.c                | 85 ++++++++++++++++++++++++++++
>   drivers/iommu/iommufd/io_pagetable.c | 15 ++++-
>   4 files changed, 121 insertions(+), 7 deletions(-)
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA
  2026-09-21 11:24   ` [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA Robin Murphy
@ 2026-09-21 11:55     ` Jason Gunthorpe
  2026-10-04 16:22       ` [PATCH v3 0/2] iommu/iommufd: Expose PCI host bridge MMIO windows for opt-in IOVA avoidance Guanghui Feng
  0 siblings, 1 reply; 9+ messages in thread
From: Jason Gunthorpe @ 2026-09-21 11:55 UTC (permalink / raw)
  To: Robin Murphy
  Cc: Guanghui Feng, kevin.tian, alex, joro, will, iommu, kvm, linux-kernel

On Mon, Sep 21, 2026 at 12:24:13PM +0100, Robin Murphy wrote:
> On 21/09/2026 11:39 am, Guanghui Feng wrote:
> > The DMA IOVA layer reserves PCI host bridge MMIO windows via
> > iova_reserve_pci_windows() to keep IOVA allocations from overlapping
> > those address ranges.  As commit fade1ec055dc ("iommu/dma: Avoid PCI
> > host bridge windows") explains, a host bridge may interpret addresses
> > falling within its MMIO windows as peer-to-peer DMA, leading to
> > faults, data corruption, or DMA transactions being misrouted to the
> > wrong PCIe device.  This is especially dangerous when a device sits
> > behind a PCIe switch and ACS Upstream Forwarding is not fully enabled.
> > 
> > Passthrough paths that let userspace pick IOVAs did not honour these
> > windows: VFIO type1 and iommufd only excluded IOMMU driver-level
> > reserved regions (RMRR, unity maps, MSI), not PCI host bridge MMIO
> > windows.
> 
> This was intentional - I forget where the exact discussion happened, but at
> the time we decided that exposing reserved regions for entire windows was
> the wrong thing to do, as being pessimistically inaccurate might be OK for
> the internal DMA API, but restricting userspace is another matter, and I
> think the consensus was that it wasn't really the IOMMU layer's job to get
> deep into PCI details anyway. Such "accidental" P2P can only happen within a
> group that's assigned to VFIO as a whole, so IIRC the conclusion was that
> users should already have enough information at the VFIO level to avoid BAR
> addresses if they need to.

Yes, but we never did a a very good job to make the information
visible to the VFIO userspace level, and I don't think anyone ever
implemented some sysfs parsing to try to get it.

It would be a nice improvement to expose these pessimistic ranges to
userspace through iommufd so it can choose to opt into them if it
wants. Some kind of helper that the dma-iommu.c and iommufd can call
perhaps.

Jason

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3 0/2] iommu/iommufd: Expose PCI host bridge MMIO windows for opt-in IOVA avoidance
  2026-09-21 11:55     ` Jason Gunthorpe
@ 2026-10-04 16:22       ` Guanghui Feng
  2026-10-04 16:22         ` [PATCH v3 1/2] iommu: Add iommu_get_pci_resv_windows() helper Guanghui Feng
  2026-10-04 16:22         ` [PATCH v3 2/2] iommufd: Add IOMMU_GET_PCI_MMIO_WINDOWS ioctl Guanghui Feng
  0 siblings, 2 replies; 9+ messages in thread
From: Guanghui Feng @ 2026-10-04 16:22 UTC (permalink / raw)
  To: jgg
  Cc: alex, guanghuifeng, iommu, joro, kevin.tian, kvm, linux-kernel,
	robin.murphy, will

When a device sits behind a PCIe switch and ACS Upstream Forwarding is
not fully enabled, DMA TLPs whose IOVA happens to fall within a host
bridge MMIO window may be routed peer-to-peer to another downstream
device instead of upstream to the root complex for IOMMU translation.
This can lead to faults, data corruption, or silent misrouting.

The DMA IOVA layer already avoids this via iova_reserve_pci_windows().
However, passthrough paths that let userspace pick IOVAs (VFIO type1,
iommufd) had no mechanism to learn about these ranges.  Commit
cd2c9fcf5c66 ("iommu/dma: Move PCI window region reservation back into
dma specific path.") intentionally keeps this information out of the
IOMMU reserved-region API to avoid pessimistically restricting
userspace.

Following Jason Gunthorpe's suggestion [1], this series takes an opt-in
approach: the kernel reports the information, userspace decides whether
to avoid it.

  Patch 1 adds iommu_get_pci_resv_windows(), a shared helper that
  enumerates a device's host bridge MMIO windows as sorted, merged
  IOMMU_RESV_RESERVED regions.

  Patch 2 adds the IOMMU_GET_PCI_MMIO_WINDOWS ioctl to iommufd, a
  per-device query that returns these windows to userspace without
  reserving or enforcing them.

[1] https://lore.kernel.org/all/20260921115513.GK11599@ziepe.ca/

v2: https://lore.kernel.org/all/20260921103922.1113752-1-guanghuifeng@linux.alibaba.com/
v1: https://lore.kernel.org/all/20260921070234.897736-1-guanghuifeng@linux.alibaba.com/

Changes since v2:
- Complete redesign per Robin Murphy's and Jason Gunthorpe's review.
  v2 forced PCI window reservation through iommu_get_group_resv_regions()
  which was explicitly rejected (this was tried before and reverted by
  cd2c9fcf5c66).  v3 instead provides an opt-in query interface.
- Patch 1: add iommu_get_pci_resv_windows() as a standalone helper.
  No longer touches dma-iommu.c (the existing direct enumeration in
  iova_reserve_pci_windows() is simpler and allocation-free; refactoring
  it to use the helper would add unnecessary overhead for no functional
  benefit).
- Patch 2: new IOMMU_GET_PCI_MMIO_WINDOWS ioctl replaces the v2
  iommufd enforce-path change.  Reports windows to userspace without
  reserving them.
- io_pagetable.c and dma-iommu.c are unchanged from base.

Changes since v1:
- (Superseded by v2->v3 changes above.)

Guanghui Feng (2):
  iommu: Add iommu_get_pci_resv_windows() helper
  iommufd: Add IOMMU_GET_PCI_MMIO_WINDOWS ioctl

 drivers/iommu/iommu-priv.h              | 12 +++++
 drivers/iommu/iommu.c                   | 57 ++++++++++++++++++++++
 drivers/iommu/iommufd/device.c          | 64 +++++++++++++++++++++++++
 drivers/iommu/iommufd/iommufd_private.h |  1 +
 drivers/iommu/iommufd/main.c            |  3 ++
 include/uapi/linux/iommufd.h            | 55 +++++++++++++++++++++
 6 files changed, 192 insertions(+)

-- 
2.43.7


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3 1/2] iommu: Add iommu_get_pci_resv_windows() helper
  2026-10-04 16:22       ` [PATCH v3 0/2] iommu/iommufd: Expose PCI host bridge MMIO windows for opt-in IOVA avoidance Guanghui Feng
@ 2026-10-04 16:22         ` Guanghui Feng
  2026-10-04 16:22         ` [PATCH v3 2/2] iommufd: Add IOMMU_GET_PCI_MMIO_WINDOWS ioctl Guanghui Feng
  1 sibling, 0 replies; 9+ messages in thread
From: Guanghui Feng @ 2026-10-04 16:22 UTC (permalink / raw)
  To: jgg
  Cc: alex, guanghuifeng, iommu, joro, kevin.tian, kvm, linux-kernel,
	robin.murphy, will

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@linux.alibaba.com>
---
 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(&region->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


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v3 2/2] iommufd: Add IOMMU_GET_PCI_MMIO_WINDOWS ioctl
  2026-10-04 16:22       ` [PATCH v3 0/2] iommu/iommufd: Expose PCI host bridge MMIO windows for opt-in IOVA avoidance Guanghui Feng
  2026-10-04 16:22         ` [PATCH v3 1/2] iommu: Add iommu_get_pci_resv_windows() helper Guanghui Feng
@ 2026-10-04 16:22         ` Guanghui Feng
  1 sibling, 0 replies; 9+ messages in thread
From: Guanghui Feng @ 2026-10-04 16:22 UTC (permalink / raw)
  To: jgg
  Cc: alex, guanghuifeng, iommu, joro, kevin.tian, kvm, linux-kernel,
	robin.murphy, will

Add a new per-device query ioctl that reports the PCI host bridge MMIO
windows behind a given device.  This allows userspace (e.g. QEMU) to
learn which address ranges a PCIe switch might misinterpret as
peer-to-peer DMA targets, and voluntarily keep its IOVA allocations away
from them.

The kernel does NOT reserve or enforce these ranges -- it only reports
them.  Userspace can choose to opt into avoiding these windows based on
its own topology knowledge and requirements.  This follows the principle
established by commit cd2c9fcf5c66 ("iommu/dma: Move PCI window region reservation back into dma specific path.")
that PCI window information should not be forced through the IOMMU
reserved-region API onto userspace.

The ioctl uses the standard "user provides array + count, kernel fills
and returns actual count, -EMSGSIZE if too small" pattern established by
IOMMU_IOAS_IOVA_RANGES.  Per-device granularity is chosen because PCI
host bridge windows are a property of the device's bus topology and are
available immediately after VFIO_DEVICE_BIND_IOMMUFD, before any IOAS
attachment.

Signed-off-by: Guanghui Feng <guanghuifeng@linux.alibaba.com>
---
 drivers/iommu/iommufd/device.c          | 64 +++++++++++++++++++++++++
 drivers/iommu/iommufd/iommufd_private.h |  1 +
 drivers/iommu/iommufd/main.c            |  3 ++
 include/uapi/linux/iommufd.h            | 55 +++++++++++++++++++++
 4 files changed, 123 insertions(+)

diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
index a664c70a6fe7..74a0642ff4fb 100644
--- a/drivers/iommu/iommufd/device.c
+++ b/drivers/iommu/iommufd/device.c
@@ -1747,3 +1747,67 @@ int iommufd_get_hw_info(struct iommufd_ucmd *ucmd)
 	iommufd_put_object(ucmd->ictx, &idev->obj);
 	return rc;
 }
+
+int iommufd_device_get_pci_mmio_windows(struct iommufd_ucmd *ucmd)
+{
+	struct iommu_pci_mmio_window __user *windows;
+	struct iommu_pci_mmio_windows *cmd = ucmd->cmd;
+	struct iommu_resv_region *resv, *next;
+	struct iommufd_device *idev;
+	LIST_HEAD(resv_windows);
+	u32 max_windows;
+	int rc;
+
+	if (cmd->flags)
+		return -EOPNOTSUPP;
+
+	idev = iommufd_get_device(ucmd, cmd->dev_id);
+	if (IS_ERR(idev))
+		return PTR_ERR(idev);
+
+	if (!dev_is_pci(idev->dev)) {
+		rc = -EOPNOTSUPP;
+		goto out_put;
+	}
+
+	rc = iommu_get_pci_resv_windows(to_pci_dev(idev->dev), &resv_windows);
+	if (rc)
+		goto out_put;
+
+	max_windows = cmd->num_windows;
+	windows = u64_to_user_ptr(cmd->windows);
+	cmd->num_windows = 0;
+	list_for_each_entry(resv, &resv_windows, list) {
+		if (!resv->length)
+			continue;
+
+		if (cmd->num_windows < max_windows) {
+			struct iommu_pci_mmio_window elm = {
+				.start = resv->start,
+				.last = resv->start + resv->length - 1,
+			};
+
+			if (copy_to_user(&windows[cmd->num_windows], &elm,
+					 sizeof(elm))) {
+				rc = -EFAULT;
+				goto out_free;
+			}
+		}
+		cmd->num_windows++;
+	}
+
+	rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
+	if (rc)
+		goto out_free;
+	if (cmd->num_windows > max_windows)
+		rc = -EMSGSIZE;
+
+out_free:
+	list_for_each_entry_safe(resv, next, &resv_windows, list) {
+		list_del(&resv->list);
+		kfree(resv);
+	}
+out_put:
+	iommufd_put_object(ucmd->ictx, &idev->obj);
+	return rc;
+}
diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index eb2e85b27e42..557f205e7836 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -535,6 +535,7 @@ iommufd_device_get_iommu_dev(struct iommufd_device *idev)
 void iommufd_device_pre_destroy(struct iommufd_object *obj);
 void iommufd_device_destroy(struct iommufd_object *obj);
 int iommufd_get_hw_info(struct iommufd_ucmd *ucmd);
+int iommufd_device_get_pci_mmio_windows(struct iommufd_ucmd *ucmd);
 
 struct device *iommufd_global_device(void);
 
diff --git a/drivers/iommu/iommufd/main.c b/drivers/iommu/iommufd/main.c
index 9a921b153162..0d1be82c1ddd 100644
--- a/drivers/iommu/iommufd/main.c
+++ b/drivers/iommu/iommufd/main.c
@@ -449,6 +449,7 @@ union ucmd_buffer {
 	struct iommu_ioas_map map;
 	struct iommu_ioas_unmap unmap;
 	struct iommu_option option;
+	struct iommu_pci_mmio_windows pci_mmio_windows;
 	struct iommu_vdevice_alloc vdev;
 	struct iommu_veventq_alloc veventq;
 	struct iommu_vfio_ioas vfio_ioas;
@@ -480,6 +481,8 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = {
 		 struct iommu_fault_alloc, out_fault_fd),
 	IOCTL_OP(IOMMU_GET_HW_INFO, iommufd_get_hw_info, struct iommu_hw_info,
 		 __reserved),
+	IOCTL_OP(IOMMU_GET_PCI_MMIO_WINDOWS, iommufd_device_get_pci_mmio_windows,
+		 struct iommu_pci_mmio_windows, windows),
 	IOCTL_OP(IOMMU_HW_QUEUE_ALLOC, iommufd_hw_queue_alloc_ioctl,
 		 struct iommu_hw_queue_alloc, length),
 	IOCTL_OP(IOMMU_HWPT_ALLOC, iommufd_hwpt_alloc, struct iommu_hwpt_alloc,
diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h
index 206fa667c782..5707eb6130ee 100644
--- a/include/uapi/linux/iommufd.h
+++ b/include/uapi/linux/iommufd.h
@@ -58,6 +58,7 @@ enum {
 	IOMMUFD_CMD_VEVENTQ_ALLOC = 0x93,
 	IOMMUFD_CMD_HW_QUEUE_ALLOC = 0x94,
 	IOMMUFD_CMD_IOAS_NOIOMMU_GET_PA = 0x95,
+	IOMMUFD_CMD_GET_PCI_MMIO_WINDOWS = 0x96,
 };
 
 /**
@@ -1390,4 +1391,58 @@ struct iommu_hw_queue_alloc {
 	__aligned_u64 length;
 };
 #define IOMMU_HW_QUEUE_ALLOC _IO(IOMMUFD_TYPE, IOMMUFD_CMD_HW_QUEUE_ALLOC)
+
+/**
+ * struct iommu_pci_mmio_window - a PCI host bridge MMIO window
+ * @start: First PCI bus address of the window
+ * @last: Inclusive last PCI bus address of the window
+ *
+ * A memory window claimed by the PCI host bridge that a device sits
+ * behind.  Addresses are PCI bus addresses (i.e. what appears in the
+ * TLP), which differ from CPU physical addresses on platforms where the
+ * host bridge applies an address translation offset.  For a device
+ * using an identity (1:1) IOVA layout the IOVA equals the bus address,
+ * so these are the IOVAs that a PCIe switch could misinterpret as
+ * peer-to-peer DMA and route to the wrong device instead of memory.
+ */
+struct iommu_pci_mmio_window {
+	__aligned_u64 start;
+	__aligned_u64 last;
+};
+
+/**
+ * struct iommu_pci_mmio_windows - ioctl(IOMMU_GET_PCI_MMIO_WINDOWS)
+ * @size: sizeof(struct iommu_pci_mmio_windows)
+ * @flags: Must be 0
+ * @dev_id: The device bound to iommufd to query
+ * @num_windows: Input/output number of MMIO windows
+ * @windows: Pointer to the output array of struct iommu_pci_mmio_window
+ *
+ * Query the memory windows of the PCI host bridge that @dev_id sits
+ * behind.  This is advisory: the kernel does not reserve these ranges, it
+ * only reports them so userspace can choose to keep its IOVA allocations
+ * away from them and avoid accidental peer-to-peer DMA routing.  The
+ * reported windows are pessimistic (the whole host bridge window, not the
+ * precise BAR ranges that may actually conflict).  If a IOAS has devices
+ * behind multiple host bridges, query each device and take the union.
+ *
+ * On input num_windows is the length of the windows array.  On output it
+ * is the total number of windows.  The ioctl will return -EMSGSIZE and set
+ * num_windows to the required value if num_windows is too small.  In this
+ * case the caller should allocate a larger output array and re-issue the
+ * ioctl.
+ *
+ * Return: 0 on success, -EOPNOTSUPP if @dev_id is not a PCI device,
+ * -ENOENT if @dev_id is invalid, -EMSGSIZE if the output array is too
+ * small.
+ */
+struct iommu_pci_mmio_windows {
+	__u32 size;
+	__u32 flags;
+	__u32 dev_id;
+	__u32 num_windows;
+	__aligned_u64 windows;
+};
+#define IOMMU_GET_PCI_MMIO_WINDOWS \
+	_IO(IOMMUFD_TYPE, IOMMUFD_CMD_GET_PCI_MMIO_WINDOWS)
 #endif
-- 
2.43.7


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-10-04 16:28 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-21  7:02 [PATCH] iommu: Reserve PCI host bridge MMIO windows in group reserved regions Guanghui Feng
2026-09-21 10:39 ` [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA Guanghui Feng
2026-09-21 10:39   ` [PATCH v2 1/2] iommu: Reserve PCI host bridge MMIO windows in group reserved regions Guanghui Feng
2026-09-21 10:39   ` [PATCH v2 2/2] iommufd: Reserve PCI host bridge MMIO windows in IOAS " Guanghui Feng
2026-09-21 11:24   ` [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA Robin Murphy
2026-09-21 11:55     ` Jason Gunthorpe
2026-10-04 16:22       ` [PATCH v3 0/2] iommu/iommufd: Expose PCI host bridge MMIO windows for opt-in IOVA avoidance Guanghui Feng
2026-10-04 16:22         ` [PATCH v3 1/2] iommu: Add iommu_get_pci_resv_windows() helper Guanghui Feng
2026-10-04 16:22         ` [PATCH v3 2/2] iommufd: Add IOMMU_GET_PCI_MMIO_WINDOWS ioctl Guanghui Feng

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®