[PATCH v3 5/7] rng: stm32: add error concealment sequence
Patrice CHOTARD
patrice.chotard at foss.st.com
Wed Sep 27 08:57:27 CEST 2023
On 9/19/23 17:27, Gatien Chevallier wrote:
> Seed errors can occur when using the hardware RNG. Implement the
> sequences to handle them. This avoids irrecoverable RNG state.
>
> 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 at foss.st.com>
> Reviewed-by: Patrick Delaunay <patrick.delaunay at foss.st.com>
> ---
> Changes in V2:
> - Added Patrick's tag
>
> drivers/rng/stm32_rng.c | 163 ++++++++++++++++++++++++++++++++++------
> 1 file changed, 140 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/rng/stm32_rng.c b/drivers/rng/stm32_rng.c
> index f943acd7d2..b1a790b217 100644
> --- a/drivers/rng/stm32_rng.c
> +++ b/drivers/rng/stm32_rng.c
> @@ -32,6 +32,8 @@
>
> #define RNG_DR 0x08
>
> +#define RNG_NB_RECOVER_TRIES 3
> +
> /*
> * struct stm32_rng_data - RNG compat data
> *
> @@ -52,45 +54,160 @@ struct stm32_rng_plat {
> bool ced;
> };
>
> +/*
> + * Extracts from the STM32 RNG specification when RNG supports CONDRST.
> + *
> + * When a noise source (or seed) error occurs, the RNG stops generating
> + * random numbers and sets to “1” both SEIS and SECS bits to indicate
> + * that a seed error occurred. (...)
> + *
> + * 1. Software reset by writing CONDRST at 1 and at 0 (see bitfield
> + * description for details). This step is needed only if SECS is set.
> + * Indeed, when SEIS is set and SECS is cleared it means RNG performed
> + * the reset automatically (auto-reset).
> + * 2. If SECS was set in step 1 (no auto-reset) wait for CONDRST
> + * to be cleared in the RNG_CR register, then confirm that SEIS is
> + * cleared in the RNG_SR register. Otherwise just clear SEIS bit in
> + * the RNG_SR register.
> + * 3. If SECS was set in step 1 (no auto-reset) wait for SECS to be
> + * cleared by RNG. The random number generation is now back to normal.
> + */
> +static int stm32_rng_conceal_seed_error_cond_reset(struct stm32_rng_plat *pdata)
> +{
> + u32 sr = readl_relaxed(pdata->base + RNG_SR);
> + u32 cr = readl_relaxed(pdata->base + RNG_CR);
> + int err;
> +
> + if (sr & RNG_SR_SECS) {
> + /* Conceal by resetting the subsystem (step 1.) */
> + writel_relaxed(cr | RNG_CR_CONDRST, pdata->base + RNG_CR);
> + writel_relaxed(cr & ~RNG_CR_CONDRST, pdata->base + RNG_CR);
> + } else {
> + /* RNG auto-reset (step 2.) */
> + writel_relaxed(sr & ~RNG_SR_SEIS, pdata->base + RNG_SR);
> + return 0;
> + }
> +
> + err = readl_relaxed_poll_timeout(pdata->base + RNG_SR, sr, !(sr & RNG_CR_CONDRST), 100000);
> + if (err) {
> + log_err("%s: timeout %x\n", __func__, sr);
> + return err;
> + }
> +
> + /* Check SEIS is cleared (step 2.) */
> + if (readl_relaxed(pdata->base + RNG_SR) & RNG_SR_SEIS)
> + return -EINVAL;
> +
> + err = readl_relaxed_poll_timeout(pdata->base + RNG_SR, sr, !(sr & RNG_SR_SECS), 100000);
> + if (err) {
> + log_err("%s: timeout %x\n", __func__, sr);
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Extracts from the STM32 RNG specification, when CONDRST is not supported
> + *
> + * When a noise source (or seed) error occurs, the RNG stops generating
> + * random numbers and sets to “1” both SEIS and SECS bits to indicate
> + * that a seed error occurred. (...)
> + *
> + * The following sequence shall be used to fully recover from a seed
> + * error after the RNG initialization:
> + * 1. Clear the SEIS bit by writing it to “0”.
> + * 2. Read out 12 words from the RNG_DR register, and discard each of
> + * them in order to clean the pipeline.
> + * 3. Confirm that SEIS is still cleared. Random number generation is
> + * back to normal.
> + */
> +static int stm32_rng_conceal_seed_error_sw_reset(struct stm32_rng_plat *pdata)
> +{
> + uint i = 0;
> + u32 sr = readl_relaxed(pdata->base + RNG_SR);
> +
> + writel_relaxed(sr & ~RNG_SR_SEIS, pdata->base + RNG_SR);
> +
> + for (i = 12; i != 0; i--)
> + (void)readl_relaxed(pdata->base + RNG_DR);
> +
> + if (readl_relaxed(pdata->base + RNG_SR) & RNG_SR_SEIS)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int stm32_rng_conceal_seed_error(struct stm32_rng_plat *pdata)
> +{
> + log_debug("Concealing RNG seed error\n");
> +
> + if (pdata->data->has_cond_reset)
> + return stm32_rng_conceal_seed_error_cond_reset(pdata);
> + else
> + return stm32_rng_conceal_seed_error_sw_reset(pdata);
> +};
> +
> static int stm32_rng_read(struct udevice *dev, void *data, size_t len)
> {
> - int retval, i;
> - u32 sr, count, reg;
> + int retval;
> + u32 sr, reg;
> size_t increment;
> struct stm32_rng_plat *pdata = dev_get_plat(dev);
> + uint tries = 0;
>
> while (len > 0) {
> retval = readl_poll_timeout(pdata->base + RNG_SR, sr,
> - sr & RNG_SR_DRDY, 10000);
> - if (retval)
> + sr, 10000);
> + if (retval) {
> + log_err("%s: Timeout RNG no data", __func__);
> return retval;
> + }
>
> - if (sr & (RNG_SR_SEIS | RNG_SR_SECS)) {
> - /* As per SoC TRM */
> - clrbits_le32(pdata->base + RNG_SR, RNG_SR_SEIS);
> - for (i = 0; i < 12; i++)
> - readl(pdata->base + RNG_DR);
> - if (readl(pdata->base + RNG_SR) & RNG_SR_SEIS) {
> - log_err("RNG Noise");
> - return -EIO;
> + if (sr != RNG_SR_DRDY) {
> + if (sr & RNG_SR_SEIS) {
> + retval = stm32_rng_conceal_seed_error(pdata);
> + tries++;
> + if (retval || tries > RNG_NB_RECOVER_TRIES) {
> + log_err("%s: Couldn't recover from seed error", __func__);
> + return -ENOTRECOVERABLE;
> + }
> +
> + /* Start again */
> + continue;
> + }
> +
> + if (sr & RNG_SR_CEIS) {
> + log_info("RNG clock too slow");
> + writel_relaxed(0, pdata->base + RNG_SR);
> }
> - /* start again */
> - continue;
> }
>
> /*
> * Once the DRDY bit is set, the RNG_DR register can
> - * be read four consecutive times.
> + * be read up to four consecutive times.
> */
> - count = 4;
> - while (len && count) {
> - reg = readl(pdata->base + RNG_DR);
> - memcpy(data, ®, min(len, sizeof(u32)));
> - increment = min(len, sizeof(u32));
> - data += increment;
> - len -= increment;
> - count--;
> + reg = readl(pdata->base + RNG_DR);
> + /* Late seed error case: DR being 0 is an error status */
> + if (!reg) {
> + retval = stm32_rng_conceal_seed_error(pdata);
> + tries++;
> +
> + if (retval || tries > RNG_NB_RECOVER_TRIES) {
> + log_err("%s: Couldn't recover from seed error", __func__);
> + return -ENOTRECOVERABLE;
> + }
> +
> + /* Start again */
> + continue;
> }
> +
> + increment = min(len, sizeof(u32));
> + memcpy(data, ®, increment);
> + data += increment;
> + len -= increment;
> +
> + tries = 0;
> }
>
> return 0;
Reviewed-by: Patrice Chotard <patrice.chotard at foss.st.com>
Thanks
Patrice
More information about the U-Boot
mailing list