[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, &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.chotard at foss.st.com>

Thanks
Patrice


More information about the U-Boot mailing list