Commit 98780613f8 for qemu.org
commit 98780613f8be3f45694d12b6a7c70be83f231d82
Author: Sairaj Kodilkar <sarunkod@amd.com>
Date: Wed Oct 14 11:04:33 2026 +0530
amd_iommu: Allow IOMMU to attachment on non-system (pcie.0) bus
Allow the AMD IOMMU to attach on the pcie-root-port using pci device's "bus"
property. This helps in isolating the set of devices by creating a pcie
hierarchy under the pci expansion bus (pxb-pci). The IOMMU always tries
to find the first root bus by traversing up the PCI hierarchy and serves
all the devices under that bus.
Example1, for below PCI configuration, IOMMU will serve all the devices
under pxb-pci (i.e. Device1 and Device2) since its a first root bus,
but will not serve Device3 and Device3, effectively isolating the
devices under pxb-pci bus.
pcie.0 -+- pxb-pci -+- pcie-root-port1 --- AMD IOMMU
| +- pcie-root-port2 --- Device1
| \- pcie-root-port3 --- Device2
+- Device3
\- Device4
Example2, for below PCI configuration, IOMMU will serve all the devices
Device[1-4], since first root bus in the parent hierarchy is pcie.0
pcie.0 -+- pcie-root-port1 --- AMD IOMMU
+- pcie-root-port2 --- Device1
+- pcie-root-port3 --- Device2
+- Device3
\- Device4
AMD IOMMU uses this root bus to register pci IOMMU ops and build the IVRS
table.
Signed-off-by: Sairaj Kodilkar <sarunkod@amd.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20261014053433.24564-9-sarunkod@amd.com>
diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c
index 14ea8274b2..2d5c7b5cdf 100644
--- a/hw/i386/amd_iommu.c
+++ b/hw/i386/amd_iommu.c
@@ -24,6 +24,7 @@
#include "hw/i386/pc.h"
#include "hw/pci/msi.h"
#include "hw/pci/pci_bus.h"
+#include "hw/pci/pci_bridge.h"
#include "migration/vmstate.h"
#include "amd_iommu.h"
#include "qapi/error.h"
@@ -2680,6 +2681,50 @@ static const VMStateDescription vmstate_amdvi_sysbus_migratable = {
}
};
+/* Go up in the PCI hierarchy and find the root bus pcie.0 or pxb-pci */
+static PCIBus *get_root_bus(struct PCIBus *bus)
+{
+ while (bus) {
+
+ if (!pci_bus_is_express(bus)) {
+ error_report("attached to non PCI express bus %s",
+ bus->qbus.name);
+ goto err_out;
+ }
+
+ if (bus->iommu_ops) {
+ error_report("different IOMMU already serves bus %s",
+ bus->qbus.name);
+ goto err_out;
+ }
+
+ if (pci_bus_is_root(bus) &&
+ object_dynamic_cast(OBJECT(bus)->parent, TYPE_PCI_HOST_BRIDGE)) {
+
+ if (bus->parent_dev &&
+ !object_dynamic_cast(OBJECT(bus), TYPE_PXB_PCIE_BUS)) {
+
+ error_report("only supports PXB-PCI as extra root bus");
+ goto err_out;
+ }
+ /* We found the valid root bus so return it to the user*/
+ goto out;
+ }
+
+ if (bus->parent_dev) {
+ bus = pci_get_bus(bus->parent_dev);
+ } else {
+ error_report("could not find valid PCIE bus");
+ goto err_out;
+ }
+ };
+
+err_out:
+ exit(EXIT_FAILURE);
+out:
+ return bus;
+}
+
static void amdvi_sysbus_realize(DeviceState *dev, Error **errp)
{
DeviceClass *dc = (DeviceClass *) object_get_class(OBJECT(dev));
@@ -2687,7 +2732,7 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp)
MachineState *ms = MACHINE(qdev_get_machine());
PCMachineState *pcms = PC_MACHINE(ms);
X86MachineState *x86ms = X86_MACHINE(ms);
- PCIBus *bus = pcms->pcibus;
+ PCIBus *iommu_bus;
if (s->pci_id) {
PCIDevice *pdev = NULL;
@@ -2703,17 +2748,19 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp)
return;
}
+ iommu_bus = pci_get_bus(pdev);
s->pci = AMD_IOMMU_PCI(pdev);
dc->vmsd = &vmstate_amdvi_sysbus_migratable;
} else {
+ iommu_bus = pcms->pcibus;
s->pci = AMD_IOMMU_PCI(object_new(TYPE_AMD_IOMMU_PCI));
/* This device should take care of IOMMU PCI properties */
- if (!qdev_realize(DEVICE(s->pci), &bus->qbus, errp)) {
+ if (!qdev_realize(DEVICE(s->pci), &iommu_bus->qbus, errp)) {
return;
}
}
- s->root_bus = bus;
+ s->root_bus = get_root_bus(iommu_bus);
s->iotlb = g_hash_table_new_full(amdvi_iotlb_hash,
amdvi_iotlb_equal, g_free, g_free);
@@ -2767,7 +2814,7 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp)
}
}
- pci_setup_iommu(bus, &amdvi_iommu_ops, s);
+ pci_setup_iommu(s->root_bus, &amdvi_iommu_ops, s);
amdvi_init(s);
}