QEMU SMMUv3 currently reports no SubstreamID support, forcing SSID to zero. This prevents accelerated use cases such as Shared Virtual Addressing (SVA), which require multiple Stage-1 context descriptors indexed by SubstreamID.
Add a new "ssidsize" property to explicitly configure the number of bits used for SubstreamIDs. A value greater than zero enables SubstreamID support and advertises PASID capability to the vIOMMU. The requested SSIDSIZE is validated against host SMMUv3 capabilities and is only supported when accel=on. 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/arm/smmuv3-accel.c | 25 ++++++++++++++++++++++++- hw/arm/smmuv3-internal.h | 2 ++ hw/arm/smmuv3.c | 22 ++++++++++++++++++++-- include/hw/arm/smmuv3.h | 1 + 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c index 342944da23..f5cd4df336 100644 --- a/hw/arm/smmuv3-accel.c +++ b/hw/arm/smmuv3-accel.c @@ -76,6 +76,16 @@ smmuv3_accel_check_hw_compatible(SMMUv3State *s, return false; } + /* Check SSIDSIZE value opted-in is compatible with Host SMMUv3 SSIDSIZE */ + if (FIELD_EX32(info->idr[1], IDR1, SSIDSIZE) < + FIELD_EX32(s->idr[1], IDR1, SSIDSIZE)) { + error_setg(errp, "Host SMMUv3 SSIDSIZE not compatible " + "(host=%u, QEMU=%u)", + FIELD_EX32(info->idr[1], IDR1, SSIDSIZE), + FIELD_EX32(s->idr[1], IDR1, SSIDSIZE)); + return false; + } + /* User can disable QEMU SMMUv3 Range Invalidation support */ if (FIELD_EX32(info->idr[3], IDR3, RIL) < FIELD_EX32(s->idr[3], IDR3, RIL)) { @@ -652,7 +662,14 @@ static uint64_t smmuv3_accel_get_viommu_flags(void *opaque) * The real HW nested support should be reported from host SMMUv3 and if * it doesn't, the nesting parent allocation will fail anyway in VFIO core. */ - return VIOMMU_FLAG_WANT_NESTING_PARENT; + uint64_t flags = VIOMMU_FLAG_WANT_NESTING_PARENT; + SMMUState *bs = opaque; + SMMUv3State *s = ARM_SMMUV3(bs); + + if (s->ssidsize) { + flags |= VIOMMU_FLAG_PASID_SUPPORTED; + } + return flags; } static const PCIIOMMUOps smmuv3_accel_ops = { @@ -680,6 +697,12 @@ void smmuv3_accel_idr_override(SMMUv3State *s) if (s->oas == SMMU_OAS_48BIT) { s->idr[5] = FIELD_DP32(s->idr[5], IDR5, OAS, SMMU_IDR5_OAS_48); } + + /* + * By default QEMU SMMUv3 has no SubstreamID support. Update IDR1 if user + * has enabled it. + */ + s->idr[1] = FIELD_DP32(s->idr[1], IDR1, SSIDSIZE, s->ssidsize); } /* Based on SMUUv3 GPBA.ABORT configuration, attach a corresponding HWPT */ diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index bcf04d0a27..c462629f79 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -111,6 +111,8 @@ REG32(IDR5, 0x14) FIELD(IDR5, VAX, 10, 2); FIELD(IDR5, STALL_MAX, 16, 16); +#define SMMU_SSID_MAX_BITS 20 + #define SMMU_OAS_44BIT 44 #define SMMU_OAS_48BIT 48 #define SMMU_IDR5_OAS_44 4 diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index cb02184d2d..c08d58c579 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -611,9 +611,11 @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, } } - if (STE_S1CDMAX(ste) != 0) { + /* Multiple context descriptors require SubstreamID support */ + if (!s->ssidsize && STE_S1CDMAX(ste) != 0) { qemu_log_mask(LOG_UNIMP, - "SMMUv3 does not support multiple context descriptors yet\n"); + "SMMUv3: multiple S1 context descriptors require SubstreamID support. " + "Configure ssidsize > 0 (requires accel=on)\n"); goto bad_ste; } @@ -1954,6 +1956,10 @@ static bool smmu_validate_property(SMMUv3State *s, Error **errp) error_setg(errp, "OAS must be 44 bits when accel=off"); return false; } + if (s->ssidsize) { + error_setg(errp, "ssidsize can only be set if accel=on"); + return false; + } return true; } @@ -1968,6 +1974,11 @@ static bool smmu_validate_property(SMMUv3State *s, Error **errp) error_setg(errp, "OAS can only be set to 44 or 48 bits"); return false; } + if (s->ssidsize > SMMU_SSID_MAX_BITS) { + error_setg(errp, "ssidsize must be in the range 0 to %d", + SMMU_SSID_MAX_BITS); + return false; + } return true; } @@ -2096,6 +2107,7 @@ static const Property smmuv3_properties[] = { DEFINE_PROP_BOOL("ril", SMMUv3State, ril, true), DEFINE_PROP_BOOL("ats", SMMUv3State, ats, false), DEFINE_PROP_UINT8("oas", SMMUv3State, oas, 44), + DEFINE_PROP_UINT8("ssidsize", SMMUv3State, ssidsize, 0), }; static void smmuv3_instance_init(Object *obj) @@ -2129,6 +2141,12 @@ static void smmuv3_class_init(ObjectClass *klass, const void *data) object_class_property_set_description(klass, "oas", "Specify Output Address Size (for accel=on). Supported values " "are 44 or 48 bits. Defaults to 44 bits"); + object_class_property_set_description(klass, "ssidsize", + "Number of bits used to represent SubstreamIDs (SSIDs). " + "A value of N allows SSIDs in the range [0 .. 2^N - 1]. " + "Valid range is 0-20, where 0 disables SubstreamID support. " + "Defaults to 0. A value greater than 0 is required to enable " + "PASID support."); } static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu, diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h index d488a39cd0..26b2fc42fd 100644 --- a/include/hw/arm/smmuv3.h +++ b/include/hw/arm/smmuv3.h @@ -72,6 +72,7 @@ struct SMMUv3State { bool ril; bool ats; uint8_t oas; + uint8_t ssidsize; }; typedef enum { -- 2.43.0
