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