1 /*
2 * Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 /* xoroshiro128ss(), rotl():
8
9 Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
10
11 To the extent possible under law, the author has dedicated all copyright
12 and related and neighboring rights to this software to the public domain
13 worldwide. This software is distributed without any warranty.
14
15 See <http://creativecommons.org/publicdomain/zero/1.0/>
16
17 splitmix64() implementation:
18
19 Written in 2015 by Sebastiano Vigna (vigna@acm.org)
20 To the extent possible under law, the author has dedicated all copyright
21 and related and neighboring rights to this software to the public domain
22 worldwide. This software is distributed without any warranty.
23
24 See <http://creativecommons.org/publicdomain/zero/1.0/>
25 */
26
27 #include "pico/rand.h"
28 #include "pico/unique_id.h"
29 #include "pico/time.h"
30 #include "hardware/clocks.h"
31 #include "hardware/structs/rosc.h"
32 #include "hardware/structs/bus_ctrl.h"
33 #include "hardware/sync.h"
34
35 static bool rng_initialised = false;
36
37 // Note: By design, do not initialise any of the variables that hold entropy,
38 // they may have useful junk in them, either from power-up or a previous boot.
39 static rng_128_t __uninitialized_ram(rng_state);
40 #if PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH
41 static uint64_t __uninitialized_ram(ram_hash);
42 #endif
43
44 #if PICO_RAND_ENTROPY_SRC_ROSC | PICO_RAND_SEED_ENTROPY_SRC_ROSC
45 static uint64_t __uninitialized_ram(rosc_samples);
46 #endif
47
48 #if PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER
49 static uint8_t bus_counter_idx;
50 #endif
51
52 /* From the original source:
53
54 This is a fixed-increment version of Java 8's SplittableRandom generator
55 See http://dx.doi.org/10.1145/2714064.2660195 and
56 http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html
57
58 It is a very fast generator passing BigCrush, and it can be useful if
59 for some reason you absolutely want 64 bits of state; otherwise, we
60 rather suggest to use a xoroshiro128+ (for moderately parallel
61 computations) or xorshift1024* (for massively parallel computations)
62 generator.
63
64 Note: This can be called with any value (i.e. including 0)
65 */
splitmix64(uint64_t x)66 static __noinline uint64_t splitmix64(uint64_t x) {
67 uint64_t z = x + 0x9E3779B97F4A7C15ull;
68 z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ull;
69 z = (z ^ (z >> 27)) * 0x94D049BB133111EBull;
70 return z ^ (z >> 31);
71 }
72
73 /* From the original source:
74
75 This is xoroshiro128** 1.0, one of our all-purpose, rock-solid,
76 small-state generators. It is extremely (sub-ns) fast and it passes all
77 tests we are aware of, but its state space is large enough only for
78 mild parallelism.
79
80 For generating just floating-point numbers, xoroshiro128+ is even
81 faster (but it has a very mild bias, see notes in the comments).
82
83 The state must be seeded so that it is not everywhere zero. If you have
84 a 64-bit seed, we suggest to seed a splitmix64 generator and use its
85 output to fill s.
86 */
rotl(const uint64_t x,int k)87 static inline uint64_t rotl(const uint64_t x, int k) {
88 return (x << k) | (x >> (64 - k));
89 }
90
xoroshiro128ss(rng_128_t * local_rng_state)91 static __noinline uint64_t xoroshiro128ss(rng_128_t *local_rng_state) {
92 const uint64_t s0 = local_rng_state->r[0];
93 uint64_t s1 = local_rng_state->r[1];
94
95 // Because the state is *modified* outside of this function, there is a
96 // 1 in 2^128 chance that it could be all zeroes (which is not allowed).
97 while (s0 == 0 && s1 == 0) {
98 s1 = time_us_64(); // should not be 0, but loop anyway
99 }
100
101 const uint64_t result = rotl(s0 * 5, 7) * 9;
102
103 s1 ^= s0;
104 local_rng_state->r[0] = rotl(s0, 24) ^ s1 ^ (s1 << 16); // a, b
105 local_rng_state->r[1] = rotl(s1, 37); // c
106
107 return result;
108 }
109
110 #if PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH
sdbm_hash64_sram(uint64_t hash)111 static uint64_t sdbm_hash64_sram(uint64_t hash) {
112 // save some time by hashing a word at a time
113 for (uint i = (PICO_RAND_RAM_HASH_START + 3) & ~3; i < PICO_RAND_RAM_HASH_END; i+=4) {
114 uint32_t c = *(uint32_t *) i;
115 hash = (uint64_t) c + (hash << 6) + (hash << 16) - hash;
116 }
117 return hash;
118 }
119 #endif
120
121 #if PICO_RAND_SEED_ENTROPY_SRC_ROSC | PICO_RAND_ENTROPY_SRC_ROSC
122 /* gather an additional n bits of entropy, and shift them into the 64 bit entropy counter */
capture_additional_rosc_samples(uint n)123 static uint64_t capture_additional_rosc_samples(uint n) {
124 static absolute_time_t next_sample_time;
125
126 // provide an override if someone really wants it, but disabling ROSC as an entropy source makes more sense
127 #if !PICO_RAND_DISABLE_ROSC_CHECK
128 // check that the ROSC is running but that the processors are NOT running from it
129 hard_assert((rosc_hw->status & ROSC_STATUS_ENABLED_BITS) &&
130 ((clocks_hw->clk[clk_sys].ctrl & CLOCKS_CLK_SYS_CTRL_AUXSRC_BITS) != (CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_ROSC_CLKSRC << CLOCKS_CLK_SYS_CTRL_AUXSRC_LSB)));
131 #endif
132
133 bool in_exception = __get_current_exception();
134 assert(n); // save us having to special case samples for this
135 uint64_t samples = 0;
136 for(uint i=0; i<n; i++) {
137 bool bit_done = false;
138 do {
139 // Ensure that the ROSC random bit is not sampled too quickly,
140 // ROSC may be ticking only a few times a microsecond.
141 // Note: In general (i.e. sporadic) use, very often there will be no delay here.
142
143 // note this is not read under lock, so the two 32 bit halves could be skewed, but in that
144 // case we'll fail the check later, which is fine in this rare case
145 absolute_time_t cached_next_sample_time = next_sample_time;
146 // we support being called from IRQ, so be careful about sleeping... still not
147 // ideal, but not much that can be done
148 if (in_exception) {
149 busy_wait_until(next_sample_time);
150 } else {
151 sleep_until(next_sample_time);
152 }
153 spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_RAND);
154 uint32_t save = spin_lock_blocking(lock);
155 if (!absolute_time_diff_us(cached_next_sample_time, next_sample_time)) {
156 // we won the race (if any) for the bit, so we collect it locally
157 samples <<= 1;
158 samples |= rosc_hw->randombit & 1u;
159 // use of relative time to now, rather than offset from before makes things
160 // a bit less predictable at the cost of some speed.
161 next_sample_time = make_timeout_time_us(PICO_RAND_MIN_ROSC_BIT_SAMPLE_TIME_US);
162 bit_done = true;
163 if (i == n - 1) {
164 // samples has our random bits, so let's mix them in now
165 samples = rosc_samples = (rosc_samples << n) | samples;
166 }
167 }
168 spin_unlock(lock, save);
169 } while (!bit_done);
170 }
171 return samples;
172 }
173 #endif
174
initialise_rand(void)175 static void initialise_rand(void) {
176 rng_128_t local_rng_state = local_rng_state;
177 uint which = 0;
178 #if PICO_RAND_SEED_ENTROPY_SRC_RAM_HASH
179 ram_hash = sdbm_hash64_sram(ram_hash);
180 local_rng_state.r[which] ^= splitmix64(ram_hash);
181 which ^= 1;
182 #endif
183
184 #if PICO_RAND_SEED_ENTROPY_SRC_BOARD_ID
185 static_assert(PICO_UNIQUE_BOARD_ID_SIZE_BYTES == sizeof(uint64_t),
186 "Code below requires that 'board_id' is 64-bits in size");
187
188 // Note! The safety of the length assumption here is protected by a 'static_assert' above
189 union unique_id_u {
190 pico_unique_board_id_t board_id_native;
191 uint64_t board_id_u64;
192 } unique_id;
193 // Note! The safety of the length assumption here is protected by a 'static_assert' above
194 pico_get_unique_board_id(&unique_id.board_id_native);
195 local_rng_state.r[which] ^= splitmix64(unique_id.board_id_u64);
196 which ^= 1;
197 #endif
198
199 #if PICO_RAND_SEED_ENTROPY_SRC_ROSC
200 // this is really quite slow (10ms per iteration), and I'm not sure that it adds value over the 64 random bits
201 // uint ref_khz = clock_get_hz(clk_ref) / 100;
202 // for (int i = 0; i < 5; i++) {
203 // // Apply hash of the rosc frequency, limited but still 'extra' entropy
204 // uint measurement = frequency_count_raw(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC, ref_khz);
205 // local_rng_state.r[which] ^= splitmix64(measurement);
206 // (void) xoroshiro128ss(&local_rng_state); //churn to mix seed sources
207 // }
208
209 // Gather a full ROSC sample array with sample bits
210 local_rng_state.r[which] ^= splitmix64(capture_additional_rosc_samples(8 * sizeof(rosc_samples)));
211 which ^= 1;
212 #endif
213
214 #if PICO_RAND_SEED_ENTROPY_SRC_TIME
215 // Mix in hashed time. This is [possibly] predictable boot-to-boot
216 // but will vary application-to-application.
217 local_rng_state.r[which] ^= splitmix64(time_us_64());
218 which ^= 1;
219 #endif
220
221 spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_RAND);
222 uint32_t save = spin_lock_blocking(lock);
223 if (!rng_initialised) {
224 #if PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER
225 #if !PICO_RAND_BUSCTRL_COUNTER_INDEX
226 int idx = -1;
227 for(uint i = 0; i < count_of(bus_ctrl_hw->counter); i++) {
228 if (bus_ctrl_hw->counter[i].sel == BUSCTRL_PERFSEL0_RESET) {
229 idx = (int)i;
230 break;
231 }
232 }
233 hard_assert(idx != -1);
234 bus_counter_idx = (uint8_t)idx;
235 #else
236 bus_counter_idx = (uint8_t)PICO_RAND_BUSCTRL_COUNTER_INDEX;
237 #endif
238 bus_ctrl_hw->counter[bus_counter_idx].sel = PICO_RAND_BUS_PERF_COUNTER_EVENT;
239 #endif
240 (void) xoroshiro128ss(&local_rng_state);
241 rng_state = local_rng_state;
242 rng_initialised = true;
243 }
244 spin_unlock(lock, save);
245 }
246
get_rand_64(void)247 uint64_t get_rand_64(void) {
248 if (!rng_initialised) {
249 // Do not provide 'RNs' until the system has been initialised. Note:
250 // The first initialisation can be quite time-consuming depending on
251 // the amount of RAM hashed, see RAM_HASH_START and RAM_HASH_END
252 initialise_rand();
253 }
254
255 static volatile uint8_t check_byte;
256 rng_128_t local_rng_state = rng_state;
257 uint8_t local_check_byte = check_byte;
258 // Modify PRNG state with the run-time entropy sources,
259 // hashed to reduce correlation with previous modifications.
260 uint which = 0;
261 #if PICO_RAND_ENTROPY_SRC_TIME
262 local_rng_state.r[which] ^= splitmix64(time_us_64());
263 which ^= 1;
264 #endif
265 #if PICO_RAND_ENTROPY_SRC_ROSC
266 local_rng_state.r[which] ^= splitmix64(capture_additional_rosc_samples(PICO_RAND_ROSC_BIT_SAMPLE_COUNT));
267 which ^= 1;
268 #endif
269 #if PICO_RAND_ENTROPY_SRC_BUS_PERF_COUNTER
270 uint32_t bus_counter_value = bus_ctrl_hw->counter[bus_counter_idx].value;
271 // counter is saturating, so clear it if it has reached saturation
272 if (bus_counter_value == BUSCTRL_PERFCTR0_BITS) {
273 bus_ctrl_hw->counter[bus_counter_idx].value = 0;
274 }
275 local_rng_state.r[which] &= splitmix64(bus_counter_value);
276 which ^= 1;
277 #endif
278
279 spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_RAND);
280 uint32_t save = spin_lock_blocking(lock);
281 if (local_check_byte != check_byte) {
282 // someone got a random number in the interim, so mix it in
283 local_rng_state.r[0] ^= rng_state.r[0];
284 local_rng_state.r[1] ^= rng_state.r[1];
285 }
286 // Generate a 64-bit RN from the modified PRNG state.
287 // Note: This also "churns" the 128-bit state for next time.
288 uint64_t rand64 = xoroshiro128ss(&local_rng_state);
289 rng_state = local_rng_state;
290 check_byte++;
291 spin_unlock(lock, save);
292
293 return rand64;
294 }
295
get_rand_128(rng_128_t * ptr128)296 void get_rand_128(rng_128_t *ptr128) {
297 ptr128->r[0] = get_rand_64();
298 ptr128->r[1] = get_rand_64();
299 }
300
get_rand_32(void)301 uint32_t get_rand_32(void) {
302 return (uint32_t) get_rand_64();
303 }
304