Subject: [PATCH v5 0/6] Add support for Qualcomm's legacy IOMMU v2

This series adds support for handling "v2" firmware's IOMMU, found
on at least MSM8956 and MSM8976 (some other SoCs also need the same
but I honestly don't remember which ones precisely).

This is strictly required to get functional IOMMUs on these SoCs.

I'm sorry for not performing a much needed schema conversion on
qcom,iommu.txt, but I really didn't have time to do that :-(

This series was tested on Sony Xperia X and X Compact (MSM8956):
ADSP, LPASS, Venus, MSS, MDP and GPU are happy :-)

Changes in v5:
- Renamed "qcom,ctx-num" to "qcom,ctx-asid" as suggested
by Rob Herring

Changes in v4:
- Rebase over next-20230619
- Rewrite qcom,iommu.txt changes to qcom,iommu.yaml
- Changed reset writes to only disable CB through CB_SCTLR
and reset CB_FSR and CB_FAR
- Addressed misc reviewer's comments

Changes in v3:
- Removed useless FSRRESTORE reset and definition as pointed
out in Robin Murphy's review
- Fixed qcom,iommu.txt changes: squashed MSM8976 compatible
string addition with msm-iommu-v2 generics addition

Changes in v2:
- Added back Marijn's notes (sorry man!)
- Added ARM_SMMU_CB_FSRRESTORE definition
- Changed context bank reset to properly set FSR and FSRRESTORE

AngeloGioacchino Del Regno (6):
dt-bindings: iommu: qcom,iommu: Add qcom,ctx-asid property
iommu/qcom: Use the asid read from device-tree if specified
iommu/qcom: Disable and reset context bank before programming
iommu/qcom: Index contexts by asid number to allow asid 0
dt-bindings: iommu: qcom,iommu: Add QSMMUv2 and MSM8976 compatibles
iommu/qcom: Add support for QSMMUv2 and QSMMU-500 secured contexts

.../devicetree/bindings/iommu/qcom,iommu.yaml | 22 +++++--
drivers/iommu/arm/arm-smmu/qcom_iommu.c | 62 ++++++++++++++-----
2 files changed, 64 insertions(+), 20 deletions(-)

--
2.40.1



Subject: [PATCH v5 3/6] iommu/qcom: Disable and reset context bank before programming

Writing the new TTBRs, TCRs and MAIRs on a previously enabled
context bank may trigger a context fault, resulting in firmware
driven AP resets: change the domain initialization programming
sequence to disable the context bank(s) and to also clear the
related fault address (CB_FAR) and fault status (CB_FSR)
registers before writing new values to TTBR0/1, TCR/TCR2, MAIR0/1.

Fixes: 0ae349a0f33f ("iommu/qcom: Add qcom_iommu")
Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
---
drivers/iommu/arm/arm-smmu/qcom_iommu.c | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
index 8face57c4180..f1bd7c035db8 100644
--- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c
+++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
@@ -273,6 +273,13 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain,
ctx->secure_init = true;
}

+ /* Disable context bank before programming */
+ iommu_writel(ctx, ARM_SMMU_CB_SCTLR, 0);
+
+ /* Clear context bank fault address fault status registers */
+ iommu_writel(ctx, ARM_SMMU_CB_FAR, 0);
+ iommu_writel(ctx, ARM_SMMU_CB_FSR, ARM_SMMU_FSR_FAULT);
+
/* TTBRs */
iommu_writeq(ctx, ARM_SMMU_CB_TTBR0,
pgtbl_cfg.arm_lpae_s1_cfg.ttbr |
--
2.40.1


Subject: [PATCH v5 6/6] iommu/qcom: Add support for QSMMUv2 and QSMMU-500 secured contexts

On some SoCs like MSM8956, MSM8976 and others, secure contexts are
also secured: these get programmed by the bootloader or TZ (as usual)
but their "interesting" registers are locked out by the hypervisor,
disallowing direct register writes from Linux and, in many cases,
completely disallowing the reprogramming of TTBR, TCR, MAIR and other
registers including, but not limited to, resetting contexts.
This is referred downstream as a "v2" IOMMU but this is effectively
a "v2 firmware configuration" instead.

Luckily, the described behavior of version 2 is effective only on
secure contexts and not on non-secure ones: add support for that,
finally getting a completely working IOMMU on at least MSM8956/76.

Signed-off-by: Marijn Suijten <[email protected]>
[Marijn: Rebased over next-20221111]
Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
Reviewed-by: Dmitry Baryshkov <[email protected]>
---
drivers/iommu/arm/arm-smmu/qcom_iommu.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
index 9786fd094e7d..7b6241f36698 100644
--- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c
+++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
@@ -59,6 +59,7 @@ struct qcom_iommu_ctx {
struct device *dev;
void __iomem *base;
bool secure_init;
+ bool secured_ctx;
u8 asid; /* asid and ctx bank # are 1:1 */
struct iommu_domain *domain;
};
@@ -273,6 +274,12 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain,
ctx->secure_init = true;
}

+ /* Secured QSMMU-500/QSMMU-v2 contexts cannot be programmed */
+ if (ctx->secured_ctx) {
+ ctx->domain = domain;
+ continue;
+ }
+
/* Disable context bank before programming */
iommu_writel(ctx, ARM_SMMU_CB_SCTLR, 0);

@@ -669,10 +676,14 @@ static int qcom_iommu_ctx_probe(struct platform_device *pdev)
if (irq < 0)
return -ENODEV;

+ if (of_device_is_compatible(dev->of_node, "qcom,msm-iommu-v2-sec"))
+ ctx->secured_ctx = true;
+
/* clear IRQs before registering fault handler, just in case the
* boot-loader left us a surprise:
*/
- iommu_writel(ctx, ARM_SMMU_CB_FSR, iommu_readl(ctx, ARM_SMMU_CB_FSR));
+ if (!ctx->secured_ctx)
+ iommu_writel(ctx, ARM_SMMU_CB_FSR, iommu_readl(ctx, ARM_SMMU_CB_FSR));

ret = devm_request_irq(dev, irq,
qcom_iommu_fault,
@@ -712,6 +723,8 @@ static void qcom_iommu_ctx_remove(struct platform_device *pdev)
static const struct of_device_id ctx_of_match[] = {
{ .compatible = "qcom,msm-iommu-v1-ns" },
{ .compatible = "qcom,msm-iommu-v1-sec" },
+ { .compatible = "qcom,msm-iommu-v2-ns" },
+ { .compatible = "qcom,msm-iommu-v2-sec" },
{ /* sentinel */ }
};

@@ -729,7 +742,8 @@ static bool qcom_iommu_has_secure_context(struct qcom_iommu_dev *qcom_iommu)
struct device_node *child;

for_each_child_of_node(qcom_iommu->dev->of_node, child) {
- if (of_device_is_compatible(child, "qcom,msm-iommu-v1-sec")) {
+ if (of_device_is_compatible(child, "qcom,msm-iommu-v1-sec") ||
+ of_device_is_compatible(child, "qcom,msm-iommu-v2-sec")) {
of_node_put(child);
return true;
}
@@ -873,6 +887,7 @@ static const struct dev_pm_ops qcom_iommu_pm_ops = {

static const struct of_device_id qcom_iommu_of_match[] = {
{ .compatible = "qcom,msm-iommu-v1" },
+ { .compatible = "qcom,msm-iommu-v2" },
{ /* sentinel */ }
};

--
2.40.1


Subject: [PATCH v5 2/6] iommu/qcom: Use the asid read from device-tree if specified

As specified in this driver, the context banks are 0x1000 apart but
on some SoCs the context number does not necessarily match this
logic, hence we end up using the wrong ASID: keeping in mind that
this IOMMU implementation relies heavily on SCM (TZ) calls, it is
mandatory that we communicate the right context number.

Since this is all about how context banks are mapped in firmware,
which may be board dependent (as a different firmware version may
eventually change the expected context bank numbers), introduce a
new property "qcom,ctx-asid": when found, the ASID will be forced
as read from the devicetree.

When "qcom,ctx-asid" is not found, this driver retains the previous
behavior as to avoid breaking older devicetrees or systems that do
not require forcing ASID numbers.

Signed-off-by: Marijn Suijten <[email protected]>
[Marijn: Rebased over next-20221111]
Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
---
drivers/iommu/arm/arm-smmu/qcom_iommu.c | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
index a503ed758ec3..8face57c4180 100644
--- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c
+++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
@@ -531,7 +531,8 @@ static int qcom_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
* index into qcom_iommu->ctxs:
*/
if (WARN_ON(asid < 1) ||
- WARN_ON(asid > qcom_iommu->num_ctxs)) {
+ WARN_ON(asid > qcom_iommu->num_ctxs) ||
+ WARN_ON(qcom_iommu->ctxs[asid - 1] == NULL)) {
put_device(&iommu_pdev->dev);
return -EINVAL;
}
@@ -617,7 +618,8 @@ static int qcom_iommu_sec_ptbl_init(struct device *dev)

static int get_asid(const struct device_node *np)
{
- u32 reg;
+ u32 reg, val;
+ int asid;

/* read the "reg" property directly to get the relative address
* of the context bank, and calculate the asid from that:
@@ -625,7 +627,17 @@ static int get_asid(const struct device_node *np)
if (of_property_read_u32_index(np, "reg", 0, &reg))
return -ENODEV;

- return reg / 0x1000; /* context banks are 0x1000 apart */
+ /*
+ * Context banks are 0x1000 apart but, in some cases, the ASID
+ * number doesn't match to this logic and needs to be passed
+ * from the DT configuration explicitly.
+ */
+ if (!of_property_read_u32(np, "qcom,ctx-asid", &val))
+ asid = val;
+ else
+ asid = reg / 0x1000;
+
+ return asid;
}

static int qcom_iommu_ctx_probe(struct platform_device *pdev)
--
2.40.1


Subject: [PATCH v5 4/6] iommu/qcom: Index contexts by asid number to allow asid 0

This driver was indexing the contexts by asid-1, which is probably
done under the assumption that the first ASID is always 1.
Unfortunately this is not always true: at least for MSM8956 and
MSM8976's GPU IOMMU, the gpu_user context's ASID number is zero.
To allow using a zero asid number, index the contexts by `asid`
instead of by `asid - 1`.

While at it, also enhance human readability by renaming the
`num_ctxs` member of struct qcom_iommu_dev to `max_asid`.

Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
---
drivers/iommu/arm/arm-smmu/qcom_iommu.c | 22 ++++++++++------------
1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
index f1bd7c035db8..9786fd094e7d 100644
--- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c
+++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
@@ -51,8 +51,8 @@ struct qcom_iommu_dev {
struct clk_bulk_data clks[CLK_NUM];
void __iomem *local_base;
u32 sec_id;
- u8 num_ctxs;
- struct qcom_iommu_ctx *ctxs[]; /* indexed by asid-1 */
+ u8 max_asid;
+ struct qcom_iommu_ctx *ctxs[]; /* indexed by asid */
};

struct qcom_iommu_ctx {
@@ -94,7 +94,7 @@ static struct qcom_iommu_ctx * to_ctx(struct qcom_iommu_domain *d, unsigned asid
struct qcom_iommu_dev *qcom_iommu = d->iommu;
if (!qcom_iommu)
return NULL;
- return qcom_iommu->ctxs[asid - 1];
+ return qcom_iommu->ctxs[asid];
}

static inline void
@@ -534,12 +534,10 @@ static int qcom_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
qcom_iommu = platform_get_drvdata(iommu_pdev);

/* make sure the asid specified in dt is valid, so we don't have
- * to sanity check this elsewhere, since 'asid - 1' is used to
- * index into qcom_iommu->ctxs:
+ * to sanity check this elsewhere:
*/
- if (WARN_ON(asid < 1) ||
- WARN_ON(asid > qcom_iommu->num_ctxs) ||
- WARN_ON(qcom_iommu->ctxs[asid - 1] == NULL)) {
+ if (WARN_ON(asid > qcom_iommu->max_asid) ||
+ WARN_ON(qcom_iommu->ctxs[asid] == NULL)) {
put_device(&iommu_pdev->dev);
return -EINVAL;
}
@@ -696,7 +694,7 @@ static int qcom_iommu_ctx_probe(struct platform_device *pdev)

dev_dbg(dev, "found asid %u\n", ctx->asid);

- qcom_iommu->ctxs[ctx->asid - 1] = ctx;
+ qcom_iommu->ctxs[ctx->asid] = ctx;

return 0;
}
@@ -708,7 +706,7 @@ static void qcom_iommu_ctx_remove(struct platform_device *pdev)

platform_set_drvdata(pdev, NULL);

- qcom_iommu->ctxs[ctx->asid - 1] = NULL;
+ qcom_iommu->ctxs[ctx->asid] = NULL;
}

static const struct of_device_id ctx_of_match[] = {
@@ -755,11 +753,11 @@ static int qcom_iommu_device_probe(struct platform_device *pdev)
for_each_child_of_node(dev->of_node, child)
max_asid = max(max_asid, get_asid(child));

- qcom_iommu = devm_kzalloc(dev, struct_size(qcom_iommu, ctxs, max_asid),
+ qcom_iommu = devm_kzalloc(dev, struct_size(qcom_iommu, ctxs, max_asid + 1),
GFP_KERNEL);
if (!qcom_iommu)
return -ENOMEM;
- qcom_iommu->num_ctxs = max_asid;
+ qcom_iommu->max_asid = max_asid;
qcom_iommu->dev = dev;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
--
2.40.1


2023-06-22 09:58:16

by Konrad Dybcio

[permalink] [raw]
Subject: Re: [PATCH v5 6/6] iommu/qcom: Add support for QSMMUv2 and QSMMU-500 secured contexts

On 22.06.2023 11:27, AngeloGioacchino Del Regno wrote:
> On some SoCs like MSM8956, MSM8976 and others, secure contexts are
> also secured: these get programmed by the bootloader or TZ (as usual)
> but their "interesting" registers are locked out by the hypervisor,
> disallowing direct register writes from Linux and, in many cases,
> completely disallowing the reprogramming of TTBR, TCR, MAIR and other
> registers including, but not limited to, resetting contexts.
> This is referred downstream as a "v2" IOMMU but this is effectively
> a "v2 firmware configuration" instead.
>
> Luckily, the described behavior of version 2 is effective only on
> secure contexts and not on non-secure ones: add support for that,
> finally getting a completely working IOMMU on at least MSM8956/76.
>
> Signed-off-by: Marijn Suijten <[email protected]>
> [Marijn: Rebased over next-20221111]
> Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
> Reviewed-by: Dmitry Baryshkov <[email protected]>
> ---
Reviewed-by: Konrad Dybcio <[email protected]>

Konrad
> drivers/iommu/arm/arm-smmu/qcom_iommu.c | 19 +++++++++++++++++--
> 1 file changed, 17 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
> index 9786fd094e7d..7b6241f36698 100644
> --- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c
> +++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
> @@ -59,6 +59,7 @@ struct qcom_iommu_ctx {
> struct device *dev;
> void __iomem *base;
> bool secure_init;
> + bool secured_ctx;
> u8 asid; /* asid and ctx bank # are 1:1 */
> struct iommu_domain *domain;
> };
> @@ -273,6 +274,12 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain,
> ctx->secure_init = true;
> }
>
> + /* Secured QSMMU-500/QSMMU-v2 contexts cannot be programmed */
> + if (ctx->secured_ctx) {
> + ctx->domain = domain;
> + continue;
> + }
> +
> /* Disable context bank before programming */
> iommu_writel(ctx, ARM_SMMU_CB_SCTLR, 0);
>
> @@ -669,10 +676,14 @@ static int qcom_iommu_ctx_probe(struct platform_device *pdev)
> if (irq < 0)
> return -ENODEV;
>
> + if (of_device_is_compatible(dev->of_node, "qcom,msm-iommu-v2-sec"))
> + ctx->secured_ctx = true;
> +
> /* clear IRQs before registering fault handler, just in case the
> * boot-loader left us a surprise:
> */
> - iommu_writel(ctx, ARM_SMMU_CB_FSR, iommu_readl(ctx, ARM_SMMU_CB_FSR));
> + if (!ctx->secured_ctx)
> + iommu_writel(ctx, ARM_SMMU_CB_FSR, iommu_readl(ctx, ARM_SMMU_CB_FSR));
>
> ret = devm_request_irq(dev, irq,
> qcom_iommu_fault,
> @@ -712,6 +723,8 @@ static void qcom_iommu_ctx_remove(struct platform_device *pdev)
> static const struct of_device_id ctx_of_match[] = {
> { .compatible = "qcom,msm-iommu-v1-ns" },
> { .compatible = "qcom,msm-iommu-v1-sec" },
> + { .compatible = "qcom,msm-iommu-v2-ns" },
> + { .compatible = "qcom,msm-iommu-v2-sec" },
> { /* sentinel */ }
> };
>
> @@ -729,7 +742,8 @@ static bool qcom_iommu_has_secure_context(struct qcom_iommu_dev *qcom_iommu)
> struct device_node *child;
>
> for_each_child_of_node(qcom_iommu->dev->of_node, child) {
> - if (of_device_is_compatible(child, "qcom,msm-iommu-v1-sec")) {
> + if (of_device_is_compatible(child, "qcom,msm-iommu-v1-sec") ||
> + of_device_is_compatible(child, "qcom,msm-iommu-v2-sec")) {
> of_node_put(child);
> return true;
> }
> @@ -873,6 +887,7 @@ static const struct dev_pm_ops qcom_iommu_pm_ops = {
>
> static const struct of_device_id qcom_iommu_of_match[] = {
> { .compatible = "qcom,msm-iommu-v1" },
> + { .compatible = "qcom,msm-iommu-v2" },
> { /* sentinel */ }
> };
>

2023-06-22 10:09:33

by Konrad Dybcio

[permalink] [raw]
Subject: Re: [PATCH v5 3/6] iommu/qcom: Disable and reset context bank before programming

On 22.06.2023 11:27, AngeloGioacchino Del Regno wrote:
> Writing the new TTBRs, TCRs and MAIRs on a previously enabled
> context bank may trigger a context fault, resulting in firmware
> driven AP resets: change the domain initialization programming
> sequence to disable the context bank(s) and to also clear the
> related fault address (CB_FAR) and fault status (CB_FSR)
> registers before writing new values to TTBR0/1, TCR/TCR2, MAIR0/1.
>
> Fixes: 0ae349a0f33f ("iommu/qcom: Add qcom_iommu")
> Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
> ---
Reviewed-by: Konrad Dybcio <[email protected]>

Konrad
> drivers/iommu/arm/arm-smmu/qcom_iommu.c | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
> index 8face57c4180..f1bd7c035db8 100644
> --- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c
> +++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
> @@ -273,6 +273,13 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain,
> ctx->secure_init = true;
> }
>
> + /* Disable context bank before programming */
> + iommu_writel(ctx, ARM_SMMU_CB_SCTLR, 0);
> +
> + /* Clear context bank fault address fault status registers */
> + iommu_writel(ctx, ARM_SMMU_CB_FAR, 0);
> + iommu_writel(ctx, ARM_SMMU_CB_FSR, ARM_SMMU_FSR_FAULT);
> +
> /* TTBRs */
> iommu_writeq(ctx, ARM_SMMU_CB_TTBR0,
> pgtbl_cfg.arm_lpae_s1_cfg.ttbr |

2023-06-22 10:18:53

by Konrad Dybcio

[permalink] [raw]
Subject: Re: [PATCH v5 2/6] iommu/qcom: Use the asid read from device-tree if specified

On 22.06.2023 11:27, AngeloGioacchino Del Regno wrote:
> As specified in this driver, the context banks are 0x1000 apart but
> on some SoCs the context number does not necessarily match this
> logic, hence we end up using the wrong ASID: keeping in mind that
> this IOMMU implementation relies heavily on SCM (TZ) calls, it is
> mandatory that we communicate the right context number.
>
> Since this is all about how context banks are mapped in firmware,
> which may be board dependent (as a different firmware version may
> eventually change the expected context bank numbers), introduce a
> new property "qcom,ctx-asid": when found, the ASID will be forced
> as read from the devicetree.
>
> When "qcom,ctx-asid" is not found, this driver retains the previous
> behavior as to avoid breaking older devicetrees or systems that do
> not require forcing ASID numbers.
>
> Signed-off-by: Marijn Suijten <[email protected]>
> [Marijn: Rebased over next-20221111]
> Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
> ---
Reviewed-by: Konrad Dybcio <[email protected]>

Konrad
> drivers/iommu/arm/arm-smmu/qcom_iommu.c | 18 +++++++++++++++---
> 1 file changed, 15 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
> index a503ed758ec3..8face57c4180 100644
> --- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c
> +++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
> @@ -531,7 +531,8 @@ static int qcom_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
> * index into qcom_iommu->ctxs:
> */
> if (WARN_ON(asid < 1) ||
> - WARN_ON(asid > qcom_iommu->num_ctxs)) {
> + WARN_ON(asid > qcom_iommu->num_ctxs) ||
> + WARN_ON(qcom_iommu->ctxs[asid - 1] == NULL)) {
> put_device(&iommu_pdev->dev);
> return -EINVAL;
> }
> @@ -617,7 +618,8 @@ static int qcom_iommu_sec_ptbl_init(struct device *dev)
>
> static int get_asid(const struct device_node *np)
> {
> - u32 reg;
> + u32 reg, val;
> + int asid;
>
> /* read the "reg" property directly to get the relative address
> * of the context bank, and calculate the asid from that:
> @@ -625,7 +627,17 @@ static int get_asid(const struct device_node *np)
> if (of_property_read_u32_index(np, "reg", 0, &reg))
> return -ENODEV;
>
> - return reg / 0x1000; /* context banks are 0x1000 apart */
> + /*
> + * Context banks are 0x1000 apart but, in some cases, the ASID
> + * number doesn't match to this logic and needs to be passed
> + * from the DT configuration explicitly.
> + */
> + if (!of_property_read_u32(np, "qcom,ctx-asid", &val))
> + asid = val;
> + else
> + asid = reg / 0x1000;
> +
> + return asid;
> }
>
> static int qcom_iommu_ctx_probe(struct platform_device *pdev)

Subject: Re: [PATCH v5 2/6] iommu/qcom: Use the asid read from device-tree if specified

Il 01/08/23 15:49, Will Deacon ha scritto:
> On Thu, Jun 22, 2023 at 11:27:38AM +0200, AngeloGioacchino Del Regno wrote:
>> As specified in this driver, the context banks are 0x1000 apart but
>> on some SoCs the context number does not necessarily match this
>> logic, hence we end up using the wrong ASID: keeping in mind that
>> this IOMMU implementation relies heavily on SCM (TZ) calls, it is
>> mandatory that we communicate the right context number.
>>
>> Since this is all about how context banks are mapped in firmware,
>> which may be board dependent (as a different firmware version may
>> eventually change the expected context bank numbers), introduce a
>> new property "qcom,ctx-asid": when found, the ASID will be forced
>> as read from the devicetree.
>>
>> When "qcom,ctx-asid" is not found, this driver retains the previous
>> behavior as to avoid breaking older devicetrees or systems that do
>> not require forcing ASID numbers.
>>
>> Signed-off-by: Marijn Suijten <[email protected]>
>> [Marijn: Rebased over next-20221111]
>> Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
>> ---
>> drivers/iommu/arm/arm-smmu/qcom_iommu.c | 18 +++++++++++++++---
>> 1 file changed, 15 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
>> index a503ed758ec3..8face57c4180 100644
>> --- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c
>> +++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
>> @@ -531,7 +531,8 @@ static int qcom_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
>> * index into qcom_iommu->ctxs:
>> */
>> if (WARN_ON(asid < 1) ||
>> - WARN_ON(asid > qcom_iommu->num_ctxs)) {
>> + WARN_ON(asid > qcom_iommu->num_ctxs) ||
>> + WARN_ON(qcom_iommu->ctxs[asid - 1] == NULL)) {
>> put_device(&iommu_pdev->dev);
>> return -EINVAL;
>> }
>> @@ -617,7 +618,8 @@ static int qcom_iommu_sec_ptbl_init(struct device *dev)
>>
>> static int get_asid(const struct device_node *np)
>> {
>> - u32 reg;
>> + u32 reg, val;
>> + int asid;
>>
>> /* read the "reg" property directly to get the relative address
>> * of the context bank, and calculate the asid from that:
>> @@ -625,7 +627,17 @@ static int get_asid(const struct device_node *np)
>> if (of_property_read_u32_index(np, "reg", 0, &reg))
>> return -ENODEV;
>>
>> - return reg / 0x1000; /* context banks are 0x1000 apart */
>> + /*
>> + * Context banks are 0x1000 apart but, in some cases, the ASID
>> + * number doesn't match to this logic and needs to be passed
>> + * from the DT configuration explicitly.
>> + */
>> + if (!of_property_read_u32(np, "qcom,ctx-asid", &val))
>> + asid = val;
>> + else
>> + asid = reg / 0x1000;
>> +
>> + return asid;
>
> Shouldn't we at least have some error checking here? For example, ensuring
> that the ASIDs are within range, aren't duplicates etc?
>

The only check that we can perform here for ASID-in-range is

if ((asid * 0x1000 > (mmio_start + mmio_size - 0x1000))
return -EINVAL;

...as for duplicates, a check can *probably* (surely) be done... but I'm not
sure I have any more time to feed more code to this series from years ago...

> Also, can you elaborate a little more on what sort of ASID-to-Context
> mappings you actually see in practice?
>

I'm sorry, but not really. The first version of this (including the whole research
that I had to perform to write those patches) is from year 2019, so 4 years ago...

...I don't really remember the full details anymore - if not that all of this was
done because context banks are fixed (and setup by TZ), tz takes an asid number
when trying to perform any operation on the context bank, and there's no way to
reset mappings because everything is protected by the hypervisor (which will fault
and reboot the AP instantly if you try).

Cheers,
Angelo



2023-08-01 15:26:14

by Will Deacon

[permalink] [raw]
Subject: Re: [PATCH v5 2/6] iommu/qcom: Use the asid read from device-tree if specified

On Thu, Jun 22, 2023 at 11:27:38AM +0200, AngeloGioacchino Del Regno wrote:
> As specified in this driver, the context banks are 0x1000 apart but
> on some SoCs the context number does not necessarily match this
> logic, hence we end up using the wrong ASID: keeping in mind that
> this IOMMU implementation relies heavily on SCM (TZ) calls, it is
> mandatory that we communicate the right context number.
>
> Since this is all about how context banks are mapped in firmware,
> which may be board dependent (as a different firmware version may
> eventually change the expected context bank numbers), introduce a
> new property "qcom,ctx-asid": when found, the ASID will be forced
> as read from the devicetree.
>
> When "qcom,ctx-asid" is not found, this driver retains the previous
> behavior as to avoid breaking older devicetrees or systems that do
> not require forcing ASID numbers.
>
> Signed-off-by: Marijn Suijten <[email protected]>
> [Marijn: Rebased over next-20221111]
> Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
> ---
> drivers/iommu/arm/arm-smmu/qcom_iommu.c | 18 +++++++++++++++---
> 1 file changed, 15 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
> index a503ed758ec3..8face57c4180 100644
> --- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c
> +++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
> @@ -531,7 +531,8 @@ static int qcom_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
> * index into qcom_iommu->ctxs:
> */
> if (WARN_ON(asid < 1) ||
> - WARN_ON(asid > qcom_iommu->num_ctxs)) {
> + WARN_ON(asid > qcom_iommu->num_ctxs) ||
> + WARN_ON(qcom_iommu->ctxs[asid - 1] == NULL)) {
> put_device(&iommu_pdev->dev);
> return -EINVAL;
> }
> @@ -617,7 +618,8 @@ static int qcom_iommu_sec_ptbl_init(struct device *dev)
>
> static int get_asid(const struct device_node *np)
> {
> - u32 reg;
> + u32 reg, val;
> + int asid;
>
> /* read the "reg" property directly to get the relative address
> * of the context bank, and calculate the asid from that:
> @@ -625,7 +627,17 @@ static int get_asid(const struct device_node *np)
> if (of_property_read_u32_index(np, "reg", 0, &reg))
> return -ENODEV;
>
> - return reg / 0x1000; /* context banks are 0x1000 apart */
> + /*
> + * Context banks are 0x1000 apart but, in some cases, the ASID
> + * number doesn't match to this logic and needs to be passed
> + * from the DT configuration explicitly.
> + */
> + if (!of_property_read_u32(np, "qcom,ctx-asid", &val))
> + asid = val;
> + else
> + asid = reg / 0x1000;
> +
> + return asid;

Shouldn't we at least have some error checking here? For example, ensuring
that the ASIDs are within range, aren't duplicates etc?

Also, can you elaborate a little more on what sort of ASID-to-Context
mappings you actually see in practice?

Will

2023-08-01 15:32:08

by Will Deacon

[permalink] [raw]
Subject: Re: [PATCH v5 4/6] iommu/qcom: Index contexts by asid number to allow asid 0

On Thu, Jun 22, 2023 at 11:27:40AM +0200, AngeloGioacchino Del Regno wrote:
> This driver was indexing the contexts by asid-1, which is probably
> done under the assumption that the first ASID is always 1.
> Unfortunately this is not always true: at least for MSM8956 and
> MSM8976's GPU IOMMU, the gpu_user context's ASID number is zero.
> To allow using a zero asid number, index the contexts by `asid`
> instead of by `asid - 1`.
>
> While at it, also enhance human readability by renaming the
> `num_ctxs` member of struct qcom_iommu_dev to `max_asid`.
>
> Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
> ---
> drivers/iommu/arm/arm-smmu/qcom_iommu.c | 22 ++++++++++------------
> 1 file changed, 10 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
> index f1bd7c035db8..9786fd094e7d 100644
> --- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c
> +++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
> @@ -51,8 +51,8 @@ struct qcom_iommu_dev {
> struct clk_bulk_data clks[CLK_NUM];
> void __iomem *local_base;
> u32 sec_id;
> - u8 num_ctxs;
> - struct qcom_iommu_ctx *ctxs[]; /* indexed by asid-1 */
> + u8 max_asid;
> + struct qcom_iommu_ctx *ctxs[]; /* indexed by asid */
> };
>
> struct qcom_iommu_ctx {
> @@ -94,7 +94,7 @@ static struct qcom_iommu_ctx * to_ctx(struct qcom_iommu_domain *d, unsigned asid
> struct qcom_iommu_dev *qcom_iommu = d->iommu;
> if (!qcom_iommu)
> return NULL;
> - return qcom_iommu->ctxs[asid - 1];
> + return qcom_iommu->ctxs[asid];
> }
>
> static inline void
> @@ -534,12 +534,10 @@ static int qcom_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
> qcom_iommu = platform_get_drvdata(iommu_pdev);
>
> /* make sure the asid specified in dt is valid, so we don't have
> - * to sanity check this elsewhere, since 'asid - 1' is used to
> - * index into qcom_iommu->ctxs:
> + * to sanity check this elsewhere:
> */
> - if (WARN_ON(asid < 1) ||
> - WARN_ON(asid > qcom_iommu->num_ctxs) ||
> - WARN_ON(qcom_iommu->ctxs[asid - 1] == NULL)) {
> + if (WARN_ON(asid > qcom_iommu->max_asid) ||
> + WARN_ON(qcom_iommu->ctxs[asid] == NULL)) {
> put_device(&iommu_pdev->dev);
> return -EINVAL;
> }
> @@ -696,7 +694,7 @@ static int qcom_iommu_ctx_probe(struct platform_device *pdev)
>
> dev_dbg(dev, "found asid %u\n", ctx->asid);
>
> - qcom_iommu->ctxs[ctx->asid - 1] = ctx;
> + qcom_iommu->ctxs[ctx->asid] = ctx;
>
> return 0;
> }
> @@ -708,7 +706,7 @@ static void qcom_iommu_ctx_remove(struct platform_device *pdev)
>
> platform_set_drvdata(pdev, NULL);
>
> - qcom_iommu->ctxs[ctx->asid - 1] = NULL;
> + qcom_iommu->ctxs[ctx->asid] = NULL;
> }
>
> static const struct of_device_id ctx_of_match[] = {
> @@ -755,11 +753,11 @@ static int qcom_iommu_device_probe(struct platform_device *pdev)
> for_each_child_of_node(dev->of_node, child)
> max_asid = max(max_asid, get_asid(child));
>
> - qcom_iommu = devm_kzalloc(dev, struct_size(qcom_iommu, ctxs, max_asid),
> + qcom_iommu = devm_kzalloc(dev, struct_size(qcom_iommu, ctxs, max_asid + 1),
> GFP_KERNEL);

So is this '+ 1' there to handle the case where ASIDs are indexed from 1? If
so, please add a comment because this isn't obvious at all.

Will

2023-08-11 12:16:10

by Will Deacon

[permalink] [raw]
Subject: Re: [PATCH v5 0/6] Add support for Qualcomm's legacy IOMMU v2

On Thu, 22 Jun 2023 11:27:36 +0200, AngeloGioacchino Del Regno wrote:
> This series adds support for handling "v2" firmware's IOMMU, found
> on at least MSM8956 and MSM8976 (some other SoCs also need the same
> but I honestly don't remember which ones precisely).
>
> This is strictly required to get functional IOMMUs on these SoCs.
>
> I'm sorry for not performing a much needed schema conversion on
> qcom,iommu.txt, but I really didn't have time to do that :-(
>
> [...]

Applied drivers updated to will (for-joerg/arm-smmu/updates), thanks!

[2/6] iommu/qcom: Use the asid read from device-tree if specified
https://git.kernel.org/will/c/fcf226f1f708
[3/6] iommu/qcom: Disable and reset context bank before programming
https://git.kernel.org/will/c/9f3fef23d9b5
[4/6] iommu/qcom: Index contexts by asid number to allow asid 0
https://git.kernel.org/will/c/ec5601661bfc
[6/6] iommu/qcom: Add support for QSMMUv2 and QSMMU-500 secured contexts
https://git.kernel.org/will/c/e30c960d3f44

Cheers,
--
Will

https://fixes.arm64.dev
https://next.arm64.dev
https://will.arm64.dev