[PATCH v8 12/25] iommu/arm-smmu-v3-kvm: Probe SMMU HW
From: Mostafa Saleh
Date: Tue Sep 22 2026 - 10:21:40 EST
Probe SMMU features from the IDR register space. Most of
the logic is common with the kernel.
The hypervisor enforces 2 extra requirements:
- Cache coherency.
- Nested translation support.
Signed-off-by: Mostafa Saleh <smostafa@xxxxxxxxxx>
---
.../arm/arm-smmu-v3/pkvm/arm-smmu-v3-hyp.h | 4 ++
.../iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3.c | 47 +++++++++++++++++++
2 files changed, 51 insertions(+)
diff --git a/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3-hyp.h b/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3-hyp.h
index 17b9454dab85..91177f00fa97 100644
--- a/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3-hyp.h
+++ b/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3-hyp.h
@@ -11,6 +11,9 @@
*
* Other members are filled and used at runtime by the SMMU driver.
* @base Virtual address of SMMU registers
+ * @oas PA size
+ * @pgsize_bitmap Supported page sizes
+ * @sid_bits Max number of SID bits supported
*/
struct hyp_arm_smmu_v3_device {
phys_addr_t mmio_addr;
@@ -20,6 +23,7 @@ struct hyp_arm_smmu_v3_device {
u32 options;
unsigned long oas;
unsigned long pgsize_bitmap;
+ unsigned int sid_bits;
};
extern size_t kvm_nvhe_sym(kvm_hyp_arm_smmu_v3_count);
diff --git a/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3.c
index 7022d38e75c4..f8b7c5406017 100644
--- a/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3.c
@@ -29,6 +29,47 @@ static void smmu_deinit_device(struct hyp_arm_smmu_v3_device *smmu)
smmu->base = NULL;
}
+/*
+ * Mini-probe and validation for the hypervisor.
+ */
+static int smmu_probe(struct hyp_arm_smmu_v3_device *smmu)
+{
+ u32 reg;
+
+ if (!(smmu->features & ARM_SMMU_FEAT_COHERENCY))
+ return -EINVAL;
+
+ reg = arm_smmu_idr0_probe(smmu);
+
+ if (!(FIELD_GET(IDR0_TTF, reg) & IDR0_TTF_AARCH64))
+ return -ENXIO;
+
+ if (!(smmu->features & (ARM_SMMU_FEAT_TT_LE | ARM_SMMU_FEAT_TT_BE)))
+ return -ENXIO;
+
+ reg = readl_relaxed(smmu->base + ARM_SMMU_IDR1);
+ if (reg & (IDR1_TABLES_PRESET | IDR1_QUEUES_PRESET | IDR1_REL))
+ return -EINVAL;
+
+ smmu->sid_bits = FIELD_GET(IDR1_SIDSIZE, reg);
+ /* Follows the kernel logic */
+ if (smmu->sid_bits <= STRTAB_SPLIT)
+ smmu->features &= ~ARM_SMMU_FEAT_2_LVL_STRTAB;
+
+ arm_smmu_idr3_probe(smmu);
+
+ arm_smmu_idr5_probe(smmu);
+ if (!smmu->oas)
+ smmu->oas = 48;
+
+ arm_smmu_device_iidr_probe(smmu);
+
+ if (!(smmu->features & ARM_SMMU_FEAT_NESTING))
+ return -ENXIO;
+
+ return 0;
+}
+
static int smmu_init_device(struct hyp_arm_smmu_v3_device *smmu)
{
unsigned long haddr;
@@ -43,8 +84,14 @@ static int smmu_init_device(struct hyp_arm_smmu_v3_device *smmu)
return ret;
smmu->base = (void __iomem *)haddr;
+ ret = smmu_probe(smmu);
+ if (ret)
+ goto out_ret;
return 0;
+out_ret:
+ smmu_deinit_device(smmu);
+ return ret;
}
/* Called while is the host is still trusted. */
--
2.55.0.1082.g2b9226bbc0-goog