[PATCH v9 12/25] iommu/arm-smmu-v3-kvm: Probe SMMU HW
From: Mostafa Saleh
Date: Thu Oct 15 2026 - 16:12:41 EST
Probe the SMMU features from the IDR registers with the functions shared
with the kernel driver. SubstreamID bits and queue sizes are not needed
here: CDs are not shadowed and the command queue is set up later.
The hypervisor requires two things the kernel driver does not:
- Coherent table walks, reported by both firmware and IDR0.COHACC, as
it does no cache maintenance on its tables.
- Nested translation, as the host keeps stage-1 and the hypervisor adds
stage-2.
Signed-off-by: Mostafa Saleh <smostafa@xxxxxxxxxx>
---
.../arm/arm-smmu-v3/pkvm/arm-smmu-v3-hyp.c | 46 ++++++++++++++++++-
1 file changed, 45 insertions(+), 1 deletion(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3-hyp.c b/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3-hyp.c
index ba209951039f..2b4a0f961a66 100644
--- a/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3-hyp.c
+++ b/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3-hyp.c
@@ -31,6 +31,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;
+ int ret;
+
+ /* Not supported yet. */
+ if (!(smmu->features & ARM_SMMU_FEAT_COHERENCY))
+ return -ENXIO;
+
+ ret = arm_smmu_device_idr0_probe(smmu, true, ®);
+ if (ret)
+ return ret;
+
+ /*
+ * Core code clears FEAT_ATS with !CONFIG_PCI_ATS, but the hypervisor
+ * must know if it is actually supported for CR0 emulation.
+ */
+ if (reg & IDR0_ATS)
+ smmu->features |= ARM_SMMU_FEAT_ATS;
+
+ /* Firmware says coherent, the hardware must agree. */
+ if (!(reg & IDR0_COHACC))
+ return -ENXIO;
+
+ ret = arm_smmu_device_idr1_probe(smmu, ®);
+ if (ret)
+ return ret;
+
+ arm_smmu_device_idr3_probe(smmu);
+ arm_smmu_device_idr5_probe(smmu);
+ 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;
@@ -45,8 +86,11 @@ 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)
+ smmu_deinit_device(smmu);
- return 0;
+ return ret;
}
/* Called while the host is still trusted. */
--
2.56.0.176.g3575e65682-goog