1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*
8  * CRACEN (T/ND)RNG - (True/Non-Deterministic) Random Number Generator
9  *
10  * Notes:
11  *  * Conditioning is not modeled beyond the throughput difference.
12  *    The model will produce pseudorandom data of the same quality with it enabled or not.
13  *    The KEY registers are ignored.
14  *
15  *  * The test LFSR is not modeled. Setting CONTROL.LFSREn has no effect. The RNG produces the same data either way,
16  *    which will not match what the HW LFSR would produce.
17  *
18  *  * DisableOsc has no effect, the produced entropy is the same either way
19  *
20  *  * The TestData register is ignored. It is not possible to hand feed to the logic entropy values with it. TestDataBusy is never set.
21  *
22  *  * CONFIG.ForceRun is ignored (assumed always off)
23  *  * CONFIG.FifoWriteStartUp is ignored (assumed always off)
24  *
25  *  * The model does not include the real AIS31 test logic, and therefore its associated registers are ignored (AIS31*)
26  *    Enabling or not the AIS31Bypass has no effect and so does CONTROL.AIS31TestSel.
27  *    AIS31Status is never set.
28  *
29  *  * The model does not include the real "health" monitoring logic. CONTROL.HealthTestSel is ignored and so are the Rep & PropThresh
30  *    registers.
31  *
32  *  * The model includes test logic to trigger a startup, AIS31 or Health monitoring failure.
33  *    See nhw_CRACEN_RNG_fake_test_error()
34  *
35  *  * The model does not properly support changing configuration on the fly (you should disable the peripheral first)
36  *
37  *  * The model timing is a bit faster than the real HW (up to 25% faster depending on configuration),
38  *    and when conditioning is on, it pushes to the FIFO 4 words at a time, while the real HW pushes one at a time in sets of 4.
39  *
40  *  * Regarding FIFOLEVEL, writing to this register (in HW or the model) only clears the full status flag.
41  *    The register content itself remains untouched. note that if the level is still full while writing to this
42  *    register, the status flag and interrupt will be set back right away => Fix spec.
43  *
44  *  * Regarding FIFOThresh, the spec seems to be incorrect, in that the actual threshold is
45  *    FIFOLEVEL < (FIFOThresh + 1)*4 (note the +1) => Fix spec
46  */
47 
48 #include <stdint.h>
49 #include <stdbool.h>
50 #include <string.h>
51 #include "NHW_common_types.h"
52 #include "NHW_config.h"
53 #include "NHW_peri_types.h"
54 #include "NHW_templates.h"
55 #include "NHW_xPPI.h"
56 #include "NHW_CRACEN_RNG.h"
57 #include "NHW_CRACEN_wrap.h"
58 #include "irq_ctrl.h"
59 #include "nsi_tasks.h"
60 #include "nsi_hws_models_if.h"
61 #include "bs_rand_main.h"
62 #include "bs_tracing.h"
63 
64 extern NRF_CRACEN_Type NRF_CRACEN_regs;
65 extern NRF_CRACENCORE_Type NRF_CRACENCORE_regs;
66 
67 static NRF_CRACENCORE_RNGCONTROL_Type *RNG_regs;
68 
69 bs_time_t Timer_CRACEN_NDRNG;
70 static uint Timer_rem_clocks;
71 
72 static struct rng_status {
73   enum status_t {rng_reset = 0, rng_startup, rng_idle_ron, rng_idle_roff, rng_filling, rng_error} status;
74   bool enabled;
75   uint fifo_size; //Size of FIFO in 32bit words
76   uint fifo_rptr; //Offset of the first available word to read
77   uint fifo_wptr; //Offset of the first available empty word
78   uint fifo_level; //Number of words uses in the fifo
79   uint32_t fifo[1 << NHW_CRACEN_RNG_G_log2fifodepth];
80   uint queued_words; //Number of 32bit words ready to go into FIFO
81 
82   bool error_ais31_noise;
83   bool error_ais31_prenoise;
84   bool error_prop_test;
85   bool error_rep_test;
86   bool error_startup_test;
87 
88   bool pend_error_ais31_noise;
89   bool pend_error_ais31_prenoise;
90   bool pend_error_prop_test;
91   bool pend_error_rep_test;
92   bool pend_error_startup_test;
93 } rng_st;
94 
95 static void generate_more_data(void);
96 static void startup(void);
97 static inline void update_state(int state);
98 static void check_interrupts(void);
99 static void check_errors(void);
100 
nhw_CRACEN_soft_reset(void)101 static void nhw_CRACEN_soft_reset(void) {
102   rng_st.queued_words = 0;
103   rng_st.fifo_level = 0;
104   RNG_regs->FIFOLEVEL = 0;
105   rng_st.fifo_wptr = 0;
106   rng_st.fifo_rptr = 0;
107 
108   rng_st.status = rng_reset;
109   update_state(CRACENCORE_RNGCONTROL_STATUS_STATE_RESET);
110 
111   rng_st.error_ais31_noise = false;
112   rng_st.error_ais31_prenoise = false;
113   rng_st.error_prop_test = false;
114   rng_st.error_rep_test = false;
115   rng_st.error_startup_test = false;
116   rng_st.pend_error_ais31_noise = false;
117   rng_st.pend_error_ais31_prenoise = false;
118   rng_st.pend_error_prop_test = false;
119   rng_st.pend_error_rep_test = false;
120   rng_st.pend_error_startup_test = false;
121 
122   check_interrupts();
123 }
124 
nhw_CRACEN_RNG_init(void)125 void nhw_CRACEN_RNG_init(void) {
126   RNG_regs = (NRF_CRACENCORE_RNGCONTROL_Type *)&NRF_CRACENCORE_regs.RNGCONTROL;
127 
128   RNG_regs->CONTROL = CRACENCORE_RNGCONTROL_CONTROL_ResetValue;
129   RNG_regs->FIFOTHRESHOLD = CRACENCORE_RNGCONTROL_FIFOTHRESHOLD_ResetValue;
130   RNG_regs->FIFODEPTH = CRACENCORE_RNGCONTROL_FIFODEPTH_ResetValue;
131   RNG_regs->REPEATTHRESHOLD = CRACENCORE_RNGCONTROL_REPEATTHRESHOLD_ResetValue;
132   RNG_regs->PROPTHRESHOLD = CRACENCORE_RNGCONTROL_PROPTHRESHOLD_ResetValue;
133   RNG_regs->INITWAITVAL = CRACENCORE_RNGCONTROL_INITWAITVAL_ResetValue;
134   RNG_regs->SWOFFTMRVAL = CRACENCORE_RNGCONTROL_SWOFFTMRVAL_ResetValue;
135   RNG_regs->AIS31CONF0 = CRACENCORE_RNGCONTROL_AIS31CONF0_ResetValue;
136   RNG_regs->AIS31CONF1 = CRACENCORE_RNGCONTROL_AIS31CONF1_ResetValue;
137   RNG_regs->AIS31CONF2 = CRACENCORE_RNGCONTROL_AIS31CONF2_ResetValue;
138   RNG_regs->HWCONFIG = CRACENCORE_RNGCONTROL_HWCONFIG_ResetValue;
139 
140   rng_st.fifo_size = 1 << NHW_CRACEN_RNG_G_log2fifodepth;
141 
142   Timer_CRACEN_NDRNG = TIME_NEVER;
143 
144   nhw_CRACEN_soft_reset();
145 }
146 
check_interrupts(void)147 static void check_interrupts(void) {
148   bool new_int_line = false;
149 
150   RNG_regs->STATUS &= ~( CRACENCORE_RNGCONTROL_STATUS_REPFAIL_Msk
151                        | CRACENCORE_RNGCONTROL_STATUS_PROPFAIL_Msk
152                        | CRACENCORE_RNGCONTROL_STATUS_FULLINT_Msk
153                        | CRACENCORE_RNGCONTROL_STATUS_PREINT_Msk
154                        | CRACENCORE_RNGCONTROL_STATUS_ALMINT_Msk );
155 
156 #define CHECK_SET_FAILINT(statusm, intm) \
157   RNG_regs->STATUS |= CRACENCORE_RNGCONTROL_STATUS_##statusm##_Msk; \
158   if (RNG_regs->CONTROL & CRACENCORE_RNGCONTROL_CONTROL_INTEN##intm##_Msk) { \
159     new_int_line = true; \
160   }
161 
162   if (rng_st.error_startup_test) { //Does not have an int line, only status bit
163     RNG_regs->STATUS |= CRACENCORE_RNGCONTROL_STATUS_STARTUPFAIL_Msk;
164   }
165   if (rng_st.fifo_level == rng_st.fifo_size) {
166     CHECK_SET_FAILINT(FULLINT, FULL)
167   }
168   if (rng_st.error_rep_test) {
169     CHECK_SET_FAILINT(REPFAIL, REP)
170   }
171   if (rng_st.error_prop_test) {
172     CHECK_SET_FAILINT(PROPFAIL, PROP)
173   }
174   if (rng_st.error_ais31_prenoise) {
175     CHECK_SET_FAILINT(PREINT, PRE)
176   }
177   if (rng_st.error_ais31_noise) {
178     CHECK_SET_FAILINT(ALMINT, ALM)
179   }
180   nhw_CRACEN_toggle_RNG_intline(new_int_line);
181 }
182 
183 /**
184  * Check if an error has "occurred"
185  */
check_errors(void)186 void check_errors(void) {
187   bool error = false;
188 
189 #define CHECK_SET_PEND_ERROR(err_n) \
190   if (rng_st.pend_error_##err_n) { \
191     rng_st.error_##err_n = true; \
192     error = true; \
193   }
194 
195   if (rng_st.status == rng_startup) {
196     CHECK_SET_PEND_ERROR(startup_test)
197   }
198   if (rng_st.status == rng_filling) {
199     CHECK_SET_PEND_ERROR(rep_test)
200     CHECK_SET_PEND_ERROR(prop_test)
201     CHECK_SET_PEND_ERROR(ais31_prenoise)
202     CHECK_SET_PEND_ERROR(ais31_noise)
203   }
204 
205   if (error) {
206     rng_st.status = rng_error;
207     update_state(CRACENCORE_RNGCONTROL_STATUS_STATE_ERROR);
208     Timer_CRACEN_NDRNG = TIME_NEVER;
209     nhw_CRACEN_update_timer();
210     check_interrupts();
211   }
212 }
213 
fifo_push(uint32_t data)214 static void fifo_push(uint32_t data) {
215   rng_st.fifo[rng_st.fifo_wptr] = data;
216   rng_st.fifo_level +=1; /* no need to check for overflow */
217   RNG_regs->FIFOLEVEL = rng_st.fifo_level;
218   rng_st.fifo_wptr = (rng_st.fifo_wptr + 1) % rng_st.fifo_size;
219 
220   check_interrupts();
221 }
222 
fifo_pop(void)223 static uint32_t fifo_pop(void) {
224   uint32_t value = rng_st.fifo[rng_st.fifo_rptr];
225   if (rng_st.fifo_level > 0) {
226     rng_st.fifo_level -=1;
227     RNG_regs->FIFOLEVEL = rng_st.fifo_level;
228     rng_st.fifo_rptr = (rng_st.fifo_rptr + 1) % rng_st.fifo_size;
229   } else {
230     bs_trace_warning_time_line("CRACEN RNG pop from empty FIFO\n");
231   }
232 
233   if (rng_st.fifo_level < (RNG_regs->FIFOTHRESHOLD + 1)* 4) {
234     if (rng_st.status == rng_idle_roff) {
235       startup();
236     } else if (rng_st.status == rng_idle_ron) { //Note: The condition is the same from idle_roff or idle_ron
237       generate_more_data();
238     }
239   }
240   check_interrupts();
241 
242   return value;
243 }
244 
update_state(int state)245 static inline void update_state(int state) {
246   RNG_regs->STATUS &= ~CRACENCORE_RNGCONTROL_STATUS_STATE_Msk;
247   RNG_regs->STATUS |= state << CRACENCORE_RNGCONTROL_STATUS_STATE_Pos;
248 }
249 
get_NB128BITBLOCKS(void)250 static inline int get_NB128BITBLOCKS(void) {
251   return (RNG_regs->CONTROL & CRACENCORE_RNGCONTROL_CONTROL_NB128BITBLOCKS_Msk)
252       >> CRACENCORE_RNGCONTROL_CONTROL_NB128BITBLOCKS_Pos;
253 }
254 
generate_more_data(void)255 static void generate_more_data(void) {
256   bs_time_t clocks_next;
257 
258   rng_st.status = rng_filling;
259   update_state(CRACENCORE_RNGCONTROL_STATUS_STATE_FILLFIFO);
260 
261   if (RNG_regs->CONTROL & CRACENCORE_RNGCONTROL_CONTROL_CONDBYPASS_Msk) {
262     clocks_next = 32*(RNG_regs->CLKDIV+1);
263     rng_st.queued_words = 1;
264   } else {
265     clocks_next = 128*get_NB128BITBLOCKS()*(RNG_regs->CLKDIV+1);
266     rng_st.queued_words = 4;
267   }
268 
269   clocks_next+=Timer_rem_clocks;
270   Timer_CRACEN_NDRNG = clocks_next/NHW_CRACEN_FREQ_MHZ;
271   Timer_rem_clocks = clocks_next - (Timer_CRACEN_NDRNG*NHW_CRACEN_FREQ_MHZ);
272   Timer_CRACEN_NDRNG += nsi_hws_get_time();
273   nhw_CRACEN_update_timer();
274   check_errors();
275 }
276 
nhw_CRACEN_RNG_timer_triggered(void)277 void nhw_CRACEN_RNG_timer_triggered(void) {
278 
279   if (rng_st.status == rng_idle_ron) {
280     rng_st.status = rng_idle_roff;
281     update_state(CRACENCORE_RNGCONTROL_STATUS_STATE_IDLEROFF);
282     Timer_CRACEN_NDRNG = TIME_NEVER;
283     nhw_CRACEN_update_timer();
284     return;
285 
286   } else if ((rng_st.status == rng_filling)) {
287     while ((rng_st.queued_words > 0) && (rng_st.fifo_level < rng_st.fifo_size)) {
288       fifo_push(bs_random_uint32());
289       rng_st.queued_words--;
290     }
291     rng_st.queued_words = 0; //Discard a possible remainder
292   }
293 
294   if (rng_st.fifo_level < rng_st.fifo_size) {
295     generate_more_data();
296   } else {
297     rng_st.status = rng_idle_ron;
298     update_state(CRACENCORE_RNGCONTROL_STATUS_STATE_IDLERON);
299     Timer_CRACEN_NDRNG = nsi_hws_get_time() + RNG_regs->SWOFFTMRVAL/NHW_CRACEN_FREQ_MHZ;
300     nhw_CRACEN_update_timer();
301   }
302 }
303 
startup(void)304 static void startup(void) {
305   rng_st.status = rng_startup;
306   update_state(CRACENCORE_RNGCONTROL_STATUS_STATE_STARTUP);
307 
308   Timer_CRACEN_NDRNG = nsi_hws_get_time() + RNG_regs->INITWAITVAL/NHW_CRACEN_FREQ_MHZ + NHW_CRACEN_STARTUPTEST_DUR; //TODO: do start up tests add any delay?
309   nhw_CRACEN_update_timer();
310   check_errors();
311 }
312 
nhw_CRACEN_RNG_regw_sideeffects_CONTROL(void)313 void nhw_CRACEN_RNG_regw_sideeffects_CONTROL(void) {
314   bool enabled = RNG_regs->CONTROL & CRACENCORE_RNGCONTROL_CONTROL_ENABLE_Msk;
315 
316   if (RNG_regs->CONTROL & CRACENCORE_RNGCONTROL_CONTROL_SOFTRST_Msk) {
317     nhw_CRACEN_soft_reset();
318     enabled = false;
319   }
320 
321   if (!enabled) {
322     rng_st.enabled = false;
323     Timer_CRACEN_NDRNG = TIME_NEVER;
324     nhw_CRACEN_update_timer();
325     rng_st.status = rng_reset;
326     update_state(CRACENCORE_RNGCONTROL_STATUS_STATE_RESET);
327     return;
328   }
329 
330   if ((NRF_CRACEN_regs.ENABLE & CRACEN_ENABLE_RNG_Msk) == 0) {
331     bs_trace_warning_time_line("Attempting to enable CRACEN RNG while the CRACEN wrap logic is off\n");
332   }
333 
334   check_interrupts();
335 
336   if (rng_st.enabled == true) { //Already enabled
337     return; //Nothing more to do (we don't handle too properly on the fly CONFIG changes)
338   }
339 
340   rng_st.enabled = true;
341 
342   startup();
343 }
344 
nhw_CRACEN_RNG_regr_sideeffects_FIFO(void)345 uint32_t nhw_CRACEN_RNG_regr_sideeffects_FIFO(void) {
346   if ((rng_st.status == rng_reset) && (rng_st.fifo_level == 0)) {
347     RNG_regs->STATUS |= CRACENCORE_RNGCONTROL_STATUS_FIFOACCFAIL_Msk;
348   }
349 
350   RNG_regs->FIFO[0] = fifo_pop();
351   return RNG_regs->FIFO[0];
352 }
353 
nhw_CRACEN_RNG_regw_sideeffects_FIFOLEVEL(void)354 void nhw_CRACEN_RNG_regw_sideeffects_FIFOLEVEL(void) {
355   RNG_regs->STATUS &= ~CRACENCORE_RNGCONTROL_STATUS_FULLINT_Msk;
356   RNG_regs->FIFOLEVEL = rng_st.fifo_level;
357   check_interrupts();
358 }
359 
nhw_CRACEN_RNG_fake_test_error(uint32_t mask)360 void nhw_CRACEN_RNG_fake_test_error(uint32_t mask) {
361   if (mask & CRACEN_RNG_FAKE_STARTUP_ERROR) {
362     rng_st.pend_error_startup_test = true;
363   }
364   if (mask & CRACEN_RNG_FAKE_REP_TEST_ERROR) {
365     rng_st.pend_error_rep_test = true;
366   }
367   if (mask & CRACEN_RNG_FAKE_PROP_TEST_ERROR) {
368     rng_st.pend_error_prop_test = true;
369   }
370   if (mask & CRACEN_RNG_FAKE_AIS31_PRENOISE_ERROR) {
371     rng_st.pend_error_ais31_prenoise = true;
372   }
373   if (mask & CRACEN_RNG_FAKE_AIS31_NOISE_ERROR) {
374     rng_st.pend_error_ais31_noise = true;
375   }
376   check_errors();
377 }
378