*/
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
#include "hw/arm/smmuv3.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci-host/gpex.h"
+#include "hw/vfio/pci.h"
+
#include "smmuv3-accel.h"
/*
return accel_dev;
}
+/*
+ * Only allow PCIe bridges, pxb-pcie roots, and GPEX roots so vfio-pci
+ * endpoints can sit downstream. Accelerated SMMUv3 requires a vfio-pci
+ * endpoint using the iommufd backend; all other device types are rejected.
+ * This avoids supporting emulated endpoints, which would complicate IOTLB
+ * invalidation and hurt performance.
+ */
+static bool smmuv3_accel_pdev_allowed(PCIDevice *pdev, bool *vfio_pci)
+{
+
+ if (object_dynamic_cast(OBJECT(pdev), TYPE_PCI_BRIDGE) ||
+ object_dynamic_cast(OBJECT(pdev), TYPE_PXB_PCIE_DEV) ||
+ object_dynamic_cast(OBJECT(pdev), TYPE_GPEX_ROOT_DEVICE)) {
+ return true;
+ } else if ((object_dynamic_cast(OBJECT(pdev), TYPE_VFIO_PCI))) {
+ *vfio_pci = true;
+ if (object_property_get_link(OBJECT(pdev), "iommufd", NULL)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool smmuv3_accel_supports_as(PCIBus *bus, void *opaque, int devfn,
+ Error **errp)
+{
+ PCIDevice *pdev = pci_find_device(bus, pci_bus_num(bus), devfn);
+ bool vfio_pci = false;
+
+ if (pdev && !smmuv3_accel_pdev_allowed(pdev, &vfio_pci)) {
+ if (vfio_pci) {
+ error_setg(errp, "vfio-pci endpoint devices without an iommufd "
+ "backend not allowed when using arm-smmuv3,accel=on");
+
+ } else {
+ error_setg(errp, "Emulated endpoint devices are not allowed when "
+ "using arm-smmuv3,accel=on");
+ }
+ return false;
+ }
+ return true;
+}
/*
* Find or add an address space for the given PCI device.
*
static AddressSpace *smmuv3_accel_find_add_as(PCIBus *bus, void *opaque,
int devfn)
{
+ PCIDevice *pdev = pci_find_device(bus, pci_bus_num(bus), devfn);
SMMUState *bs = opaque;
SMMUPciBus *sbus = smmu_get_sbus(bs, bus);
SMMUv3AccelDevice *accel_dev = smmuv3_accel_get_dev(bs, sbus, bus, devfn);
SMMUDevice *sdev = &accel_dev->sdev;
+ bool vfio_pci = false;
- return &sdev->as;
+ if (pdev && !smmuv3_accel_pdev_allowed(pdev, &vfio_pci)) {
+ /* Should never be here: supports_address_space() filters these out */
+ g_assert_not_reached();
+ }
+
+ /*
+ * In the accelerated mode, a vfio-pci device attached via the iommufd
+ * backend must remain in the system address space. Such a device is
+ * always translated by its physical SMMU (using either a stage-2-only
+ * STE or a nested STE), where the parent stage-2 page table is allocated
+ * by the VFIO core to back the system address space.
+ *
+ * Return the shared_as_sysmem aliased to the global system memory in this
+ * case. Sharing address_space_memory also allows devices under different
+ * vSMMU instances in the same VM to reuse a single nesting parent HWPT in
+ * the VFIO core.
+ *
+ * For non-endpoint emulated devices such as PCIe root ports and bridges,
+ * which may use the normal emulated translation path and software IOTLBs,
+ * return the SMMU's IOMMU address space.
+ */
+ if (vfio_pci) {
+ return shared_as_sysmem;
+ } else {
+ return &sdev->as;
+ }
}
static const PCIIOMMUOps smmuv3_accel_ops = {
+ .supports_address_space = smmuv3_accel_supports_as,
.get_address_space = smmuv3_accel_find_add_as,
};