Add support for selecting and initializing a CMDQV backend based on the cmdqv OnOffAuto property.
If set to OFF, CMDQV is not used and the default IOMMUFD-backed allocation path is taken. If set to AUTO, QEMU attempts to probe a CMDQV backend during device setup. If probing succeeds, the selected ops are stored in the accelerated SMMUv3 state and used. If probing fails, QEMU silently falls back to the default path. If set to ON, QEMU requires CMDQV support. Probing is performed during setup and failure results in an error. When a CMDQV backend is active, its callbacks are used for vIOMMU allocation, free, and reset handling. Otherwise, the base implementation is used. The current implementation wires up the Tegra241 CMDQV backend through the generic ops interface. Functional CMDQV behaviour is added in subsequent patches. No functional change. Reviewed-by: Eric Auger <[email protected]> Reviewed-by: Nicolin Chen <[email protected]> Signed-off-by: Shameer Kolothum <[email protected]> --- include/hw/arm/smmuv3.h | 2 + hw/arm/smmuv3-accel.c | 94 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 89 insertions(+), 7 deletions(-) diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h index 85be3d7467..34d0f65eaa 100644 --- a/include/hw/arm/smmuv3.h +++ b/include/hw/arm/smmuv3.h @@ -75,6 +75,8 @@ struct SMMUv3State { OnOffAuto ats; OasMode oas; SsidSizeMode ssidsize; + /* SMMU CMDQV extension */ + OnOffAuto cmdqv; Notifier machine_done; }; diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index 70b2581c17..3ceca56a67 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -19,6 +19,7 @@ #include "smmuv3-internal.h" #include "smmuv3-accel.h" #include "system/system.h" +#include "tegra241-cmdqv.h" /* * The root region aliases the global system memory, and shared_as_sysmem @@ -570,6 +571,7 @@ smmuv3_accel_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *hiodi, Error **errp) { SMMUv3AccelState *accel = s->s_accel; + const SMMUv3AccelCmdqvOps *cmdqv_ops = accel->cmdqv_ops; struct iommu_hwpt_arm_smmuv3 bypass_data = { .ste = { SMMU_STE_CFG_BYPASS | SMMU_STE_VALID, 0x0ULL }, }; @@ -580,10 +582,17 @@ smmuv3_accel_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *hiodi, uint32_t viommu_id, hwpt_id; IOMMUFDViommu *viommu; - if (!iommufd_backend_alloc_viommu(hiodi->iommufd, hiodi->devid, - IOMMU_VIOMMU_TYPE_ARM_SMMUV3, s2_hwpt_id, - NULL, 0, &viommu_id, errp)) { - return false; + if (cmdqv_ops) { + if (!cmdqv_ops->alloc_viommu(s, hiodi, &viommu_id, errp)) { + return false; + } + } else { + if (!iommufd_backend_alloc_viommu(hiodi->iommufd, hiodi->devid, + IOMMU_VIOMMU_TYPE_ARM_SMMUV3, + s2_hwpt_id, NULL, 0, &viommu_id, + errp)) { + return false; + } } viommu = g_new0(IOMMUFDViommu, 1); @@ -629,12 +638,70 @@ free_bypass_hwpt: free_abort_hwpt: iommufd_backend_free_id(hiodi->iommufd, accel->abort_hwpt_id); free_viommu: - iommufd_backend_free_id(hiodi->iommufd, viommu->viommu_id); + if (cmdqv_ops && cmdqv_ops->free_viommu) { + cmdqv_ops->free_viommu(s); + } else { + iommufd_backend_free_id(hiodi->iommufd, viommu->viommu_id); + } g_free(viommu); accel->viommu = NULL; return false; } +static const SMMUv3AccelCmdqvOps * +smmuv3_accel_probe_cmdqv(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, + Error **errp) +{ + const SMMUv3AccelCmdqvOps *ops = tegra241_cmdqv_get_ops(); + + if (!ops) { + error_setg(errp, "No CMDQV ops found"); + return NULL; + } + g_assert(ops->probe); + g_assert(ops->alloc_viommu); + + if (!ops->probe(s, idev, errp)) { + return NULL; + } + return ops; +} + +static bool +smmuv3_accel_select_cmdqv(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev, + Error **errp) +{ + const SMMUv3AccelCmdqvOps *ops = NULL; + + if (s->s_accel->cmdqv_ops) { + return true; + } + + switch (s->cmdqv) { + case ON_OFF_AUTO_OFF: + s->s_accel->cmdqv_ops = NULL; + return true; + case ON_OFF_AUTO_AUTO: + ops = smmuv3_accel_probe_cmdqv(s, idev, NULL); + break; + case ON_OFF_AUTO_ON: + ops = smmuv3_accel_probe_cmdqv(s, idev, errp); + if (!ops) { + error_append_hint(errp, "CMDQV requested but not supported"); + return false; + } + break; + default: + g_assert_not_reached(); + } + + if (ops && ops->init && !ops->init(s, errp)) { + return false; + } + s->s_accel->cmdqv_ops = ops; + return true; +} + static bool smmuv3_accel_set_iommu_device(PCIBus *bus, void *opaque, int devfn, HostIOMMUDevice *hiod, Error **errp) { @@ -669,6 +736,10 @@ static bool smmuv3_accel_set_iommu_device(PCIBus *bus, void *opaque, int devfn, goto done; } + if (!smmuv3_accel_select_cmdqv(s, hiodi, errp)) { + return false; + } + if (!smmuv3_accel_alloc_viommu(s, hiodi, errp)) { error_append_hint(errp, "Unable to alloc vIOMMU: hiodi devid 0x%x: ", hiodi->devid); @@ -946,8 +1017,17 @@ bool smmuv3_accel_attach_gbpa_hwpt(SMMUv3State *s, Error **errp) void smmuv3_accel_reset(SMMUv3State *s) { - /* Attach a HWPT based on GBPA reset value */ - smmuv3_accel_attach_gbpa_hwpt(s, NULL); + SMMUv3AccelState *accel = s->s_accel; + + if (!accel) { + return; + } + /* Attach a HWPT based on GBPA reset value */ + smmuv3_accel_attach_gbpa_hwpt(s, NULL); + + if (accel->cmdqv_ops && accel->cmdqv_ops->reset) { + accel->cmdqv_ops->reset(s); + } } static void smmuv3_accel_as_init(SMMUv3State *s) -- 2.43.0
