[PATCH v2 2/2] iommufd: Reserve PCI host bridge MMIO windows in IOAS reserved regions

From: Guanghui Feng

Date: Mon Sep 21 2026 - 06:43:00 EST


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@xxxxxxxxxxxxxxxxx>
---
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