]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
hwrng: stm32 - rework error handling in stm32_rng_read()
authorGatien Chevallier <gatien.chevallier@foss.st.com>
Thu, 21 Sep 2023 08:02:57 +0000 (10:02 +0200)
committerHerbert Xu <herbert@gondor.apana.org.au>
Sun, 1 Oct 2023 08:28:15 +0000 (16:28 +0800)
Try to conceal seed errors when possible. If, despite the error
concealing tries, a seed error is still present, then return an error.

A clock error does not compromise the hardware block and data can
still be read from RNG_DR. Just warn that the RNG clock is too slow
and clear RNG_SR.

Signed-off-by: Gatien Chevallier <gatien.chevallier@foss.st.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
drivers/char/hw_random/stm32-rng.c

index adefe8edfd073113751a9712e53da1df298b40d6..9dac177d5286dd7f794b2a2acf53d3e367fa6645 100644 (file)
@@ -43,6 +43,8 @@
 
 #define RNG_HTCR               0x10
 
+#define RNG_NB_RECOVER_TRIES   3
+
 struct stm32_rng_data {
        u32     cr;
        u32     nscr;
@@ -162,10 +164,10 @@ static int stm32_rng_conceal_seed_error(struct hwrng *rng)
 
 static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
 {
-       struct stm32_rng_private *priv =
-           container_of(rng, struct stm32_rng_private, rng);
+       struct stm32_rng_private *priv = container_of(rng, struct stm32_rng_private, rng);
+       unsigned int i = 0;
+       int retval = 0, err = 0;
        u32 sr;
-       int retval = 0;
 
        pm_runtime_get_sync((struct device *) priv->rng.priv);
 
@@ -174,30 +176,57 @@ static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
 
        while (max >= sizeof(u32)) {
                sr = readl_relaxed(priv->base + RNG_SR);
-               /* Manage timeout which is based on timer and take */
-               /* care of initial delay time when enabling rng */
+               /*
+                * Manage timeout which is based on timer and take
+                * care of initial delay time when enabling the RNG.
+                */
                if (!sr && wait) {
-                       int err;
-
                        err = readl_relaxed_poll_timeout_atomic(priv->base
                                                                   + RNG_SR,
                                                                   sr, sr,
                                                                   10, 50000);
-                       if (err)
+                       if (err) {
                                dev_err((struct device *)priv->rng.priv,
                                        "%s: timeout %x!\n", __func__, sr);
+                               break;
+                       }
+               } else if (!sr) {
+                       /* The FIFO is being filled up */
+                       break;
                }
 
-               /* If error detected or data not ready... */
                if (sr != RNG_SR_DRDY) {
-                       if (WARN_ONCE(sr & (RNG_SR_SEIS | RNG_SR_CEIS),
-                                       "bad RNG status - %x\n", sr))
+                       if (sr & RNG_SR_SEIS) {
+                               err = stm32_rng_conceal_seed_error(rng);
+                               i++;
+                               if (err && i > RNG_NB_RECOVER_TRIES) {
+                                       dev_err((struct device *)priv->rng.priv,
+                                               "Couldn't recover from seed error\n");
+                                       return -ENOTRECOVERABLE;
+                               }
+
+                               continue;
+                       }
+
+                       if (WARN_ONCE((sr & RNG_SR_CEIS), "RNG clock too slow - %x\n", sr))
                                writel_relaxed(0, priv->base + RNG_SR);
-                       break;
                }
 
+               /* Late seed error case: DR being 0 is an error status */
                *(u32 *)data = readl_relaxed(priv->base + RNG_DR);
+               if (!*(u32 *)data) {
+                       err = stm32_rng_conceal_seed_error(rng);
+                       i++;
+                       if (err && i > RNG_NB_RECOVER_TRIES) {
+                               dev_err((struct device *)priv->rng.priv,
+                                       "Couldn't recover from seed error");
+                               return -ENOTRECOVERABLE;
+                       }
+
+                       continue;
+               }
 
+               i = 0;
                retval += sizeof(u32);
                data += sizeof(u32);
                max -= sizeof(u32);