drivers: entropy: Port the LL rand driver to entropy
The rand driver present in the BLE Link Layer is able to provide entropy for both threads and ISRs, and so it is more suited to be used as a generic nRF5x entropy driver instead of the current one. This will allow applications and the Link Layer to use the same driver without duplicating it. Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no> Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
This commit is contained in:
parent
f7bf47010f
commit
813b41beec
3 changed files with 290 additions and 62 deletions
|
@ -1,21 +1,21 @@
|
||||||
# Kconfig.nrf5 - nRF5 entropy generator driver configuration
|
# Kconfig.nrf5 - nRF5 entropy generator driver configuration
|
||||||
#
|
#
|
||||||
# Copyright (c) 2017 Nordic Semiconductor ASA
|
# Copyright (c) 2018 Nordic Semiconductor ASA
|
||||||
# Copyright (c) 2017 Exati Tecnologia Ltda.
|
# Copyright (c) 2017 Exati Tecnologia Ltda.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
menuconfig ENTROPY_NRF5_RNG
|
menuconfig ENTROPY_NRF5_RNG
|
||||||
bool "nRF5 RNG driver"
|
bool "nRF5 RNG driver"
|
||||||
# FIXME: nRF5 RNG driver can't co-exist with Bluetooth's HAL
|
depends on ENTROPY_GENERATOR
|
||||||
# implementation yet
|
|
||||||
depends on ENTROPY_GENERATOR && !BT
|
|
||||||
select ENTROPY_HAS_DRIVER
|
select ENTROPY_HAS_DRIVER
|
||||||
help
|
help
|
||||||
This option enables the RNG peripheral, which is a random number
|
This option enables the RNG peripheral, which is a random number
|
||||||
generator, based on internal thermal noise, that provides a
|
generator, based on internal thermal noise, that provides a
|
||||||
random 8-bit value to the host when read.
|
random 8-bit value to the host when read.
|
||||||
|
|
||||||
|
if ENTROPY_NRF5_RNG
|
||||||
|
|
||||||
config ENTROPY_NRF5_BIAS_CORRECTION
|
config ENTROPY_NRF5_BIAS_CORRECTION
|
||||||
bool "Enable bias correction (uniform distribution)"
|
bool "Enable bias correction (uniform distribution)"
|
||||||
depends on ENTROPY_NRF5_RNG
|
depends on ENTROPY_NRF5_RNG
|
||||||
|
@ -23,3 +23,43 @@ config ENTROPY_NRF5_BIAS_CORRECTION
|
||||||
This option enables the RNG bias correction, which guarantees a
|
This option enables the RNG bias correction, which guarantees a
|
||||||
uniform distribution of 0 and 1. When this option is enabled, the time
|
uniform distribution of 0 and 1. When this option is enabled, the time
|
||||||
to generate a byte cannot be guaranteed.
|
to generate a byte cannot be guaranteed.
|
||||||
|
|
||||||
|
config ENTROPY_NRF5_THR_THRESHOLD
|
||||||
|
int "Thread-mode low-water threshold"
|
||||||
|
range 4 255
|
||||||
|
help
|
||||||
|
Low-water threshold in bytes to trigger entropy generation for
|
||||||
|
thread mode consumers.
|
||||||
|
|
||||||
|
config ENTROPY_NRF5_ISR_THRESHOLD
|
||||||
|
int "ISR low-water threshold"
|
||||||
|
range 12 255
|
||||||
|
help
|
||||||
|
Low-water threshold in bytes to trigger entropy generation for
|
||||||
|
ISR consumers.
|
||||||
|
|
||||||
|
config ENTROPY_NRF5_THR_BUF_LEN
|
||||||
|
int "Thread-mode buffer length"
|
||||||
|
range ENTROPY_NRF5_THR_THRESHOLD 255
|
||||||
|
help
|
||||||
|
Buffer length in bytes to pre-store entropy generation 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 to pre-store entropy generation for
|
||||||
|
ISR consumers.
|
||||||
|
|
||||||
|
config ENTROPY_NRF5_PRI
|
||||||
|
int "RNG interrupt priority"
|
||||||
|
depends on ENTROPY_NRF5_RNG
|
||||||
|
range 0 2 if SOC_SERIES_NRF51X
|
||||||
|
range 0 5 if SOC_SERIES_NRF52X
|
||||||
|
default 2 if SOC_SERIES_NRF51X
|
||||||
|
default 5 if SOC_SERIES_NRF52X
|
||||||
|
help
|
||||||
|
nRF5X RNG IRQ priority.
|
||||||
|
|
||||||
|
endif #ENTROPY_NRF5_RNG
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017 Nordic Semiconductor ASA
|
* Copyright (c) 2018 Nordic Semiconductor ASA
|
||||||
* Copyright (c) 2017 Exati Tecnologia Ltda.
|
* Copyright (c) 2017 Exati Tecnologia Ltda.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
@ -8,89 +8,214 @@
|
||||||
#include <entropy.h>
|
#include <entropy.h>
|
||||||
#include <atomic.h>
|
#include <atomic.h>
|
||||||
#include <nrf_rng.h>
|
#include <nrf_rng.h>
|
||||||
|
#include <soc.h>
|
||||||
|
|
||||||
|
struct rand {
|
||||||
|
u8_t count;
|
||||||
|
u8_t threshold;
|
||||||
|
u8_t first;
|
||||||
|
u8_t last;
|
||||||
|
u8_t rand[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define RAND_DEFINE(name, len) u8_t name[sizeof(struct rand) + len] __aligned(4)
|
||||||
|
|
||||||
|
#define RAND_THREAD_LEN (CONFIG_ENTROPY_NRF5_THR_BUF_LEN + 1)
|
||||||
|
#define RAND_ISR_LEN (CONFIG_ENTROPY_NRF5_ISR_BUF_LEN + 1)
|
||||||
|
|
||||||
struct entropy_nrf5_dev_data {
|
struct entropy_nrf5_dev_data {
|
||||||
atomic_t user_count;
|
struct k_sem sem_lock;
|
||||||
|
struct k_sem sem_sync;
|
||||||
|
|
||||||
|
RAND_DEFINE(thr, RAND_THREAD_LEN);
|
||||||
|
RAND_DEFINE(isr, RAND_ISR_LEN);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DEV_DATA(dev) \
|
#define DEV_DATA(dev) \
|
||||||
((struct entropy_nrf5_dev_data *)(dev)->driver_data)
|
((struct entropy_nrf5_dev_data *)(dev)->driver_data)
|
||||||
|
|
||||||
static inline u8_t entropy_nrf5_get_u8(void)
|
static u8_t get(struct rand *rng, u8_t octets, u8_t *rand)
|
||||||
{
|
{
|
||||||
while (!nrf_rng_event_get(NRF_RNG_EVENT_VALRDY)) {
|
u8_t first, last, avail, remaining, *d, *s;
|
||||||
__WFE();
|
|
||||||
__SEV();
|
__ASSERT_NO_MSG(rng);
|
||||||
__WFE();
|
|
||||||
|
first = rng->first;
|
||||||
|
last = rng->last;
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
nrf_rng_event_clear(NRF_RNG_EVENT_VALRDY);
|
|
||||||
|
|
||||||
/* Clear the Pending status of the RNG interrupt so that it could
|
if (remaining < rng->threshold) {
|
||||||
* wake up the core when the VALRDY event occurs again. */
|
NRF_RNG->TASKS_START = 1;
|
||||||
NVIC_ClearPendingIRQ(RNG_IRQn);
|
#if defined(CONFIG_BOARD_NRFXX_NWTSIM)
|
||||||
|
NRF_RNG_regw_sideeffects();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
return nrf_rng_random_value_get();
|
return octets;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isr(struct rand *rng, bool store)
|
||||||
|
{
|
||||||
|
u8_t last;
|
||||||
|
|
||||||
|
if (!rng) {
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = rng->last + 1;
|
||||||
|
if (last == rng->count) {
|
||||||
|
last = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!store) {
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
rng->rand[rng->last] = NRF_RNG->VALUE;
|
||||||
|
rng->last = last;
|
||||||
|
|
||||||
|
last = rng->last + 1;
|
||||||
|
if (last == rng->count) {
|
||||||
|
last = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last == rng->first) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void isr_rand(void *arg)
|
||||||
|
{
|
||||||
|
struct device *device = arg;
|
||||||
|
|
||||||
|
if (NRF_RNG->EVENTS_VALRDY) {
|
||||||
|
struct entropy_nrf5_dev_data *dev_data = DEV_DATA(device);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = isr((struct rand *)dev_data->isr, true);
|
||||||
|
if (ret != -EBUSY) {
|
||||||
|
ret = isr((struct rand *)dev_data->thr,
|
||||||
|
(ret == -ENOBUFS));
|
||||||
|
k_sem_give(&dev_data->sem_sync);
|
||||||
|
}
|
||||||
|
|
||||||
|
NRF_RNG->EVENTS_VALRDY = 0;
|
||||||
|
|
||||||
|
if (ret != -EBUSY) {
|
||||||
|
NRF_RNG->TASKS_STOP = 1;
|
||||||
|
#if defined(CONFIG_BOARD_NRFXX_NWTSIM)
|
||||||
|
NRF_RNG_regw_sideeffects();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init(struct rand *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)
|
static int entropy_nrf5_get_entropy(struct device *device, u8_t *buf, u16_t len)
|
||||||
{
|
{
|
||||||
/* Mark the peripheral as being used */
|
struct entropy_nrf5_dev_data *dev_data = DEV_DATA(device);
|
||||||
atomic_inc(&DEV_DATA(device)->user_count);
|
|
||||||
|
|
||||||
/* Disable the shortcut that stops the task after a byte is generated */
|
|
||||||
nrf_rng_shorts_disable(NRF_RNG_SHORT_VALRDY_STOP_MASK);
|
|
||||||
|
|
||||||
/* Start the RNG generator peripheral */
|
|
||||||
nrf_rng_task_trigger(NRF_RNG_TASK_START);
|
|
||||||
|
|
||||||
while (len) {
|
while (len) {
|
||||||
*buf = entropy_nrf5_get_u8();
|
u8_t len8;
|
||||||
buf++;
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Only stop the RNG generator peripheral if we're the last user */
|
if (len > UINT8_MAX) {
|
||||||
if (atomic_dec(&DEV_DATA(device)->user_count) == 1) {
|
len8 = UINT8_MAX;
|
||||||
/* Disable the peripheral on the next VALRDY event */
|
} else {
|
||||||
nrf_rng_shorts_enable(NRF_RNG_SHORT_VALRDY_STOP_MASK);
|
len8 = len;
|
||||||
|
}
|
||||||
|
len -= len8;
|
||||||
|
|
||||||
if (atomic_get(&DEV_DATA(device)->user_count) != 0) {
|
while (len8) {
|
||||||
/* Race condition: another thread started to use
|
k_sem_take(&dev_data->sem_lock, K_FOREVER);
|
||||||
* the peripheral while we were disabling it.
|
len8 = get((struct rand *)dev_data->thr, len8, buf);
|
||||||
* Enable the peripheral again
|
k_sem_give(&dev_data->sem_lock);
|
||||||
*/
|
if (len8) {
|
||||||
nrf_rng_shorts_disable(NRF_RNG_SHORT_VALRDY_STOP_MASK);
|
/* Sleep until next interrupt */
|
||||||
nrf_rng_task_trigger(NRF_RNG_TASK_START);
|
k_sem_take(&dev_data->sem_sync, K_FOREVER);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int entropy_nrf5_init(struct device *device)
|
|
||||||
{
|
|
||||||
/* Enable the RNG interrupt to be generated on the VALRDY event,
|
|
||||||
* but do not enable this interrupt in NVIC to be serviced.
|
|
||||||
* When the interrupt enters the Pending state it will set internal
|
|
||||||
* event (SEVONPEND is activated by kernel) and wake up the core
|
|
||||||
* if it was suspended by WFE. And that's enough. */
|
|
||||||
nrf_rng_int_enable(NRF_RNG_INT_VALRDY_MASK);
|
|
||||||
NVIC_ClearPendingIRQ(RNG_IRQn);
|
|
||||||
|
|
||||||
/* Enable or disable bias correction */
|
|
||||||
if (IS_ENABLED(CONFIG_ENTROPY_NRF5_BIAS_CORRECTION)) {
|
|
||||||
nrf_rng_error_correction_enable();
|
|
||||||
} else {
|
|
||||||
nrf_rng_error_correction_disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize the user count with zero */
|
|
||||||
atomic_clear(&DEV_DATA(device)->user_count);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct entropy_nrf5_dev_data entropy_nrf5_data;
|
static struct entropy_nrf5_dev_data entropy_nrf5_data;
|
||||||
|
static int entropy_nrf5_init(struct device *device);
|
||||||
|
|
||||||
static const struct entropy_driver_api entropy_nrf5_api_funcs = {
|
static const struct entropy_driver_api entropy_nrf5_api_funcs = {
|
||||||
.get_entropy = entropy_nrf5_get_entropy
|
.get_entropy = entropy_nrf5_get_entropy
|
||||||
|
@ -100,3 +225,45 @@ DEVICE_AND_API_INIT(entropy_nrf5, CONFIG_ENTROPY_NAME,
|
||||||
entropy_nrf5_init, &entropy_nrf5_data, NULL,
|
entropy_nrf5_init, &entropy_nrf5_data, NULL,
|
||||||
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
||||||
&entropy_nrf5_api_funcs);
|
&entropy_nrf5_api_funcs);
|
||||||
|
|
||||||
|
static int entropy_nrf5_init(struct device *device)
|
||||||
|
{
|
||||||
|
struct entropy_nrf5_dev_data *dev_data = DEV_DATA(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);
|
||||||
|
|
||||||
|
init((struct rand *)dev_data->thr, RAND_THREAD_LEN,
|
||||||
|
CONFIG_ENTROPY_NRF5_THR_THRESHOLD);
|
||||||
|
init((struct rand *)dev_data->isr, RAND_ISR_LEN,
|
||||||
|
CONFIG_ENTROPY_NRF5_ISR_THRESHOLD);
|
||||||
|
|
||||||
|
/* Enable or disable bias correction */
|
||||||
|
if (IS_ENABLED(CONFIG_ENTROPY_NRF5_BIAS_CORRECTION)) {
|
||||||
|
NRF_RNG->CONFIG |= RNG_CONFIG_DERCEN_Msk;
|
||||||
|
} else {
|
||||||
|
NRF_RNG->CONFIG &= ~RNG_CONFIG_DERCEN_Msk;
|
||||||
|
}
|
||||||
|
|
||||||
|
NRF_RNG->EVENTS_VALRDY = 0;
|
||||||
|
NRF_RNG->INTENSET = RNG_INTENSET_VALRDY_Msk;
|
||||||
|
|
||||||
|
NRF_RNG->TASKS_START = 1;
|
||||||
|
#if defined(CONFIG_BOARD_NRFXX_NWTSIM)
|
||||||
|
NRF_RNG_regw_sideeffects();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
IRQ_CONNECT(NRF5_IRQ_RNG_IRQn, CONFIG_ENTROPY_NRF5_PRI, isr_rand,
|
||||||
|
DEVICE_GET(entropy_nrf5), 0);
|
||||||
|
irq_enable(NRF5_IRQ_RNG_IRQn);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8_t entropy_get_entropy_isr(struct device *dev, u8_t *buf, u8_t len)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(dev);
|
||||||
|
return get((struct rand *)entropy_nrf5_data.isr, len, buf);
|
||||||
|
}
|
||||||
|
|
21
include/drivers/entropy/nrf5_entropy.h
Normal file
21
include/drivers/entropy/nrf5_entropy.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 Nordic Semiconductor ASA
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _NRF5_ENTROPY_H_
|
||||||
|
#define _NRF5_ENTROPY_H_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fills a buffer with entropy in a non-blocking manner.
|
||||||
|
* Callable from ISRs.
|
||||||
|
*
|
||||||
|
* @param dev Pointer to the device structure.
|
||||||
|
* @param buf Buffer to fill with entropy.
|
||||||
|
* @param len Buffer length.
|
||||||
|
* @retval number of bytes filled with entropy.
|
||||||
|
*/
|
||||||
|
u8_t entropy_get_entropy_isr(struct device *dev, u8_t *buf, u8_t len);
|
||||||
|
|
||||||
|
#endif /* _NRF5_ENTROPY_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue