diff mbox series

[v3,09/10] iommu/arm-smmu: Get associated RMR info and install bypass SMR

Message ID 20210420082751.1829-10-shameerali.kolothum.thodi@huawei.com (mailing list archive)
State New
Headers show
Series ACPI/IORT: Support for IORT RMR node | expand

Commit Message

Shameer Kolothum April 20, 2021, 8:27 a.m. UTC
From: Jon Nettleton <jon@solid-run.com>

Check if there is any RMR info associated with the devices behind
the SMMU and if any, install bypass SMRs for them. This is to
keep any ongoing traffic associated with these devices alive
when we enable/reset SMMU during probe().

Signed-off-by: Jon Nettleton <jon@solid-run.com>
Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
---
 drivers/iommu/arm/arm-smmu/arm-smmu.c | 42 +++++++++++++++++++++++++++
 drivers/iommu/arm/arm-smmu/arm-smmu.h |  2 ++
 2 files changed, 44 insertions(+)

Comments

Steven Price May 6, 2021, 3:17 p.m. UTC | #1
On 20/04/2021 09:27, Shameer Kolothum wrote:
> From: Jon Nettleton <jon@solid-run.com>
> 
> Check if there is any RMR info associated with the devices behind
> the SMMU and if any, install bypass SMRs for them. This is to
> keep any ongoing traffic associated with these devices alive
> when we enable/reset SMMU during probe().
> 
> Signed-off-by: Jon Nettleton <jon@solid-run.com>
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> ---
>   drivers/iommu/arm/arm-smmu/arm-smmu.c | 42 +++++++++++++++++++++++++++
>   drivers/iommu/arm/arm-smmu/arm-smmu.h |  2 ++
>   2 files changed, 44 insertions(+)
> 
> diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c
> index d8c6bfde6a61..4d2f91626d87 100644
> --- a/drivers/iommu/arm/arm-smmu/arm-smmu.c
> +++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c
> @@ -2102,6 +2102,43 @@ err_reset_platform_ops: __maybe_unused;
>   	return err;
>   }
>   
> +static void arm_smmu_rmr_install_bypass_smr(struct arm_smmu_device *smmu)
> +{
> +	struct iommu_rmr *e;
> +	int i, cnt = 0;
> +	u32 smr;
> +
> +	for (i = 0; i < smmu->num_mapping_groups; i++) {
> +		smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i));
> +		if (!FIELD_GET(ARM_SMMU_SMR_VALID, smr))
> +			continue;
> +
> +		list_for_each_entry(e, &smmu->rmr_list, list) {
> +			if (FIELD_GET(ARM_SMMU_SMR_ID, smr) != e->sid)
> +				continue;
> +
> +			smmu->smrs[i].id = FIELD_GET(ARM_SMMU_SMR_ID, smr);
> +			smmu->smrs[i].mask = FIELD_GET(ARM_SMMU_SMR_MASK, smr);
> +			smmu->smrs[i].valid = true;
> +
> +			smmu->s2crs[i].type = S2CR_TYPE_BYPASS;
> +			smmu->s2crs[i].privcfg = S2CR_PRIVCFG_DEFAULT;
> +			smmu->s2crs[i].cbndx = 0xff;
> +
> +			cnt++;
> +		}
> +	}

If I understand this correctly - this is looking at the current
(hardware) configuration of the SMMU and attempting to preserve any
bypass SMRs. However from what I can tell it suffers from the following
two problems:

  (a) Only the ID of the SMR is being checked, not the MASK. So if the
firmware has setup an SMR matching a number of streams this will break.

  (b) The SMMU might not be enabled at all (CLIENTPD==1) or bypass
enabled for unmatched streams (USFCFG==0).

Certainly in my test setup case (b) applies and so this doesn't work.
Perhaps something like the below would work better? (It works in the
case of the SMMU not enabled - I've not tested case (a)).

Steve

----8<----
static void arm_smmu_rmr_install_bypass_smr(struct arm_smmu_device *smmu)
{
	struct iommu_rmr *e;
	int i, cnt = 0;
	u32 smr;
	u32 reg;

	reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sCR0);

	if ((reg & ARM_SMMU_sCR0_USFCFG) && !(reg & ARM_SMMU_sCR0_CLIENTPD)) {
		/*
		 * SMMU is already enabled and disallowing bypass, so preserve
		 * the existing SMRs
		 */
		for (i = 0; i < smmu->num_mapping_groups; i++) {
			smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i));
			if (!FIELD_GET(ARM_SMMU_SMR_VALID, smr))
				continue;
			smmu->smrs[i].id = FIELD_GET(ARM_SMMU_SMR_ID, smr);
			smmu->smrs[i].mask = FIELD_GET(ARM_SMMU_SMR_MASK, smr);
			smmu->smrs[i].valid = true;
		}
	}

	list_for_each_entry(e, &smmu->rmr_list, list) {
		u32 sid = e->sid;

		i = arm_smmu_find_sme(smmu, sid, ~0);
		if (i < 0)
			continue;
		if (smmu->s2crs[i].count == 0) {
			smmu->smrs[i].id = sid;
			smmu->smrs[i].mask = ~0;
			smmu->smrs[i].valid = true;
		}
		smmu->s2crs[i].count++;
		smmu->s2crs[i].type = S2CR_TYPE_BYPASS;
		smmu->s2crs[i].privcfg = S2CR_PRIVCFG_DEFAULT;
		smmu->s2crs[i].cbndx = 0xff;

		cnt++;
	}

	if ((reg & ARM_SMMU_sCR0_USFCFG) && !(reg & ARM_SMMU_sCR0_CLIENTPD)) {
		/* Remove the valid bit for unused SMRs */
		for (i = 0; i < smmu->num_mapping_groups; i++) {
			if (smmu->s2crs[i].count == 0)
				smmu->smrs[i].valid = false;
		}
	}

	dev_notice(smmu->dev, "\tpreserved %d boot mapping%s\n", cnt,
		   cnt == 1 ? "" : "s");
}
Robin Murphy May 7, 2021, 9:52 a.m. UTC | #2
On 2021-05-06 16:17, Steven Price wrote:
> On 20/04/2021 09:27, Shameer Kolothum wrote:
>> From: Jon Nettleton <jon@solid-run.com>
>>
>> Check if there is any RMR info associated with the devices behind
>> the SMMU and if any, install bypass SMRs for them. This is to
>> keep any ongoing traffic associated with these devices alive
>> when we enable/reset SMMU during probe().
>>
>> Signed-off-by: Jon Nettleton <jon@solid-run.com>
>> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
>> ---
>>   drivers/iommu/arm/arm-smmu/arm-smmu.c | 42 +++++++++++++++++++++++++++
>>   drivers/iommu/arm/arm-smmu/arm-smmu.h |  2 ++
>>   2 files changed, 44 insertions(+)
>>
>> diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c 
>> b/drivers/iommu/arm/arm-smmu/arm-smmu.c
>> index d8c6bfde6a61..4d2f91626d87 100644
>> --- a/drivers/iommu/arm/arm-smmu/arm-smmu.c
>> +++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c
>> @@ -2102,6 +2102,43 @@ err_reset_platform_ops: __maybe_unused;
>>       return err;
>>   }
>> +static void arm_smmu_rmr_install_bypass_smr(struct arm_smmu_device 
>> *smmu)
>> +{
>> +    struct iommu_rmr *e;
>> +    int i, cnt = 0;
>> +    u32 smr;
>> +
>> +    for (i = 0; i < smmu->num_mapping_groups; i++) {
>> +        smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i));
>> +        if (!FIELD_GET(ARM_SMMU_SMR_VALID, smr))
>> +            continue;
>> +
>> +        list_for_each_entry(e, &smmu->rmr_list, list) {
>> +            if (FIELD_GET(ARM_SMMU_SMR_ID, smr) != e->sid)
>> +                continue;
>> +
>> +            smmu->smrs[i].id = FIELD_GET(ARM_SMMU_SMR_ID, smr);
>> +            smmu->smrs[i].mask = FIELD_GET(ARM_SMMU_SMR_MASK, smr);
>> +            smmu->smrs[i].valid = true;
>> +
>> +            smmu->s2crs[i].type = S2CR_TYPE_BYPASS;
>> +            smmu->s2crs[i].privcfg = S2CR_PRIVCFG_DEFAULT;
>> +            smmu->s2crs[i].cbndx = 0xff;
>> +
>> +            cnt++;
>> +        }
>> +    }
> 
> If I understand this correctly - this is looking at the current
> (hardware) configuration of the SMMU and attempting to preserve any
> bypass SMRs. However from what I can tell it suffers from the following
> two problems:
> 
>   (a) Only the ID of the SMR is being checked, not the MASK. So if the
> firmware has setup an SMR matching a number of streams this will break.
> 
>   (b) The SMMU might not be enabled at all (CLIENTPD==1) or bypass
> enabled for unmatched streams (USFCFG==0).

Yes, trying to infer anything from the current SMMU hardware state is 
bogus - consider what you might find left over after a kexec, for 
instance. The *only* way to detect the presence and applicability of 
RMRs is to look at the actual RMR nodes in the IORT.

Ignore what we let the Qualcomm ACPI bootloader hack do - that whole 
implementation is "special".

Robin.

> Certainly in my test setup case (b) applies and so this doesn't work.
> Perhaps something like the below would work better? (It works in the
> case of the SMMU not enabled - I've not tested case (a)).
> 
> Steve
> 
> ----8<----
> static void arm_smmu_rmr_install_bypass_smr(struct arm_smmu_device *smmu)
> {
>      struct iommu_rmr *e;
>      int i, cnt = 0;
>      u32 smr;
>      u32 reg;
> 
>      reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sCR0);
> 
>      if ((reg & ARM_SMMU_sCR0_USFCFG) && !(reg & ARM_SMMU_sCR0_CLIENTPD)) {
>          /*
>           * SMMU is already enabled and disallowing bypass, so preserve
>           * the existing SMRs
>           */
>          for (i = 0; i < smmu->num_mapping_groups; i++) {
>              smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i));
>              if (!FIELD_GET(ARM_SMMU_SMR_VALID, smr))
>                  continue;
>              smmu->smrs[i].id = FIELD_GET(ARM_SMMU_SMR_ID, smr);
>              smmu->smrs[i].mask = FIELD_GET(ARM_SMMU_SMR_MASK, smr);
>              smmu->smrs[i].valid = true;
>          }
>      }
> 
>      list_for_each_entry(e, &smmu->rmr_list, list) {
>          u32 sid = e->sid;
> 
>          i = arm_smmu_find_sme(smmu, sid, ~0);
>          if (i < 0)
>              continue;
>          if (smmu->s2crs[i].count == 0) {
>              smmu->smrs[i].id = sid;
>              smmu->smrs[i].mask = ~0;
>              smmu->smrs[i].valid = true;
>          }
>          smmu->s2crs[i].count++;
>          smmu->s2crs[i].type = S2CR_TYPE_BYPASS;
>          smmu->s2crs[i].privcfg = S2CR_PRIVCFG_DEFAULT;
>          smmu->s2crs[i].cbndx = 0xff;
> 
>          cnt++;
>      }
> 
>      if ((reg & ARM_SMMU_sCR0_USFCFG) && !(reg & ARM_SMMU_sCR0_CLIENTPD)) {
>          /* Remove the valid bit for unused SMRs */
>          for (i = 0; i < smmu->num_mapping_groups; i++) {
>              if (smmu->s2crs[i].count == 0)
>                  smmu->smrs[i].valid = false;
>          }
>      }
> 
>      dev_notice(smmu->dev, "\tpreserved %d boot mapping%s\n", cnt,
>             cnt == 1 ? "" : "s");
> }
diff mbox series

Patch

diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c
index d8c6bfde6a61..4d2f91626d87 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c
@@ -2102,6 +2102,43 @@  err_reset_platform_ops: __maybe_unused;
 	return err;
 }
 
+static void arm_smmu_rmr_install_bypass_smr(struct arm_smmu_device *smmu)
+{
+	struct iommu_rmr *e;
+	int i, cnt = 0;
+	u32 smr;
+
+	for (i = 0; i < smmu->num_mapping_groups; i++) {
+		smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i));
+		if (!FIELD_GET(ARM_SMMU_SMR_VALID, smr))
+			continue;
+
+		list_for_each_entry(e, &smmu->rmr_list, list) {
+			if (FIELD_GET(ARM_SMMU_SMR_ID, smr) != e->sid)
+				continue;
+
+			smmu->smrs[i].id = FIELD_GET(ARM_SMMU_SMR_ID, smr);
+			smmu->smrs[i].mask = FIELD_GET(ARM_SMMU_SMR_MASK, smr);
+			smmu->smrs[i].valid = true;
+
+			smmu->s2crs[i].type = S2CR_TYPE_BYPASS;
+			smmu->s2crs[i].privcfg = S2CR_PRIVCFG_DEFAULT;
+			smmu->s2crs[i].cbndx = 0xff;
+
+			cnt++;
+		}
+	}
+
+	dev_notice(smmu->dev, "\tpreserved %d boot mapping%s\n", cnt,
+		   cnt == 1 ? "" : "s");
+}
+
+static int arm_smmu_get_rmr(struct arm_smmu_device *smmu)
+{
+	INIT_LIST_HEAD(&smmu->rmr_list);
+	return iommu_dma_get_rmrs(dev_fwnode(smmu->dev), &smmu->rmr_list);
+}
+
 static int arm_smmu_device_probe(struct platform_device *pdev)
 {
 	struct resource *res;
@@ -2231,6 +2268,11 @@  static int arm_smmu_device_probe(struct platform_device *pdev)
 	}
 
 	platform_set_drvdata(pdev, smmu);
+
+	/* Check for RMRs and install bypass SMRs if any */
+	if (!arm_smmu_get_rmr(smmu))
+		arm_smmu_rmr_install_bypass_smr(smmu);
+
 	arm_smmu_device_reset(smmu);
 	arm_smmu_test_smr_masks(smmu);
 
diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.h b/drivers/iommu/arm/arm-smmu/arm-smmu.h
index d2a2d1bc58ba..ca9559eb8733 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.h
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.h
@@ -326,6 +326,8 @@  struct arm_smmu_device {
 
 	/* IOMMU core code handle */
 	struct iommu_device		iommu;
+
+	struct list_head		rmr_list;
 };
 
 enum arm_smmu_context_fmt {