Just before the device gets attached to the SMMUv3, make sure QEMU SMMUv3
features are compatible with the host SMMUv3.
Not all fields in the host SMMUv3 IDR registers are meaningful for userspace.
Only the following fields can be used:
- IDR0: ST_LEVEL, TERM_MODEL, STALL_MODEL, TTENDIAN, CD2L, ASID16, TTF
- IDR1: SIDSIZE, SSIDSIZE
- IDR3: BBML, RIL
- IDR5: VAX, GRAN64K, GRAN16K, GRAN4K
For now, the check is to make sure the features are in sync to enable
basic accelerated SMMUv3 support.
Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com>
---
hw/arm/smmuv3-accel.c | 100 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 100 insertions(+)
diff --git a/hw/arm/smmuv3-accel.c b/hw/arm/smmuv3-accel.c
index a2deda3c32..8b9f88dd8e 100644
--- a/hw/arm/smmuv3-accel.c
+++ b/hw/arm/smmuv3-accel.c
@@ -28,6 +28,98 @@ MemoryRegion root;
MemoryRegion sysmem;
static AddressSpace *shared_as_sysmem;
+static bool
+smmuv3_accel_check_hw_compatible(SMMUv3State *s,
+ struct iommu_hw_info_arm_smmuv3 *info,
+ Error **errp)
+{
+ /* QEMU SMMUv3 supports both linear and 2-level stream tables */
+ if (FIELD_EX32(info->idr[0], IDR0, STLEVEL) !=
+ FIELD_EX32(s->idr[0], IDR0, STLEVEL)) {
+ error_setg(errp, "Host SMMUv3 differs in Stream Table format");
+ return false;
+ }
+
+ /* QEMU SMMUv3 supports only little-endian translation table walks */
+ if (FIELD_EX32(info->idr[0], IDR0, TTENDIAN) >
+ FIELD_EX32(s->idr[0], IDR0, TTENDIAN)) {
+ error_setg(errp, "Host SMMUv3 doesn't support Little-endian "
+ "translation table");
+ return false;
+ }
+
+ /* QEMU SMMUv3 supports only AArch64 translation table format */
+ if (FIELD_EX32(info->idr[0], IDR0, TTF) <
+ FIELD_EX32(s->idr[0], IDR0, TTF)) {
+ error_setg(errp, "Host SMMUv3 doesn't support AArch64 translation "
+ "table format");
+ return false;
+ }
+
+ /* QEMU SMMUv3 supports SIDSIZE 16 */
+ if (FIELD_EX32(info->idr[1], IDR1, SIDSIZE) <
+ FIELD_EX32(s->idr[1], IDR1, SIDSIZE)) {
+ error_setg(errp, "Host SMMUv3 SIDSIZE not compatible");
+ return false;
+ }
+
+ /* QEMU SMMUv3 supports Range Invalidation by default */
+ if (FIELD_EX32(info->idr[3], IDR3, RIL) !=
+ FIELD_EX32(s->idr[3], IDR3, RIL)) {
+ error_setg(errp, "Host SMMUv3 doesn't support Range Invalidation");
+ return false;
+ }
+
+ /* QEMU SMMUv3 supports GRAN4K/GRAN16K/GRAN64K translation granules */
+ if (FIELD_EX32(info->idr[5], IDR5, GRAN4K) !=
+ FIELD_EX32(s->idr[5], IDR5, GRAN4K)) {
+ error_setg(errp, "Host SMMUv3 doesn't support 4K translation granule");
+ return false;
+ }
+ if (FIELD_EX32(info->idr[5], IDR5, GRAN16K) !=
+ FIELD_EX32(s->idr[5], IDR5, GRAN16K)) {
+ error_setg(errp, "Host SMMUv3 doesn't support 16K translation granule");
+ return false;
+ }
+ if (FIELD_EX32(info->idr[5], IDR5, GRAN64K) !=
+ FIELD_EX32(s->idr[5], IDR5, GRAN64K)) {
+ error_setg(errp, "Host SMMUv3 doesn't support 64K translation granule");
+ return false;
+ }
+
+ /* QEMU SMMUv3 supports architecture version 3.1 */
+ if (info->aidr < s->aidr) {
+ error_setg(errp, "Host SMMUv3 architecture version not compatible");
+ return false;
+ }
+ return true;
+}
+
+static bool
+smmuv3_accel_hw_compatible(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev,
+ Error **errp)
+{
+ struct iommu_hw_info_arm_smmuv3 info;
+ uint32_t data_type;
+ uint64_t caps;
+
+ if (!iommufd_backend_get_device_info(idev->iommufd, idev->devid, &data_type,
+ &info, sizeof(info), &caps, errp)) {
+ return false;
+ }
+
+ if (data_type != IOMMU_HW_INFO_TYPE_ARM_SMMUV3) {
+ error_setg(errp, "Wrong data type (%d) for Host SMMUv3 device info",
+ data_type);
+ return false;
+ }
+
+ if (!smmuv3_accel_check_hw_compatible(s, &info, errp)) {
+ return false;
+ }
+ return true;
+}
+
static bool
smmuv3_accel_alloc_vdev(SMMUv3AccelDevice *accel_dev, int sid, Error **errp)
{
@@ -356,6 +448,14 @@ static bool smmuv3_accel_set_iommu_device(PCIBus *bus, void *opaque, int devfn,
return true;
}
+ /*
+ * Check the host SMMUv3 associated with the dev is compatible with the
+ * QEMU SMMUv3 accel.
+ */
+ if (!smmuv3_accel_hw_compatible(s, idev, errp)) {
+ return false;
+ }
+
if (!smmuv3_accel_dev_alloc_viommu(accel_dev, idev, errp)) {
error_append_hint(errp, "Device 0x%x: Unable to alloc viommu", sid);
return false;
--
2.43.0