[PATCH v3 5/7] rng: stm32: add error concealment sequence
Patrice CHOTARD
patrice.chotard at foss.st.com
Wed Sep 27 09:26:41 CEST 2023
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.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
Apply on stm32/next
Patrice
More information about the U-Boot
mailing list