[RFC PATCH v2 6/9] iommu/arm-smmu-v3: check smmu compatibility on attach

From: Michael Shavit
Date: Tue Aug 22 2023 - 06:58:35 EST


Verify a domain's compatibility with the smmu when it's being attached
to a master belonging to a different smmu device.

Signed-off-by: Michael Shavit <mshavit@xxxxxxxxxx>
---

Changes in v2:
- Access the pgtbl_cfg from the pgtable_ops instead of storing a copy in
the arm_smmu_domain.

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 94 +++++++++++++++++----
1 file changed, 79 insertions(+), 15 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 9adc2cedd487b..2f305037b9250 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2213,10 +2213,41 @@ static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
return 0;
}

+static int arm_smmu_prepare_pgtbl_cfg(struct arm_smmu_device *smmu,
+ enum arm_smmu_domain_stage stage,
+ struct io_pgtable_cfg *pgtbl_cfg)
+{
+ unsigned long ias, oas;
+
+ switch (stage) {
+ case ARM_SMMU_DOMAIN_S1:
+ ias = (smmu->features & ARM_SMMU_FEAT_VAX) ? 52 : 48;
+ ias = min_t(unsigned long, ias, VA_BITS);
+ oas = smmu->ias;
+ break;
+ case ARM_SMMU_DOMAIN_NESTED:
+ case ARM_SMMU_DOMAIN_S2:
+ ias = smmu->ias;
+ oas = smmu->oas;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ *pgtbl_cfg = (struct io_pgtable_cfg) {
+ .pgsize_bitmap = smmu->pgsize_bitmap,
+ .ias = ias,
+ .oas = oas,
+ .coherent_walk = smmu->features & ARM_SMMU_FEAT_COHERENCY,
+ .tlb = &arm_smmu_flush_ops,
+ .iommu_dev = smmu->dev,
+ };
+ return 0;
+}
+
static int arm_smmu_domain_finalise(struct iommu_domain *domain)
{
int ret;
- unsigned long ias, oas;
enum io_pgtable_fmt fmt;
struct io_pgtable_cfg pgtbl_cfg;
struct io_pgtable_ops *pgtbl_ops;
@@ -2238,16 +2269,11 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)

switch (smmu_domain->stage) {
case ARM_SMMU_DOMAIN_S1:
- ias = (smmu->features & ARM_SMMU_FEAT_VAX) ? 52 : 48;
- ias = min_t(unsigned long, ias, VA_BITS);
- oas = smmu->ias;
fmt = ARM_64_LPAE_S1;
finalise_stage_fn = arm_smmu_domain_finalise_s1;
break;
case ARM_SMMU_DOMAIN_NESTED:
case ARM_SMMU_DOMAIN_S2:
- ias = smmu->ias;
- oas = smmu->oas;
fmt = ARM_64_LPAE_S2;
finalise_stage_fn = arm_smmu_domain_finalise_s2;
break;
@@ -2255,14 +2281,9 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain)
return -EINVAL;
}

- pgtbl_cfg = (struct io_pgtable_cfg) {
- .pgsize_bitmap = smmu->pgsize_bitmap,
- .ias = ias,
- .oas = oas,
- .coherent_walk = smmu->features & ARM_SMMU_FEAT_COHERENCY,
- .tlb = &arm_smmu_flush_ops,
- .iommu_dev = smmu->dev,
- };
+ ret = arm_smmu_prepare_pgtbl_cfg(smmu, smmu_domain->stage, &pgtbl_cfg);
+ if (ret)
+ return ret;

pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);
if (!pgtbl_ops)
@@ -2424,6 +2445,48 @@ static void arm_smmu_disable_pasid(struct arm_smmu_master *master)
pci_disable_pasid(pdev);
}

+static int
+arm_smmu_verify_domain_compatible(struct arm_smmu_device *smmu,
+ struct arm_smmu_domain *smmu_domain)
+{
+ struct io_pgtable_cfg pgtbl_cfg;
+ struct io_pgtable_cfg *domain_pgtbl_cfg =
+ &io_pgtable_ops_to_pgtable(smmu_domain->pgtbl_ops)->cfg;
+ int ret;
+
+ if (smmu_domain->domain.type == IOMMU_DOMAIN_IDENTITY)
+ return 0;
+
+ if (smmu_domain->stage == ARM_SMMU_DOMAIN_S2) {
+ if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2))
+ return -EINVAL;
+ if (smmu_domain->s2_cfg.vmid >> smmu->vmid_bits)
+ return -EINVAL;
+ } else if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {
+ if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1))
+ return -EINVAL;
+ if (smmu_domain->cd.asid >> smmu->asid_bits)
+ return -EINVAL;
+ }
+
+ ret = arm_smmu_prepare_pgtbl_cfg(smmu, smmu_domain->stage, &pgtbl_cfg);
+ if (ret)
+ return ret;
+
+ if (domain_pgtbl_cfg->ias > pgtbl_cfg.ias ||
+ domain_pgtbl_cfg->oas > pgtbl_cfg.oas ||
+ /*
+ * The supported pgsize_bitmap must be a superset of the domain's
+ * pgsize_bitmap.
+ */
+ (domain_pgtbl_cfg->pgsize_bitmap ^ pgtbl_cfg.pgsize_bitmap) &
+ domain_pgtbl_cfg->pgsize_bitmap ||
+ domain_pgtbl_cfg->coherent_walk != pgtbl_cfg.coherent_walk)
+ return -EINVAL;
+
+ return 0;
+}
+
static void arm_smmu_detach_dev(struct arm_smmu_master *master)
{
unsigned long flags;
@@ -2505,7 +2568,8 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
ret = arm_smmu_domain_finalise(domain);
if (ret)
smmu_domain->smmu = NULL;
- } else if (smmu_domain->smmu != smmu)
+ } else if (smmu_domain->smmu != smmu ||
+ !arm_smmu_verify_domain_compatible(smmu, smmu_domain))
ret = -EINVAL;

mutex_unlock(&smmu_domain->init_mutex);
--
2.42.0.rc1.204.g551eb34607-goog