diff --git a/drivers/dev_urandom.c b/drivers/dev_urandom.c index 28bad840b46..e7022cb9f54 100644 --- a/drivers/dev_urandom.c +++ b/drivers/dev_urandom.c @@ -67,21 +67,19 @@ #endif #ifdef CONFIG_DEV_URANDOM_XORSHIFT128 -# define PRNG() xorshift128() +# define PRNG() do_xorshift128() #else /* CONFIG_DEV_URANDOM_CONGRUENTIAL */ -# define PRNG() congruential() +# define PRNG() do_congruential() #endif /**************************************************************************** * Private Types ****************************************************************************/ -struct xorshift128_state_s +union xorshift128_state_u { - uint32_t x; - uint32_t y; - uint32_t z; - uint32_t w; + struct xorshift128_state_s state; + uint8_t u[16]; }; /**************************************************************************** @@ -119,16 +117,31 @@ static const struct file_operations g_urand_fops = #endif }; +#ifdef CONFIG_DEV_URANDOM_XORSHIFT128 +static union xorshift128_state_u g_prng; +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** - * Name: congruential + * Name: do_xorshift128 + ****************************************************************************/ + +#ifdef CONFIG_DEV_URANDOM_XORSHIFT128 +static inline uint32_t do_xorshift128(void) +{ + return xorshift128(&g_prng.state); +} +#endif + +/**************************************************************************** + * Name: do_congruential ****************************************************************************/ #ifdef CONFIG_DEV_URANDOM_CONGRUENTIAL -static uint32_t congruential(void) +static inline uint32_t do_congruential(void) { /* REVISIT: We could probably generate a 32-bit value with a single * call to nrand(). @@ -211,26 +224,14 @@ static ssize_t devurand_write(FAR struct file *filep, FAR const char *buffer, #ifdef CONFIG_DEV_URANDOM_CONGRUENTIAL unsigned int seed = 0; - if (len < sizeof(unsigned int)) - { - return -ERANGE; - } - - memcpy(&seed, buffer, sizeof(unsigned int)); + len = min(len, sizeof(unsigned int)); + memcpy(&seed, buffer, len); srand(seed); - return sizeof(unsigned int); - + return len; #else - struct xorshift128_state_s seed; - - if (len < sizeof(struct xorshift128_state_s)) - { - return -ERANGE; - } - - memcpy(&seed, buffer, sizeof(struct xorshift128_state_s)); - xorshift128_seed(seed.w, seed.x, seed.y, seed.z); - return sizeof(struct xorshift128_state_s); + len = min(len, sizeof(g_prng.u)); + memcpy(&g_prng.u, buffer, len); + return len; #endif } @@ -269,12 +270,15 @@ static int devurand_poll(FAR struct file *filep, FAR struct pollfd *fds, void devurandom_register(void) { + /* Seed the PRNG */ + #ifdef CONFIG_DEV_URANDOM_CONGRUENTIAL srand(10197); #else - /* Seed the PRNG */ - - xorshift128_seed(97, 101, 97 << 17, 101 << 25); + g_prng.state.w = 97; + g_prng.state.x = 101; + g_prng.state.y = g_prng.state.w << 17; + g_prng.state.z = g_prng.state.x << 25; #endif (void)register_driver("/dev/urandom", &g_urand_fops, 0666, NULL); diff --git a/include/nuttx/lib/xorshift128.h b/include/nuttx/lib/xorshift128.h index f2952d44d70..b2eec4fc2b8 100644 --- a/include/nuttx/lib/xorshift128.h +++ b/include/nuttx/lib/xorshift128.h @@ -46,6 +46,28 @@ #include +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Default XorShift128 state initializer */ + +#define XORSHIFT128_INITIALIZER { 97, 101, 97 << 17, 101 << 25 } + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Provides the state of the XorShift128 PRNG */ + +struct xorshift128_state_s +{ + uint32_t x; + uint32_t y; + uint32_t z; + uint32_t w; +}; + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ @@ -58,37 +80,27 @@ extern "C" #define EXTERN extern #endif -/**************************************************************************** - * Name: xorshift128_seed - * - * Description: - * Seed the XorShift128 PRNG - * - * Input Parameters: - * w, x, y, z: Values for XorShift128 generation state. - * - * Returned Value: - * No - * - ****************************************************************************/ - -void xorshift128_seed(uint32_t w, uint32_t x, uint32_t y, uint32_t z); - /**************************************************************************** * Name: xorshift128 * * Description: - * Generate one 32-bit pseudo-random number + * Generate one 32-bit pseudo-random number. + * + * NOTE: Because the PRNG state is passed as a parameter, this function is + * fully re-entrant and may be called from an interrupt handler. + * + * The downside to this is that users of the PRNG might not get as much + * entropy as if it were a common state structure. * * Input Parameters: - * None + * state - The current XorShift128 state. * * Returned Value: * The generated pseudo-random number * ****************************************************************************/ -uint32_t xorshift128(void); +uint32_t xorshift128(FAR struct xorshift128_state_s *state); #undef EXTERN #ifdef __cplusplus diff --git a/libc/misc/lib_xorshift128.c b/libc/misc/lib_xorshift128.c index e31867c572d..912740771e3 100644 --- a/libc/misc/lib_xorshift128.c +++ b/libc/misc/lib_xorshift128.c @@ -43,10 +43,11 @@ #include +#include #include -#include +#include -#include +#include /**************************************************************************** * Private Types @@ -56,119 +57,46 @@ # define CONFIG_CPULOAD_ONESHOT_ENTROPY 0 #endif -/**************************************************************************** - * Private Types - ****************************************************************************/ - -struct xorshift128_state_s -{ - uint32_t x; - uint32_t y; - uint32_t z; - uint32_t w; -}; - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -static struct xorshift128_state_s g_prng; - /**************************************************************************** * Public Functions ****************************************************************************/ -/**************************************************************************** - * Name: xorshift128_seed - * - * Description: - * Seed the XorShift128 PRNG - * - * Input Parameters: - * w, x, y, z: Values for XorShift128 generation state. - * - * Returned Value: - * No - * - ****************************************************************************/ - -void xorshift128_seed(uint32_t w, uint32_t x, uint32_t y, uint32_t z) -{ - /* With CONFIG_CPULOAD_ONESHOT_ENTROPY > 0, this PRNG could be called from - * the interrupt handling state (currently is not). - */ - -#if CONFIG_CPULOAD_ONESHOT_ENTROPY > 0 - irqstate_t flags; - - flags = enter_critical_section(); -#else - sched_lock(); -#endif - - /* Seed the PRNG */ - - g_prng.w = w; - g_prng.x = x; - g_prng.y = y; - g_prng.z = z; - -#if CONFIG_CPULOAD_ONESHOT_ENTROPY > 0 - leave_critical_section(flags); -#else - sched_unlock(); -#endif -} - /**************************************************************************** * Name: xorshift128 * * Description: - * Generate one 32-bit pseudo-random number + * Generate one 32-bit pseudo-random number. + * + * NOTE: Because the PRNG state is passed as a parameter, this function is + * fully re-entrant and may be called from an interrupt handler. + * + * The downside to this is that users of the PRNG might not get as much + * entropy as if it were a common state structure. * * Input Parameters: - * None + * state - The current XorShift128 state. * * Returned Value: * The generated pseudo-random number * ****************************************************************************/ -uint32_t xorshift128(void) +uint32_t xorshift128(FAR struct xorshift128_state_s *state) { - uint32_t ret; uint32_t t; - /* With CONFIG_CPULOAD_ONESHOT_ENTROPY > 0, this PRNG will be called from - * the interrupt handling state. - */ + DEBUGASSERT(state != NULL); -#if CONFIG_CPULOAD_ONESHOT_ENTROPY > 0 - irqstate_t flags; - - flags = enter_critical_section(); -#else - sched_lock(); -#endif - - t = g_prng.x; + t = state->x; t ^= t << 11; t ^= t >> 8; - g_prng.x = g_prng.y; - g_prng.y = g_prng.z; - g_prng.z = g_prng.w; + state->x = state->y; + state->y = state->z; + state->z = state->w; - g_prng.w ^= g_prng.w >> 19; - g_prng.w ^= t; + state->w ^= state->w >> 19; + state->w ^= t; - ret = g_prng.w; - -#if CONFIG_CPULOAD_ONESHOT_ENTROPY > 0 - leave_critical_section(flags); -#else - sched_unlock(); -#endif - - return ret; + return state->w; } diff --git a/sched/sched/sched_cpuload_oneshot.c b/sched/sched/sched_cpuload_oneshot.c index adf96311dff..fba9e43f33a 100644 --- a/sched/sched/sched_cpuload_oneshot.c +++ b/sched/sched/sched_cpuload_oneshot.c @@ -91,6 +91,7 @@ struct sched_oneshot_s { FAR struct oneshot_lowerhalf_s *oneshot; #if CONFIG_CPULOAD_ONESHOT_ENTROPY > 0 + struct xorshift128_state_s prng; int32_t maxdelay; int32_t error; #endif @@ -153,7 +154,7 @@ static void sched_oneshot_start(void) /* Add the random value in the range 0..(CPULOAD_ONESHOT_ENTRY - 1) */ - entropy = xorshift128(); + entropy = xorshift128(&g_sched_oneshot.prng); usecs += (int32_t)(entropy & CPULOAD_ONESHOT_ENTROPY_MASK); DEBUGASSERT(usecs > 0); /* Check for overflow to negative or zero */ @@ -272,7 +273,10 @@ void sched_oneshot_extclk(FAR struct oneshot_lowerhalf_s *lower) /* Seed the PRNG */ - xorshift128_seed(97, 101, 97 << 17, 101 << 25); + g_sched_oneshot.prng.w = 97; + g_sched_oneshot.prng.x = 101; + g_sched_oneshot.prng.y = g_sched_oneshot.prng.w << 17; + g_sched_oneshot.prng.z = g_sched_oneshot.prng.x << 25; #endif /* Then start the oneshot */