From 55da034e69706b278472f53037c3c1f256f880a6 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Fri, 13 Nov 2026 14:13:23 -0800 Subject: [PATCH 1/2] iommu/xen: keep reserving the PCI host-bridge windows in dom0 Commit 1b8277179ffe ("iommu/dma: Don't reserve PCI windows under a paravirtual IOMMU") skips iova_reserve_pci_windows() for every device the Xen IOMMU driver owns, because the windows a guest sees are invented: pcifront hands out iomem_resource itself and a PVH guest with no host-bridge _CRS gets Linux's catch-all default. That holds for a guest. Dom0 sees the real host bridges, and their windows route to the real BARs of the devices behind them. A PVH dom0 device gets a translated DMA domain, and dma-iommu hands out IOVAs top-down below 4 GiB. The driver reserves only the MSI range, so once the space above it is used, IOVAs land inside the host-bridge window where sibling devices' BARs live. On a root port or switch without ACS a DMA to such an address is decoded as peer-to-peer and delivered to the sibling's BAR: it never reaches the IOMMU, so the transfer is lost and the sibling's registers are written. Xen enables ACS where it can, which redirects such requests upstream, but that is defence in depth, not a reason to skip the reservation. Have xen_iommu_manages_iova() answer false in the initial domain, so dom0 reserves its windows as on bare metal. Signed-off-by: Ariadne Conill --- drivers/iommu/dma-iommu.c | 4 +++- drivers/iommu/xen-iommu.c | 10 +++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 0be617b7854dfa..660828a5327029 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -569,7 +569,9 @@ static int iova_reserve_iommu_regions(struct device *dev, * are invented -- pcifront hands out iomem_resource itself, and a PVH * guest with no host bridge _CRS gets Linux's catch-all default. There * is nothing meaningful to reserve, and reserving it leaves no usable - * IOVA space whatsoever. + * IOVA space whatsoever. Dom0 sees the real host bridges and keeps the + * reservation: without it an IOVA can land on a sibling's BAR, and a + * fabric without ACS delivers the DMA there instead of to the IOMMU. */ if (dev_is_pci(dev) && !xen_iommu_manages_iova(dev)) { ret = iova_reserve_pci_windows(to_pci_dev(dev), iovad); diff --git a/drivers/iommu/xen-iommu.c b/drivers/iommu/xen-iommu.c index 986cf874319eb8..ccebf8508693d5 100644 --- a/drivers/iommu/xen-iommu.c +++ b/drivers/iommu/xen-iommu.c @@ -477,9 +477,17 @@ static struct iommu_ops xen_iommu_ops = { }, }; +/* + * True when the host-bridge windows @dev sees are not the machine's and + * dma-iommu must not reserve them: a guest's IOVA space belongs to the + * hypervisor, and the windows it sees are invented. Dom0 sees the real + * host bridges, whose windows route to real BARs, so there the reservation + * stands as on bare metal. + */ bool xen_iommu_manages_iova(struct device *dev) { - return dev->iommu && dev_iommu_ops(dev) == &xen_iommu_ops; + return !xen_initial_domain() && dev->iommu && + dev_iommu_ops(dev) == &xen_iommu_ops; } static int __init xen_iommu_init(void) From 9e5d4e3c3ddfb5e5f0cbe37ff1633612a991028d Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Fri, 13 Nov 2026 14:13:23 -0800 Subject: [PATCH 2/2] xen/pcifront: do not promise pcifront_machine_sbdf() to built-in code when modular include/xen/pci.h declares pcifront_machine_sbdf() as a real function whenever CONFIG_XEN_PCIDEV_FRONTEND is defined, which it is for =m as well as =y. XEN_IOMMU is a bool and calls it from built-in code, so XEN_IOMMU=y with XEN_PCIDEV_FRONTEND=m fails to link vmlinux. Declare the function only when pcifront is built in and give built-in callers the -ENODEV stub otherwise. The IOMMU driver with that stub would silently use a pcifront device's guest BDF, which means nothing to Xen, so also keep XEN_IOMMU from being configured with a modular pcifront rather than let it build into a driver that cannot name the devices it manages. Signed-off-by: Ariadne Conill --- drivers/iommu/Kconfig | 2 ++ include/xen/pci.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 97fba540753ddf..16cc557617059f 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -366,6 +366,8 @@ config VIRTIO_IOMMU config XEN_IOMMU bool "Xen IOMMU driver" depends on X86 && XEN + # Built-in, and asks pcifront for a device's machine BDF. + depends on XEN_PCIDEV_FRONTEND != m select IOMMU_API select IOMMU_IO_PGTABLE help diff --git a/include/xen/pci.h b/include/xen/pci.h index b5efff694a03fe..c6a215b7ba5526 100644 --- a/include/xen/pci.h +++ b/include/xen/pci.h @@ -8,7 +8,8 @@ struct pci_dev; -#if defined(CONFIG_XEN_PCIDEV_FRONTEND) +/* Called from built-in code, so a modular pcifront is as good as none. */ +#if IS_BUILTIN(CONFIG_XEN_PCIDEV_FRONTEND) int pcifront_machine_sbdf(const struct pci_dev *dev, u32 *sbdf); #else static inline int pcifront_machine_sbdf(const struct pci_dev *dev, u32 *sbdf)