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