diff mbox series

[RFC,v1,4/8] iommu/arm-smmu-v3: check smmu compatibility on attach

Message ID 20230818021629.RFC.v1.4.I100c49a1e2ce915982965a065f95a494c2e9ad28@changeid (mailing list archive)
State New, archived
Headers show
Series Install domain onto multiple smmus | expand

Commit Message

Michael Shavit Aug. 17, 2023, 6:16 p.m. UTC
Record the domain's pgtbl_cfg when it's being prepared so that it can
later be compared to the features an smmu supports.
Verify a domain's compatibility with the smmu when it's being attached
to a master belong to a different smmu device.

Signed-off-by: Michael Shavit <mshavit@google.com>
---

 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 103 ++++++++++++++++----
 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |   2 +
 2 files changed, 86 insertions(+), 19 deletions(-)

Comments

Robin Murphy Aug. 17, 2023, 7:16 p.m. UTC | #1
On 2023-08-17 19:16, Michael Shavit wrote:
> Record the domain's pgtbl_cfg when it's being prepared so that it can
> later be compared to the features an smmu supports.

What's wrong with retrieving the existing config from the 
io_pgtable_ops, same as all the other io-pgtable code does?

Thanks,
Robin.

> Verify a domain's compatibility with the smmu when it's being attached
> to a master belong to a different smmu device.
> 
> Signed-off-by: Michael Shavit <mshavit@google.com>
> ---
> 
>   drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 103 ++++++++++++++++----
>   drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h |   2 +
>   2 files changed, 86 insertions(+), 19 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 447af74dbe280..c0943cf3c09ca 100644
> --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
> +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
> @@ -2195,17 +2195,48 @@ 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;
>   	int (*finalise_stage_fn)(struct arm_smmu_domain *,
>   				 struct io_pgtable_cfg *);
>   	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
>   	struct arm_smmu_device *smmu = smmu_domain->smmu;
> +	struct io_pgtable_cfg *pgtbl_cfg = &smmu_domain->pgtbl_cfg;
>   
>   	if (domain->type == IOMMU_DOMAIN_IDENTITY) {
>   		smmu_domain->stage = ARM_SMMU_DOMAIN_BYPASS;
> @@ -2220,16 +2251,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;
> @@ -2237,24 +2263,19 @@ 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);
> +	pgtbl_ops = alloc_io_pgtable_ops(fmt, pgtbl_cfg, smmu_domain);
>   	if (!pgtbl_ops)
>   		return -ENOMEM;
>   
> -	domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
> -	domain->geometry.aperture_end = (1UL << pgtbl_cfg.ias) - 1;
> +	domain->pgsize_bitmap = pgtbl_cfg->pgsize_bitmap;
> +	domain->geometry.aperture_end = (1UL << pgtbl_cfg->ias) - 1;
>   	domain->geometry.force_aperture = true;
>   
> -	ret = finalise_stage_fn(smmu_domain, &pgtbl_cfg);
> +	ret = finalise_stage_fn(smmu_domain, pgtbl_cfg);
>   	if (ret < 0) {
>   		free_io_pgtable_ops(pgtbl_ops);
>   		return ret;
> @@ -2406,6 +2427,46 @@ 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;
> +	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 (smmu_domain->pgtbl_cfg.ias > pgtbl_cfg.ias ||
> +	    smmu_domain->pgtbl_cfg.oas > pgtbl_cfg.oas ||
> +	    /*
> +	     * The supported pgsize_bitmap must be a superset of the domain's
> +	     * pgsize_bitmap.
> +	     */
> +	    (smmu_domain->pgtbl_cfg.pgsize_bitmap ^ pgtbl_cfg.pgsize_bitmap) &
> +		    smmu_domain->pgtbl_cfg.pgsize_bitmap ||
> +	    smmu_domain->pgtbl_cfg.coherent_walk != pgtbl_cfg.coherent_walk)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
>   static void arm_smmu_installed_smmus_remove_device(
>   	struct arm_smmu_domain *smmu_domain,
>   	struct arm_smmu_master *master)
> @@ -2449,6 +2510,10 @@ arm_smmu_installed_smmus_add_device(struct arm_smmu_domain *smmu_domain,
>   		}
>   	}
>   	if (!list_entry_found) {
> +		ret = arm_smmu_verify_domain_compatible(smmu, smmu_domain);
> +		if (ret)
> +			goto unlock;
> +
>   		installed_smmu = kzalloc(sizeof(*installed_smmu), GFP_KERNEL);
>   		if (!installed_smmu) {
>   			ret = -ENOMEM;
> 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 2ab23139c796e..143b287be2f8b 100644
> --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
> +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
> @@ -9,6 +9,7 @@
>   #define _ARM_SMMU_V3_H
>   
>   #include <linux/bitfield.h>
> +#include <linux/io-pgtable.h>
>   #include <linux/iommu.h>
>   #include <linux/kernel.h>
>   #include <linux/mmzone.h>
> @@ -729,6 +730,7 @@ struct arm_smmu_domain {
>   	struct mutex			init_mutex; /* Protects smmu pointer */
>   
>   	struct io_pgtable_ops		*pgtbl_ops;
> +	struct io_pgtable_cfg		pgtbl_cfg;
>   	atomic_t			nr_ats_masters;
>   
>   	enum arm_smmu_domain_stage	stage;
Michael Shavit Aug. 18, 2023, 3:14 a.m. UTC | #2
On Fri, Aug 18, 2023 at 3:17 AM Robin Murphy <robin.murphy@arm.com> wrote:
>
> On 2023-08-17 19:16, Michael Shavit wrote:
> > Record the domain's pgtbl_cfg when it's being prepared so that it can
> > later be compared to the features an smmu supports.
>
> What's wrong with retrieving the existing config from the
> io_pgtable_ops, same as all the other io-pgtable code does?
>

I didn't think accessing the io_pgtable struct outside of io-pgtable
code would be ok, but now that you mention it I do see usages of
io_pgtable_ops_to_pgtable in iommu drivers.

Will change.
diff mbox series

Patch

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 447af74dbe280..c0943cf3c09ca 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2195,17 +2195,48 @@  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;
 	int (*finalise_stage_fn)(struct arm_smmu_domain *,
 				 struct io_pgtable_cfg *);
 	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
 	struct arm_smmu_device *smmu = smmu_domain->smmu;
+	struct io_pgtable_cfg *pgtbl_cfg = &smmu_domain->pgtbl_cfg;
 
 	if (domain->type == IOMMU_DOMAIN_IDENTITY) {
 		smmu_domain->stage = ARM_SMMU_DOMAIN_BYPASS;
@@ -2220,16 +2251,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;
@@ -2237,24 +2263,19 @@  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);
+	pgtbl_ops = alloc_io_pgtable_ops(fmt, pgtbl_cfg, smmu_domain);
 	if (!pgtbl_ops)
 		return -ENOMEM;
 
-	domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
-	domain->geometry.aperture_end = (1UL << pgtbl_cfg.ias) - 1;
+	domain->pgsize_bitmap = pgtbl_cfg->pgsize_bitmap;
+	domain->geometry.aperture_end = (1UL << pgtbl_cfg->ias) - 1;
 	domain->geometry.force_aperture = true;
 
-	ret = finalise_stage_fn(smmu_domain, &pgtbl_cfg);
+	ret = finalise_stage_fn(smmu_domain, pgtbl_cfg);
 	if (ret < 0) {
 		free_io_pgtable_ops(pgtbl_ops);
 		return ret;
@@ -2406,6 +2427,46 @@  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;
+	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 (smmu_domain->pgtbl_cfg.ias > pgtbl_cfg.ias ||
+	    smmu_domain->pgtbl_cfg.oas > pgtbl_cfg.oas ||
+	    /*
+	     * The supported pgsize_bitmap must be a superset of the domain's
+	     * pgsize_bitmap.
+	     */
+	    (smmu_domain->pgtbl_cfg.pgsize_bitmap ^ pgtbl_cfg.pgsize_bitmap) &
+		    smmu_domain->pgtbl_cfg.pgsize_bitmap ||
+	    smmu_domain->pgtbl_cfg.coherent_walk != pgtbl_cfg.coherent_walk)
+		return -EINVAL;
+
+	return 0;
+}
+
 static void arm_smmu_installed_smmus_remove_device(
 	struct arm_smmu_domain *smmu_domain,
 	struct arm_smmu_master *master)
@@ -2449,6 +2510,10 @@  arm_smmu_installed_smmus_add_device(struct arm_smmu_domain *smmu_domain,
 		}
 	}
 	if (!list_entry_found) {
+		ret = arm_smmu_verify_domain_compatible(smmu, smmu_domain);
+		if (ret)
+			goto unlock;
+
 		installed_smmu = kzalloc(sizeof(*installed_smmu), GFP_KERNEL);
 		if (!installed_smmu) {
 			ret = -ENOMEM;
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 2ab23139c796e..143b287be2f8b 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -9,6 +9,7 @@ 
 #define _ARM_SMMU_V3_H
 
 #include <linux/bitfield.h>
+#include <linux/io-pgtable.h>
 #include <linux/iommu.h>
 #include <linux/kernel.h>
 #include <linux/mmzone.h>
@@ -729,6 +730,7 @@  struct arm_smmu_domain {
 	struct mutex			init_mutex; /* Protects smmu pointer */
 
 	struct io_pgtable_ops		*pgtbl_ops;
+	struct io_pgtable_cfg		pgtbl_cfg;
 	atomic_t			nr_ats_masters;
 
 	enum arm_smmu_domain_stage	stage;