On 9/27/23 08:57, Patrice CHOTARD wrote:
> 
> 
> 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.chevall...@foss.st.com>
>> Reviewed-by: Patrick Delaunay <patrick.delau...@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, &reg, 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, &reg, increment);
>> +            data += increment;
>> +            len -= increment;
>> +
>> +            tries = 0;
>>      }
>>  
>>      return 0;
> 
> Reviewed-by: Patrice Chotard <patrice.chot...@foss.st.com>
> 
> Thanks
> Patrice
Apply on stm32/next

Patrice

Reply via email to