1 /*
2 * Copyright (c) 2021, Commonwealth Scientific and Industrial Research
3 * Organisation (CSIRO) ABN 41 687 119 230.
4 *
5 * SPDX-License-Identifier: CC0-1.0
6 *
7 * Based on code written in 2019 by David Blackman and Sebastiano Vigna
8 * (vigna@acm.org)
9 *
10 * To the extent possible under law, the author has dedicated all copyright
11 * and related and neighboring rights to this software to the public domain
12 * worldwide. This software is distributed without any warranty.
13 *
14 * See <http://creativecommons.org/publicdomain/zero/1.0/>.
15 *
16 * From: https://prng.di.unimi.it/xoshiro128plusplus.c
17 *
18 * This is xoshiro128++ 1.0, one of our 32-bit all-purpose, rock-solid
19 * generators. It has excellent speed, a state size (128 bits) that is
20 * large enough for mild parallelism, and it passes all tests we are aware
21 * of.
22 *
23 * For generating just single-precision (i.e., 32-bit) floating-point
24 * numbers, xoshiro128+ is even faster.
25 *
26 * The state must be seeded so that it is not everywhere zero.
27 */
28
29 #include <zephyr/init.h>
30 #include <zephyr/device.h>
31 #include <zephyr/drivers/entropy.h>
32 #include <zephyr/kernel.h>
33 #include <string.h>
34
35 static const struct device *const entropy_driver =
36 DEVICE_DT_GET(DT_CHOSEN(zephyr_entropy));
37 static uint32_t state[4];
38 static bool initialized;
39
rotl(const uint32_t x,int k)40 static inline uint32_t rotl(const uint32_t x, int k)
41 {
42 return (x << k) | (x >> (32 - k));
43 }
44
xoshiro128_initialize(void)45 static int xoshiro128_initialize(void)
46 {
47 if (!device_is_ready(entropy_driver)) {
48 return -ENODEV;
49 }
50 return 0;
51 }
52
xoshiro128_init_state(void)53 static void xoshiro128_init_state(void)
54 {
55 int rc;
56
57 /* This is not thread safe but it doesn't matter as we will just end
58 * up with a mix of random bytes from both threads.
59 */
60 rc = entropy_get_entropy(entropy_driver, (uint8_t *)&state, sizeof(state));
61 if (rc == 0) {
62 initialized = true;
63 } else {
64 /* Entropy device failed or is not yet ready.
65 * Reseed the PRNG state with pseudo-random data until it can
66 * be properly seeded. This may be needed if random numbers are
67 * requested before the backing entropy device has been enabled.
68 */
69 state[0] = k_cycle_get_32();
70 state[1] = k_cycle_get_32() ^ 0x9b64c2b0;
71 state[2] = k_cycle_get_32() ^ 0x86d3d2d4;
72 state[3] = k_cycle_get_32() ^ 0xa00ae278;
73 }
74 }
75
xoshiro128_next(void)76 static uint32_t xoshiro128_next(void)
77 {
78 const uint32_t result = rotl(state[0] + state[3], 7) + state[0];
79
80 const uint32_t t = state[1] << 9;
81
82 state[2] ^= state[0];
83 state[3] ^= state[1];
84 state[1] ^= state[2];
85 state[0] ^= state[3];
86
87 state[2] ^= t;
88
89 state[3] = rotl(state[3], 11);
90
91 return result;
92 }
93
z_impl_sys_rand_get(void * dst,size_t outlen)94 void z_impl_sys_rand_get(void *dst, size_t outlen)
95 {
96 size_t blocks = outlen / sizeof(uint32_t);
97 size_t rem = (outlen - (blocks * sizeof(uint32_t)));
98 uint32_t *unaligned = dst;
99 uint32_t ret;
100
101 if (unlikely(!initialized)) {
102 xoshiro128_init_state();
103 }
104
105 /* Write all full 32bit chunks */
106 while (blocks--) {
107 UNALIGNED_PUT(xoshiro128_next(), unaligned++);
108 }
109 /* Write trailing bytes */
110 if (rem) {
111 ret = xoshiro128_next();
112 memcpy(unaligned, &ret, rem);
113 }
114 }
115
116 /* In-tree entropy drivers will initialize in PRE_KERNEL_1; ensure that they're
117 * initialized properly before initializing ourselves.
118 */
119 SYS_INIT(xoshiro128_initialize, PRE_KERNEL_2,
120 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
121