diff --git a/drivers/entropy/Kconfig.nrf5 b/drivers/entropy/Kconfig.nrf5 index 9d1273163be..b02a49ffaee 100644 --- a/drivers/entropy/Kconfig.nrf5 +++ b/drivers/entropy/Kconfig.nrf5 @@ -25,8 +25,17 @@ config ENTROPY_NRF5_BIAS_CORRECTION uniform distribution of 0 and 1. When this option is enabled, the time to generate a byte cannot be guaranteed. +config ENTROPY_NRF5_THR_POOL_SIZE + int "Thread-mode random number pool size" + range ENTROPY_NRF5_THR_THRESHOLD 256 + default 8 + help + Buffer length in bytes used to store entropy bytes generated by the + hardware to make them ready for thread mode consumers. + Please note, that size of the pool must be a power of 2. + config ENTROPY_NRF5_THR_THRESHOLD - int "Thread-mode low-water threshold" + int "Thread-mode random number pool low-water threshold" range 4 255 help Low water-mark threshold in bytes to trigger entropy generation for @@ -34,8 +43,17 @@ config ENTROPY_NRF5_THR_THRESHOLD buffer goes below this number hardware entropy generation will be started. +config ENTROPY_NRF5_ISR_POOL_SIZE + int "ISR-mode random number pool size" + range ENTROPY_NRF5_ISR_THRESHOLD 256 + default 16 + help + Buffer length in bytes used to store entropy bytes generated by the + hardware to make them ready for ISR consumers. + Please note, that size of the pool must be a power of 2. + config ENTROPY_NRF5_ISR_THRESHOLD - int "ISR low-water threshold" + int "ISR-mode random number pool low-water threshold" range 12 255 help Low water-mark threshold in bytes to trigger entropy generation for @@ -43,19 +61,6 @@ config ENTROPY_NRF5_ISR_THRESHOLD buffer goes below this number hardware entropy generation will be started. -config ENTROPY_NRF5_THR_BUF_LEN - int "Thread-mode buffer length" - range ENTROPY_NRF5_THR_THRESHOLD 255 - help - Buffer length in bytes used to store entropy bytes generated by the - hardware to make them ready for thread mode consumers. - -config ENTROPY_NRF5_ISR_BUF_LEN - int "ISR buffer length" - range ENTROPY_NRF5_ISR_THRESHOLD 255 - help - Buffer length in bytes used to store entropy bytes generated by the - hardware to make them ready for ISR consumers. config ENTROPY_NRF5_PRI int "RNG interrupt priority" diff --git a/drivers/entropy/entropy_nrf5.c b/drivers/entropy/entropy_nrf5.c index 32341349f43..2d15ae06aa3 100644 --- a/drivers/entropy/entropy_nrf5.c +++ b/drivers/entropy/entropy_nrf5.c @@ -64,24 +64,30 @@ */ struct rng_pool { - u8_t count; - u8_t threshold; - u8_t first; + u8_t first_alloc; + u8_t first_read; u8_t last; - u8_t rand[0]; + u8_t mask; + u8_t threshold; + u8_t buffer[0]; }; -#define RNG_POOL_DEFINE(name, len) u8_t name[sizeof(struct rng_pool) + len] __aligned(4) +#define RNG_POOL_DEFINE(name, len) u8_t name[sizeof(struct rng_pool) + (len)] -#define RNG_POOL_ISR_LEN (CONFIG_ENTROPY_NRF5_ISR_BUF_LEN + 1) -#define RNG_POOL_THR_LEN (CONFIG_ENTROPY_NRF5_THR_BUF_LEN + 1) +BUILD_ASSERT_MSG((CONFIG_ENTROPY_NRF5_ISR_POOL_SIZE & + (CONFIG_ENTROPY_NRF5_ISR_POOL_SIZE - 1)) == 0, + "The CONFIG_ENTROPY_NRF5_ISR_POOL_SIZE must be a power of 2!"); + +BUILD_ASSERT_MSG((CONFIG_ENTROPY_NRF5_THR_POOL_SIZE & + (CONFIG_ENTROPY_NRF5_THR_POOL_SIZE - 1)) == 0, + "The CONFIG_ENTROPY_NRF5_THR_POOL_SIZE must be a power of 2!"); struct entropy_nrf5_dev_data { struct k_sem sem_lock; struct k_sem sem_sync; - RNG_POOL_DEFINE(isr, RNG_POOL_ISR_LEN); - RNG_POOL_DEFINE(thr, RNG_POOL_THR_LEN); + RNG_POOL_DEFINE(isr, CONFIG_ENTROPY_NRF5_ISR_POOL_SIZE); + RNG_POOL_DEFINE(thr, CONFIG_ENTROPY_NRF5_THR_POOL_SIZE); }; #define DEV_DATA(dev) \ @@ -108,121 +114,87 @@ static int random_byte_get(void) #if defined(CONFIG_BT_CTLR_FAST_ENC) #pragma GCC optimize ("Ofast") #endif -static inline u8_t rng_pool_get(struct rng_pool *rng, u8_t octets, u8_t *rand) +static u16_t rng_pool_get(struct rng_pool *rngp, u8_t *buf, u16_t len) { - u8_t first, last, avail, remaining, *d, *s; + u32_t last = rngp->last; + u32_t mask = rngp->mask; + u8_t *dst = buf; + u32_t first, available; + u32_t other_read_in_progress; + unsigned int key; - __ASSERT_NO_MSG(rng); + key = irq_lock(); + first = rngp->first_alloc; - first = rng->first; - last = rng->last; + /* + * The other_read_in_progress is non-zero if rngp->first_read != first, + * which means that lower-priority code (which was interrupted by this + * call) already allocated area for read. + */ + other_read_in_progress = (rngp->first_read ^ first); - d = &rand[octets]; - s = &rng->rand[first]; - - if (first <= last) { - /* copy octets from contiguous memory */ - avail = last - first; - if (octets < avail) { - remaining = avail - octets; - avail = octets; - } else { - remaining = 0; - } - - first += avail; - octets -= avail; - - while (avail--) { - *(--d) = *s++; - } - - rng->first = first; - } else { - /* copy octets from split halves - until end of array */ - avail = rng->count - first; - if (octets < avail) { - remaining = avail + last - octets; - avail = octets; - first += avail; - } else { - remaining = last; - first = 0; - } - - octets -= avail; - - while (avail--) { - *(--d) = *s++; - } - - /* copy from beginning of array - until ring buffer last idx */ - if (octets && last) { - s = &rng->rand[0]; - - if (octets < last) { - remaining = last - octets; - last = octets; - } else { - remaining = 0; - } - - first = last; - octets -= last; - - while (last--) { - *(--d) = *s++; - } - } - - rng->first = first; + available = (last - first) & mask; + if (available < len) { + len = available; } - if (remaining < rng->threshold) { + /* + * Move alloc index forward to signal, that part of the buffer is + * now reserved for this call. + */ + rngp->first_alloc = (first + len) & mask; + irq_unlock(key); + + while (likely(len--)) { + *dst++ = rngp->buffer[first]; + first = (first + 1) & mask; + } + + /* + * If this call is the last one accessing the pool, move read index + * to signal that all allocated regions are now read and could be + * overwritten. + */ + if (likely(!other_read_in_progress)) { + key = irq_lock(); + rngp->first_read = rngp->first_alloc; + irq_unlock(key); + } + + len = dst - buf; + available = available - len; + if (available <= rngp->threshold) { nrf_rng_task_trigger(NRF_RNG_TASK_START); } - return octets; + return len; } #pragma GCC pop_options -static int rng_pool_put(struct rng_pool *rng, bool store, u8_t byte) +static int rng_pool_put(struct rng_pool *rngp, u8_t byte) { - u8_t last; + u8_t first = rngp->first_read; + u8_t last = rngp->last; + u8_t mask = rngp->mask; - if (!rng) { + /* Signal error if the pool is full. */ + if (((last - first) & mask) == mask) { return -ENOBUFS; } - last = rng->last + 1; - if (last == rng->count) { - last = 0; - } + rngp->buffer[last] = byte; + rngp->last = (last + 1) & mask; - if (last == rng->first) { - /* this condition should not happen, but due to probable race, - * new value could be generated before NRF_RNG task is stopped. - */ - return -ENOBUFS; - } + return 0; +} - if (!store) { - return -EBUSY; - } - - rng->rand[rng->last] = byte; - rng->last = last; - - last = rng->last + 1; - if (last == rng->count) { - last = 0; - } - - if (last == rng->first) { - return 0; - } - - return -EBUSY; +static void rng_pool_init(struct rng_pool *rngp, u16_t size, u8_t threshold) +{ + rngp->first_alloc = 0; + rngp->first_read = 0; + rngp->last = 0; + rngp->mask = size - 1; + rngp->threshold = threshold; } static void isr(void *arg) @@ -235,23 +207,15 @@ static void isr(void *arg) return; } - ret = rng_pool_put((struct rng_pool *)dev_data->isr, true, byte); - if (ret != -EBUSY) { - ret = rng_pool_put((struct rng_pool *)dev_data->thr, - (ret == -ENOBUFS), byte); + ret = rng_pool_put((struct rng_pool *)dev_data->isr, byte); + if (ret < 0) { + ret = rng_pool_put((struct rng_pool *)dev_data->thr, byte); + if (ret < 0) { + nrf_rng_task_trigger(NRF_RNG_TASK_STOP); + } + k_sem_give(&dev_data->sem_sync); } - - if (ret != -EBUSY) { - nrf_rng_task_trigger(NRF_RNG_TASK_STOP); - } -} - -static void rng_pool_init(struct rng_pool *rng, u8_t len, u8_t threshold) -{ - rng->count = len; - rng->threshold = threshold; - rng->first = rng->last = 0; } static int entropy_nrf5_get_entropy(struct device *device, u8_t *buf, u16_t len) @@ -259,25 +223,21 @@ static int entropy_nrf5_get_entropy(struct device *device, u8_t *buf, u16_t len) struct entropy_nrf5_dev_data *dev_data = DEV_DATA(device); while (len) { - u8_t len8; + u16_t bytes; - if (len > UINT8_MAX) { - len8 = UINT8_MAX; - } else { - len8 = len; - } - len -= len8; + k_sem_take(&dev_data->sem_lock, K_FOREVER); + bytes = rng_pool_get((struct rng_pool *)dev_data->thr, + buf, len); + k_sem_give(&dev_data->sem_lock); - while (len8) { - k_sem_take(&dev_data->sem_lock, K_FOREVER); - len8 = rng_pool_get((struct rng_pool *)dev_data->thr, - len8, buf); - k_sem_give(&dev_data->sem_lock); - if (len8) { - /* Sleep until next interrupt */ - k_sem_take(&dev_data->sem_sync, K_FOREVER); - } + if (bytes == 0) { + /* Pool is empty: Sleep until next interrupt. */ + k_sem_take(&dev_data->sem_sync, K_FOREVER); + continue; } + + len -= bytes; + buf += bytes; } return 0; @@ -290,7 +250,7 @@ static int entropy_nrf5_get_entropy_isr(struct device *dev, u8_t *buf, u16_t len u16_t cnt = len; if (!(flags & ENTROPY_BUSYWAIT)) { - return rng_pool_get((struct rng_pool *)dev_data->isr, len, buf); + return rng_pool_get((struct rng_pool *)dev_data->isr, buf, len); } if (len) { @@ -351,13 +311,16 @@ static int entropy_nrf5_init(struct device *device) /* Locking semaphore initialized to 1 (unlocked) */ k_sem_init(&dev_data->sem_lock, 1, 1); + /* Synching semaphore */ k_sem_init(&dev_data->sem_sync, 0, 1); - rng_pool_init((struct rng_pool *)dev_data->thr, RNG_POOL_THR_LEN, - CONFIG_ENTROPY_NRF5_THR_THRESHOLD); - rng_pool_init((struct rng_pool *)dev_data->isr, RNG_POOL_ISR_LEN, - CONFIG_ENTROPY_NRF5_ISR_THRESHOLD); + rng_pool_init((struct rng_pool *)dev_data->thr, + CONFIG_ENTROPY_NRF5_THR_POOL_SIZE, + CONFIG_ENTROPY_NRF5_THR_THRESHOLD); + rng_pool_init((struct rng_pool *)dev_data->isr, + CONFIG_ENTROPY_NRF5_ISR_POOL_SIZE, + CONFIG_ENTROPY_NRF5_ISR_THRESHOLD); /* Enable or disable bias correction */ if (IS_ENABLED(CONFIG_ENTROPY_NRF5_BIAS_CORRECTION)) { @@ -380,6 +343,6 @@ static int entropy_nrf5_init(struct device *device) u8_t entropy_nrf_get_entropy_isr(struct device *dev, u8_t *buf, u8_t len) { ARG_UNUSED(dev); - return rng_pool_get((struct rng_pool *)entropy_nrf5_data.isr, len, buf); + return rng_pool_get((struct rng_pool *)entropy_nrf5_data.isr, buf, len); }