Introduce an optional supports_address_space() callback in PCIIOMMUOps to allow a vIOMMU implementation to reject devices that should not be attached to it.
Currently, get_address_space() is the first and mandatory callback into the vIOMMU layer, which always returns an address space. For certain setups, such as hardware accelerated vIOMMUs (e.g. ARM SMMUv3 with accel=on), attaching emulated endpoint devices is undesirable as it may impact the behavior or performance of VFIO passthrough devices, for example, by triggering unnecessary invalidations on the host IOMMU. The new callback allows a vIOMMU to check and reject unsupported devices early during PCI device registration. Cc: Michael S. Tsirkin <[email protected]> Reviewed-by: Jonathan Cameron <[email protected]> Reviewed-by: Eric Auger <[email protected]> Reviewed-by: Nicolin Chen <[email protected]> Signed-off-by: Shameer Kolothum <[email protected]> --- hw/pci/pci.c | 20 ++++++++++++++++++++ include/hw/pci/pci.h | 19 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index a852694cc9..95b29a690e 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -135,6 +135,21 @@ static void pci_set_master(PCIDevice *d, bool enable) d->is_master = enable; /* cache the status */ } +static bool +pci_device_supports_iommu_address_space(PCIDevice *dev, Error **errp) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + pci_device_get_iommu_bus_devfn(dev, &iommu_bus, &bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->supports_address_space) { + return iommu_bus->iommu_ops->supports_address_space(bus, + iommu_bus->iommu_opaque, devfn, errp); + } + return true; +} + static void pci_init_bus_master(PCIDevice *pci_dev) { AddressSpace *dma_as = pci_device_iommu_address_space(pci_dev); @@ -1424,6 +1439,11 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, pci_dev->config_write = config_write; bus->devices[devfn] = pci_dev; pci_dev->version_id = 2; /* Current pci device vmstate version */ + if (!pci_device_supports_iommu_address_space(pci_dev, errp)) { + do_pci_unregister_device(pci_dev); + bus->devices[devfn] = NULL; + return NULL; + } if (phase_check(PHASE_MACHINE_READY)) { pci_init_bus_master(pci_dev); } diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 868817cc05..efe9547185 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -417,6 +417,25 @@ typedef struct IOMMUPRINotifier { * framework for a set of devices on a PCI bus. */ typedef struct PCIIOMMUOps { + /** + * @supports_address_space: Optional pre-check to determine whether a PCI + * device can be associated with an IOMMU. If this callback returns true, + * the IOMMU accepts the device association and get_address_space() can be + * called to obtain the address_space to be used. + * + * @bus: the #PCIBus being accessed. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number. + * + * @errp: pass an Error out only when return false + * + * Returns: true if the device can be associated with an IOMMU, false + * otherwise with errp set. + */ + bool (*supports_address_space)(PCIBus *bus, void *opaque, int devfn, + Error **errp); /** * @get_address_space: get the address space for a set of devices * on a PCI bus. -- 2.43.0
