[PATCH v10 07/13] iommu/arm-smmu-v3-kexec: Add a CD table parse helper
Nicolin Chen
nicolinc at nvidia.com
Sun Aug 30 16:18:08 PDT 2026
An S1 STE points to a CD table that both of the kexec flavors decode: the
kdump adoption scans the CD table to reserve all the in-use ASIDs, and the
live-update restoration claims the CD table via the KHO restore API.
Add another read-only helper to the arm-smmu-v3-kexec.c:
- arm_smmu_kexec_check_ste_cdtab()
It validates the CD table geometry in an S1 STE against this kernel's own
ssid_bits and the 2-level HW capability. And it accepts a linear CD table
on the 2-level capable HW too, since a previous kernel might have used one,
like the linear stream table.
The CD table base gets validated against the table size as well, since the
spec aligns a CD table to its own size, where an unaligned base would be a
CONSTRAINED UNPREDICTABLE case: HW may zero its low bits or may fetch any
CD in the table, so a scan reading such a base could miss the CDs in use.
Reviewed-by: Jason Gunthorpe <jgg at nvidia.com>
Assisted-by: Claude:claude-fable-5
Signed-off-by: Nicolin Chen <nicolinc at nvidia.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 3 +
.../iommu/arm/arm-smmu-v3/arm-smmu-v3-kexec.c | 57 +++++++++++++++++++
2 files changed, 60 insertions(+)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index 41d7a907b9ba2..1bcf6cb2ebb3c 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -1261,6 +1261,9 @@ int arm_smmu_kexec_parse_strtab_linear(struct arm_smmu_device *smmu,
int arm_smmu_kexec_check_strtab_l1_desc(struct arm_smmu_device *smmu,
u64 l1_desc, u32 idx,
phys_addr_t *l2_base);
+int arm_smmu_kexec_check_ste_cdtab(struct arm_smmu_device *smmu, u64 ste0,
+ phys_addr_t *cdtab, u32 *s1fmt,
+ u32 *max_contexts);
#endif /* CONFIG_ARM_SMMU_V3_KEXEC */
#ifdef CONFIG_CRASH_DUMP
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kexec.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kexec.c
index b15e23df11b6d..700f63c7972e9 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kexec.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kexec.c
@@ -164,3 +164,60 @@ int arm_smmu_kexec_check_strtab_l1_desc(struct arm_smmu_device *smmu,
*l2_base = base;
return 0;
}
+
+/**
+ * arm_smmu_kexec_check_ste_cdtab() - Decode the CD table geometry of an STE
+ * @smmu: SMMU device of this kernel
+ * @ste0: first 64 bits of the previous kernel's S1 STE
+ * @cdtab: pointer to return the CD table's physical address
+ * @s1fmt: pointer to return the CD table format
+ * @max_contexts: pointer to return the number of CDs
+ *
+ * Note that a linear CD table on the 2-level capable hardware is accepted, as a
+ * previous kernel might have used one, like the linear stream table.
+ *
+ * Note that the spec requires a CD table to be aligned to its own size, so an
+ * unaligned @cdtab gets rejected here: HW may then zero the low bits or fetch
+ * any CD in the table, leaving the live ASIDs unknowable to this scan.
+ *
+ * Return: 0 on success with the three outputs set, or -EINVAL on a bad geometry
+ */
+int arm_smmu_kexec_check_ste_cdtab(struct arm_smmu_device *smmu, u64 ste0,
+ phys_addr_t *cdtab, u32 *s1fmt,
+ u32 *max_contexts)
+{
+ phys_addr_t base = ste0 & STRTAB_STE_0_S1CTXPTR_MASK;
+ u32 s1cdmax = FIELD_GET(STRTAB_STE_0_S1CDMAX, ste0);
+ u32 fmt = FIELD_GET(STRTAB_STE_0_S1FMT, ste0);
+ size_t size;
+
+ if (!base || s1cdmax > smmu->ssid_bits)
+ return -EINVAL;
+
+ if (fmt != STRTAB_STE_0_S1FMT_LINEAR &&
+ fmt != STRTAB_STE_0_S1FMT_64K_L2)
+ return -EINVAL;
+
+ /* Both kernels run on the same HW, so a genuine STE never has this */
+ if (fmt == STRTAB_STE_0_S1FMT_64K_L2 &&
+ !(smmu->features & ARM_SMMU_FEAT_2_LVL_CDTAB))
+ return -EINVAL;
+
+ if (fmt == STRTAB_STE_0_S1FMT_LINEAR)
+ size = (1UL << s1cdmax) * sizeof(struct arm_smmu_cd);
+ else
+ size = DIV_ROUND_UP(1UL << s1cdmax, CTXDESC_L2_ENTRIES) *
+ sizeof(struct arm_smmu_cdtab_l1);
+
+ /*
+ * An unaligned base is CONSTRAINED UNPREDICTABLE: HW may zero the low
+ * bits or fetch any CD in the table, so live ASIDs become unknowable.
+ */
+ if (!IS_ALIGNED(base, size))
+ return -EINVAL;
+
+ *cdtab = base;
+ *s1fmt = fmt;
+ *max_contexts = 1U << s1cdmax;
+ return 0;
+}
--
2.43.0
More information about the linux-arm-kernel
mailing list