[RFC PATCH] crypto: caam - restore retry count after HW RNG failure

From: Michal Vokáč
Date: Fri Nov 12 2021 - 10:27:58 EST


From: Petr Benes <petr.benes@xxxxxxxxx>

Each time TRNG generates entropy, statistical tests are run.
If they fail, RETRY_COUNT value is decremented. Once it
reaches 0, HW RNG returns an error, and needs to be reset.
RETRY_COUNT could be programmed in RTSCMISC register and is
set to 1 by default. Hence, we are left without hwrng after
the first error, which could happen even under normal
conditions.

Cc: petrben@xxxxxxxxx
Signed-off-by: Petr Benes <petr.benes@xxxxxxxxx>
Signed-off-by: Michal Vokáč <michal.vokac@xxxxxxxxx>
---
Hi,
we are also experiencing this issue:

caam_jr 2101000.jr0: 20003c5b: CCB: desc idx 60: RNG: Hardware error.

It is happening on both i.MX6S and i.MX6DL SoCs we use.
On Solo I can reproduce it really fast. Sometimes it happens right
after the board is NFS booted, sometimes I need to stress the HWRNG
for a while (generate few hundred KBs of random data). On some
DualLite SoCs it is happening at least once a day.

We are using the v5.10 LTS branch but I can confirm that this is
happening on all kernels since v5.7 to the latest linux-next.

We also tried to increase the RTSDCTL_ENT_DLY_MIN delay as suggested
in this thread [1]. It helped and the issue never occurred since then
but we are looking for more universal and permanent solution suitable
for upstream, hence we came up with this patch.

Any comments will be appreciated.
Thanks, Michal

[1] https://lkml.org/lkml/2021/8/30/296

drivers/crypto/caam/caamrng.c | 42 ++++++++++++++++++++++++++++++++---
drivers/crypto/caam/ctrl.c | 13 +++++++++++
drivers/crypto/caam/ctrl.h | 2 ++
3 files changed, 54 insertions(+), 3 deletions(-)

diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c
index 77d048dfe5d0..2be5584ae591 100644
--- a/drivers/crypto/caam/caamrng.c
+++ b/drivers/crypto/caam/caamrng.c
@@ -21,6 +21,7 @@
#include "desc_constr.h"
#include "jr.h"
#include "error.h"
+#include "ctrl.h"

#define CAAM_RNG_MAX_FIFO_STORE_SIZE 16

@@ -113,6 +114,35 @@ static int caam_rng_read_one(struct device *jrdev,
return err ?: (ret ?: len);
}

+static void caam_rng_retry_reset(struct caam_rng_ctx *context)
+{
+ struct device *ctrldev = context->ctrldev;
+ struct caam_drv_private *ctrlpriv = dev_get_drvdata(ctrldev);
+ struct caam_ctrl __iomem *ctrl;
+ struct rng4tst __iomem *r4tst;
+ u32 __iomem *rtstatus;
+ u32 retry_count;
+
+ ctrl = (struct caam_ctrl __iomem *)ctrlpriv->ctrl;
+ r4tst = &ctrl->r4tst[0];
+
+ /*
+ * There is unfortunately no member for RTSTATUS register in
+ * struct rng4tst and the structure doesn't look stable
+ */
+ rtstatus = (u32 *)((char *)&ctrl->r4tst[0] + 0x3C);
+ retry_count = (rd_reg32(rtstatus) >> 16) & 0xf;
+ dev_dbg(ctrldev, "CAAM RNG retry count %d\n", retry_count);
+ if (retry_count == 0) {
+ dev_err(ctrldev, "CAAM RNG resetting retry count to 1\n");
+ clrsetbits_32(&r4tst->rtmctl, 0, RTMCTL_PRGM | RTMCTL_ACC);
+ wr_reg32(&r4tst->rtscmisc, (rd_reg32(&r4tst->rtscmisc) & 0x7f) | (1 << 16));
+ clrsetbits_32(&r4tst->rtmctl, RTMCTL_PRGM | RTMCTL_ACC,
+ RTMCTL_SAMP_MODE_RAW_ES_SC);
+ caam_reinstantiate_rng(ctrldev);
+ }
+}
+
static void caam_rng_fill_async(struct caam_rng_ctx *ctx)
{
struct scatterlist sg[1];
@@ -129,8 +159,10 @@ static void caam_rng_fill_async(struct caam_rng_ctx *ctx)
sg[0].length,
ctx->desc_async,
&done);
- if (len < 0)
+ if (len < 0) {
+ caam_rng_retry_reset(ctx);
return;
+ }

kfifo_dma_in_finish(&ctx->fifo, len);
}
@@ -145,13 +177,17 @@ static void caam_rng_worker(struct work_struct *work)
static int caam_read(struct hwrng *rng, void *dst, size_t max, bool wait)
{
struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
- int out;
+ int out, ret;

if (wait) {
struct completion done;

- return caam_rng_read_one(ctx->jrdev, dst, max,
+ ret = caam_rng_read_one(ctx->jrdev, dst, max,
ctx->desc_sync, &done);
+ if (ret < 0)
+ caam_rng_retry_reset(ctx);
+
+ return ret;
}

out = kfifo_out(&ctx->fifo, dst, max);
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index ca0361b2dbb0..e421f8d1982b 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -339,6 +339,19 @@ static int instantiate_rng(struct device *ctrldev, int state_handle_mask,
return devm_add_action_or_reset(ctrldev, devm_deinstantiate_rng, ctrldev);
}

+/*
+ * caam_reinstantiate_rng - reinstantiates RNG. Intended for a case when RNG falls into
+ * HW error condition. That happens if TRNG fails statistical
+ * check and RTY_CNT value set in RTSCMISC decrements to zero.
+ * It is exported to caamrng.c
+ * @ctrldev - pointer to device
+ */
+
+int caam_reinstantiate_rng(struct device *ctrldev)
+{
+ return instantiate_rng(ctrldev, 0, 0);
+}
+
/*
* kick_trng - sets the various parameters for enabling the initialization
* of the RNG4 block in CAAM
diff --git a/drivers/crypto/caam/ctrl.h b/drivers/crypto/caam/ctrl.h
index f3ecd67922a7..26ff5a49a865 100644
--- a/drivers/crypto/caam/ctrl.h
+++ b/drivers/crypto/caam/ctrl.h
@@ -8,6 +8,8 @@
#ifndef CTRL_H
#define CTRL_H

+int caam_reinstantiate_rng(struct device *ctrldev);
+
/* Prototypes for backend-level services exposed to APIs */
extern bool caam_dpaa2;

--
2.25.1