Software APIs
entropy_src_csrng_test.c
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
10 #include "sw/device/lib/runtime/irq.h"
12 #include "sw/device/lib/testing/csrng_testutils.h"
13 #include "sw/device/lib/testing/entropy_testutils.h"
15 #include "sw/device/lib/testing/rand_testutils.h"
16 #include "sw/device/lib/testing/rv_plic_testutils.h"
17 #include "sw/device/lib/testing/test_framework/check.h"
19 #include "sw/device/tests/otbn_randomness_impl.h"
20 
22 #include "sw/device/lib/testing/autogen/isr_testutils.h"
23 
24 static dif_csrng_t csrng;
25 static dif_edn_t edn0;
26 static dif_edn_t edn1;
27 static dif_otbn_t otbn;
28 static dif_rv_plic_t plic;
29 
30 OTTF_DEFINE_TEST_CONFIG();
31 
32 enum {
33  /**
34  * The size of the buffer used in firmware to process the entropy bits in
35  * firmware override mode.
36  */
37  kTestParamFifoBufferSize = 12,
38  /**
39  * The number of test iterations per target.
40  */
41  kTestParamNumIterationsSim = 1,
42  kTestParamNumIterationsOther = 20,
43 };
44 
45 /**
46  * Interrupt flag IDs. Used to index the interrupt flags used in this test.
47  */
48 typedef enum irq_flag_id {
49  kTestIrqFlagIdCsrngEntropyReq,
50  kTestIrqFlagIdEdn1CmdDone,
51  kTestIrqFlagIdEdn0CmdDone,
52  kTestIrqFlagCount,
53 } irq_flag_id_t;
54 
55 /**
56  * Interrupt flags. Set by `ottf_external_isr()` and cleared by
57  * `plic_interrupts_enable()`.
58  */
59 static volatile bool irq_flags[kTestIrqFlagCount];
60 
61 /**
62  * Initializes the peripherals used in this test.
63  */
64 static void init_peripherals(void) {
65  CHECK_DIF_OK(dif_csrng_init(
67  CHECK_DIF_OK(
69  CHECK_DIF_OK(
71  CHECK_DIF_OK(
72  dif_otbn_init(mmio_region_from_addr(TOP_EARLGREY_OTBN_BASE_ADDR), &otbn));
73  CHECK_DIF_OK(dif_rv_plic_init(
75 }
76 
77 /**
78  * Enables the interrupts required by this test.
79  */
80 static void plic_interrupts_enable(void) {
81  irq_external_ctrl(false);
82  irq_global_ctrl(false);
83 
84  for (size_t i = 0; i < kTestIrqFlagCount; ++i) {
85  irq_flags[i] = false;
86  }
87 
91  for (size_t i = 0; i < ARRAYSIZE(irq_ids); ++i) {
92  CHECK_DIF_OK(
93  dif_rv_plic_irq_set_priority(&plic, irq_ids[i], /*priority=*/1u));
94  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(
95  &plic, irq_ids[i], kTopEarlgreyPlicTargetIbex0, kDifToggleEnabled));
96  }
97 
99  &plic, kTopEarlgreyPlicTargetIbex0, /*threshold=*/0u));
100 
101  CHECK_DIF_OK(dif_csrng_irq_set_enabled(&csrng, kDifCsrngIrqCsEntropyReq,
103  CHECK_DIF_OK(dif_edn_irq_set_enabled(&edn0, kDifEdnIrqEdnCmdReqDone,
105  CHECK_DIF_OK(dif_edn_irq_set_enabled(&edn1, kDifEdnIrqEdnCmdReqDone,
107 
108  irq_global_ctrl(true);
109  irq_external_ctrl(true);
110 }
111 
112 /**
113  * Blocks until the interrupt flag with `isr_id` is set to true by the
114  * `ottf_external_isr()` routine.
115  */
116 static void irq_block_wait(irq_flag_id_t isr_id) {
117  ATOMIC_WAIT_FOR_INTERRUPT(irq_flags[isr_id]);
118  switch (isr_id) {
119  case kTestIrqFlagIdCsrngEntropyReq:
120  CHECK_DIF_OK(dif_csrng_irq_set_enabled(&csrng, kDifCsrngIrqCsEntropyReq,
122  break;
123  case kTestIrqFlagIdEdn0CmdDone:
124  CHECK_DIF_OK(dif_edn_irq_set_enabled(&edn0, kDifEdnIrqEdnCmdReqDone,
126  break;
127  case kTestIrqFlagIdEdn1CmdDone:
128  CHECK_DIF_OK(dif_edn_irq_set_enabled(&edn0, kDifEdnIrqEdnCmdReqDone,
130  break;
131  default:
132  CHECK(false, "Invalid isr_id: %d", isr_id);
133  }
134 }
135 
136 /**
137  * Requests data from CSRNG software instance.
138  *
139  * Asserts error if there are any repeated words in the output data, or if there
140  * are any errors set in the CSRNG status registers.
141  */
142 static void csrng_generate_output_check(void) {
143  uint32_t output[kTestParamFifoBufferSize] = {0};
144  CHECK_STATUS_OK(
145  csrng_testutils_cmd_generate_run(&csrng, output, ARRAYSIZE(output)));
146 
147  uint32_t prev_data = 0;
148  for (size_t i = 0; i < ARRAYSIZE(output); ++i) {
149  CHECK(prev_data != output[i],
150  "Received duplicate data. Last index: %d, value: 0x%x", i, prev_data);
151  }
152 }
153 
154 /**
155  * Verifies that the entropy req interrupt is triggered on CSRNG instantiate and
156  * reseed commands.
157  */
158 static void test_csrng_sw_entropy_req_interrupt(
159  const dif_csrng_seed_material_t *seed_material) {
160  CHECK_STATUS_OK(entropy_testutils_stop_all());
161  CHECK_STATUS_OK(entropy_testutils_entropy_src_init());
162  CHECK_DIF_OK(dif_csrng_configure(&csrng));
163  CHECK_DIF_OK(dif_csrng_clear_recoverable_alerts(&csrng));
164 
165  CHECK_STATUS_OK(csrng_testutils_cmd_ready_wait(&csrng));
166  plic_interrupts_enable();
168  seed_material));
169  irq_block_wait(kTestIrqFlagIdCsrngEntropyReq);
170  csrng_generate_output_check();
171 
172  plic_interrupts_enable();
173  CHECK_DIF_OK(dif_csrng_reseed(&csrng, seed_material));
174  irq_block_wait(kTestIrqFlagIdCsrngEntropyReq);
175  csrng_generate_output_check();
176 
177  CHECK_STATUS_OK(csrng_testutils_cmd_status_check(&csrng));
178  CHECK_STATUS_OK(csrng_testutils_recoverable_alerts_check(&csrng));
179 }
180 
181 /**
182  * Blocks until EDN is ready to process commands.
183  *
184  * @param edn A EDN instance.
185  */
186 static void edn_ready_wait(const dif_edn_t *edn) {
187  bool ready = false;
188  while (!ready) {
189  CHECK_DIF_OK(dif_edn_get_status(edn, kDifEdnStatusReady, &ready));
190  }
191  bool ack_err;
192  CHECK_DIF_OK(dif_edn_get_status(edn, kDifEdnStatusCsrngStatus, &ack_err));
193  CHECK(!ack_err, "Unexpected CSRNG ack error");
194 }
195 
196 /**
197  * Configures the `edn` instance.
198  *
199  * Verifies that the entropy req interrupt is triggered on EDN instantiate and
200  * reseed commands.
201  *
202  * @param edn A EDN instance.
203  * @param irq_flag_id The interrupt flag ID to poll after each command is sent
204  * to EDN.
205  * @param seed_material Seed material used in instantiate and reseed commands.
206  */
207 static void edn_configure(const dif_edn_t *edn, irq_flag_id_t irq_flag_id,
208  const dif_edn_seed_material_t *seed_material) {
209  CHECK_DIF_OK(dif_edn_configure(edn));
210 
211  edn_ready_wait(edn);
212  plic_interrupts_enable();
213  CHECK_DIF_OK(
215  irq_block_wait(kTestIrqFlagIdCsrngEntropyReq);
216  irq_block_wait(irq_flag_id);
217 
218  edn_ready_wait(edn);
219  plic_interrupts_enable();
220  CHECK_DIF_OK(dif_edn_reseed(edn, seed_material));
221  irq_block_wait(kTestIrqFlagIdCsrngEntropyReq);
222  irq_block_wait(irq_flag_id);
223 
224  edn_ready_wait(edn);
225 }
226 
227 /**
228  * Initializes EDN instances using the `SW_CMD_REQ` interface and runs the OTBN
229  * randomness test to verify the entropy delivered by EDN0 and EDN1.
230  *
231  * @param seed_material Seed material used in EDN instantiate and reseed
232  * commands.
233  */
234 static void test_edn_cmd_done(const dif_edn_seed_material_t *seed_material) {
235  CHECK_STATUS_OK(entropy_testutils_stop_all());
236  CHECK_DIF_OK(dif_csrng_clear_recoverable_alerts(&csrng));
237  CHECK_STATUS_OK(entropy_testutils_entropy_src_init());
238  CHECK_DIF_OK(dif_csrng_configure(&csrng));
239 
240  edn_configure(&edn0, kTestIrqFlagIdEdn0CmdDone, seed_material);
241  edn_configure(&edn1, kTestIrqFlagIdEdn1CmdDone, seed_material);
242 
243  plic_interrupts_enable();
244 
245  // The EDN0 is connected to other peripherals that regularly request entropy
246  // so we keep generating entropy on the EDN0 to make sure that the OTBN
247  // gets enough to finish the test. The EDN1 is only connected to the OTBN
248  // so we generate exactly the amount of entropy necessary for the test and not
249  // more otherwise the Generate command will
250  // not be fully executed, causing a hang in the `irq_block_wait()` calls
251  // following the end of the OTBN test. The OTBN test reads `RND` 5 times
252  // and each read consumes 256b of entropy. The EDN1 generates entropy per
253  // blocks of 128b so we need to generate 10 blocks.
254  const size_t kEdnBlockSizeBits = 128; // Each EDN block contains 128b.
255  const size_t kOtbnRequestSizeBits = 256; // Each OTBN request requires 256b.
256  const size_t kOtbnTestRequestCount =
257  5; // The number of `RND` reads in the randomness test.
258 
259  // Number of blocks generated on the EDN1.
260  size_t edn1_generated_blocks = 0;
261 
262  // Warning: `dif_edn_generate_start` takes a length in words (32b).
263  CHECK_DIF_OK(dif_edn_generate_start(&edn0, /*len=*/1));
264  CHECK_DIF_OK(dif_edn_generate_start(&edn1, kEdnBlockSizeBits / 32));
265  edn_ready_wait(&edn0);
266  edn_ready_wait(&edn1);
267  CHECK_STATUS_OK(entropy_testutils_error_check(&csrng, &edn0, &edn1));
268 
269  LOG_INFO("OTBN:START");
270  otbn_randomness_test_start(&otbn, /*iters=*/0);
271 
272  bool busy = true;
273  while (busy) {
274  // Clearing `irq_flags` is ok here since there are no other in-flight
275  // commands being sent from the EDN instances to the CSRNG block.
276  if (irq_flags[kTestIrqFlagIdEdn0CmdDone]) {
277  irq_flags[kTestIrqFlagIdEdn0CmdDone] = false;
278  CHECK_DIF_OK(dif_edn_generate_start(&edn0, /*len=*/1));
279  }
280  if (irq_flags[kTestIrqFlagIdEdn1CmdDone]) {
281  edn1_generated_blocks++;
282  if (edn1_generated_blocks <
283  kOtbnTestRequestCount * kOtbnRequestSizeBits / kEdnBlockSizeBits) {
284  irq_flags[kTestIrqFlagIdEdn1CmdDone] = false;
285  // Warning: `dif_edn_generate_start` takes a length in words (32b).
286  CHECK_DIF_OK(dif_edn_generate_start(&edn1, kEdnBlockSizeBits / 32));
287  }
288  }
289  // Check if OTBN is still running.
291  CHECK_DIF_OK(dif_otbn_get_status(&otbn, &status));
292  busy = status != kDifOtbnStatusIdle && status != kDifOtbnStatusLocked;
293  }
294 
295  CHECK(otbn_randomness_test_end(&otbn, /*skip_otbn_done_check=*/false));
296  LOG_INFO("OTBN:END");
297 
298  // See comment above regarding generate command length and potential test
299  // locking issues for EDN1.
300  irq_block_wait(kTestIrqFlagIdEdn1CmdDone);
301  irq_block_wait(kTestIrqFlagIdEdn0CmdDone);
302  LOG_INFO("DONE");
303 
304  plic_interrupts_enable();
305  CHECK_DIF_OK(dif_edn_uninstantiate(&edn0));
306  irq_block_wait(kTestIrqFlagIdEdn0CmdDone);
307 
308  plic_interrupts_enable();
309  CHECK_DIF_OK(dif_edn_uninstantiate(&edn1));
310  irq_block_wait(kTestIrqFlagIdEdn1CmdDone);
311 
312  CHECK_STATUS_OK(csrng_testutils_recoverable_alerts_check(&csrng));
313  CHECK_STATUS_OK(entropy_testutils_error_check(&csrng, &edn0, &edn1));
314 }
315 
316 void ottf_external_isr(uint32_t *exc_info) {
317  // Claim the IRQ at the PLIC.
318  dif_rv_plic_irq_id_t plic_irq_id;
319  CHECK_DIF_OK(
320  dif_rv_plic_irq_claim(&plic, kTopEarlgreyPlicTargetIbex0, &plic_irq_id));
321 
322  // Get the peripheral the IRQ belongs to.
323  top_earlgrey_plic_peripheral_t peripheral_serviced =
326 
327  // Get the IRQ that was fired from the PLIC IRQ ID and set the corresponding
328  // `irq_flags`.
329  if (peripheral_serviced == kTopEarlgreyPlicPeripheralCsrng) {
330  dif_csrng_irq_t irq =
331  (dif_csrng_irq_t)(plic_irq_id - kTopEarlgreyPlicIrqIdCsrngCsCmdReqDone);
332  CHECK(irq == kDifCsrngIrqCsEntropyReq, "Unexpected irq: 0x%x", irq);
333  CHECK_DIF_OK(dif_csrng_irq_acknowledge(&csrng, irq));
334  irq_flags[kTestIrqFlagIdCsrngEntropyReq] = true;
335  } else if (peripheral_serviced == kTopEarlgreyPlicPeripheralEdn0) {
336  dif_edn_irq_t irq =
337  (dif_edn_irq_t)(plic_irq_id - kTopEarlgreyPlicIrqIdEdn0EdnCmdReqDone);
338  CHECK(irq == kDifEdnIrqEdnCmdReqDone, "Unexpected irq: 0x%x", irq);
339  CHECK_DIF_OK(dif_edn_irq_acknowledge(&edn0, irq));
340  irq_flags[kTestIrqFlagIdEdn0CmdDone] = true;
341  } else if (peripheral_serviced == kTopEarlgreyPlicPeripheralEdn1) {
342  dif_edn_irq_t irq =
343  (dif_edn_irq_t)(plic_irq_id - kTopEarlgreyPlicIrqIdEdn1EdnCmdReqDone);
344  CHECK(irq == kDifEdnIrqEdnCmdReqDone, "Unexpected irq: 0x%x", irq);
345  CHECK_DIF_OK(dif_edn_irq_acknowledge(&edn1, irq));
346  irq_flags[kTestIrqFlagIdEdn1CmdDone] = true;
347  }
348 
350  plic_irq_id));
351 }
352 
353 bool test_main(void) {
354  init_peripherals();
355 
356  // Get test random parameters before we disable the entropy complex.
357  // rand_testutils relies on the ibex rnd CSR which is connected to EDN0.
358  dif_csrng_seed_material_t csrng_seed;
359  csrng_seed.seed_material_len =
360  rand_testutils_gen32_range(/*min=*/0, kDifCsrngSeedMaterialMaxWordLen);
361  for (size_t i = 0; i < csrng_seed.seed_material_len; ++i) {
362  csrng_seed.seed_material[i] = rand_testutils_gen32();
363  }
364 
365  dif_edn_seed_material_t edn_seed;
366  edn_seed.len =
367  rand_testutils_gen32_range(/*min=*/0, kDifEntropySeedMaterialMaxWordLen);
368  for (size_t i = 0; i < edn_seed.len; ++i) {
369  edn_seed.data[i] = rand_testutils_gen32();
370  }
371 
372  uint32_t num_iterations = kTestParamNumIterationsSim;
374  num_iterations = kTestParamNumIterationsOther;
375  }
376 
377  for (size_t i = 0; i < num_iterations; ++i) {
378  test_csrng_sw_entropy_req_interrupt(&csrng_seed);
379  test_edn_cmd_done(&edn_seed);
380  }
381 
382  return true;
383 }