Software APIs
otbn_irq_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 
8 #include "sw/device/lib/runtime/irq.h"
10 #include "sw/device/lib/testing/entropy_testutils.h"
12 #include "sw/device/lib/testing/test_framework/check.h"
14 
16 
17 OTBN_DECLARE_APP_SYMBOLS(err_test);
18 
19 static const otbn_app_t kAppErrTest = OTBN_APP_T_INIT(err_test);
20 
21 OTTF_DEFINE_TEST_CONFIG();
22 
23 static dif_rv_plic_t plic;
24 static volatile bool otbn_finished;
25 
26 /**
27  * Get OTBN error bits; check they match expected_err_bits.
28  */
29 static void check_otbn_err_bits(dif_otbn_t *otbn,
30  dif_otbn_err_bits_t expected_err_bits) {
31  dif_otbn_err_bits_t otbn_err_bits;
32  CHECK_DIF_OK(dif_otbn_get_err_bits(otbn, &otbn_err_bits));
33  CHECK(otbn_err_bits == expected_err_bits,
34  "dif_otbn_get_err_bits() produced unexpected error bits: %x",
35  otbn_err_bits);
36 }
37 
38 /**
39  * Get the OTBN instruction count; check that it matches expected_insn_cnt.
40  */
41 static void check_otbn_insn_cnt(dif_otbn_t *otbn, uint32_t expected_insn_cnt) {
42  uint32_t insn_cnt;
43  CHECK_DIF_OK(dif_otbn_get_insn_cnt(otbn, &insn_cnt));
44  CHECK(insn_cnt == expected_insn_cnt,
45  "Expected to execute %d instructions, but got %d.", expected_insn_cnt,
46  insn_cnt);
47 }
48 
49 /**
50  * Get OTBN's status; check that it matches expected_status.
51  */
52 static void check_otbn_status(dif_otbn_t *otbn,
53  dif_otbn_status_t expected_status) {
55  CHECK_DIF_OK(dif_otbn_get_status(otbn, &status));
56  CHECK(status == expected_status, "Unexpected status: expected %d but got %d.",
57  expected_status, status);
58 }
59 
60 /**
61  * Run a binary on OTBN, waiting for completion by interrupt.
62  *
63  * Once the binary has finished, check for expected status, error bits and
64  * instruction count.
65  */
66 static void run_test_with_irqs(dif_otbn_t *otbn, otbn_app_t app,
67  dif_otbn_status_t expected_status,
68  dif_otbn_err_bits_t expected_err_bits,
69  uint32_t expected_insn_cnt) {
70  // Clear the otbn_finished flag: we'll set it in the interrupt handler when
71  // we see the Done interrupt fire.
72  otbn_finished = false;
73 
74  CHECK_STATUS_OK(otbn_testutils_load_app(otbn, app));
75 
76  // If the CTRL.SOFTWARE_ERRS_FATAL flag is set, a software error will be
77  // promoted to a fatal error (which, among other things, bricks OTBN until
78  // next reset). Make sure that's not turned on.
79  CHECK(dif_otbn_set_ctrl_software_errs_fatal(otbn, false) == kDifOk);
80 
81  // Enable Done interrupt
82  CHECK_DIF_OK(
83  dif_otbn_irq_set_enabled(otbn, kDifOtbnIrqDone, kDifToggleEnabled));
84 
85  // Start OTBN
86  CHECK_STATUS_OK(otbn_testutils_execute(otbn));
87 
88  // At this point, OTBN should be running. Wait for an interrupt that says
89  // it's done.
90  ATOMIC_WAIT_FOR_INTERRUPT(otbn_finished);
91 
92  check_otbn_status(otbn, expected_status);
93  check_otbn_err_bits(otbn, expected_insn_cnt);
94  check_otbn_insn_cnt(otbn, expected_err_bits);
95 }
96 
97 /**
98  * Initialize PLIC and enable OTBN interrupt.
99  */
100 static void plic_init_with_irqs(void) {
101  mmio_region_t base_addr =
103  CHECK_DIF_OK(dif_rv_plic_init(base_addr, &plic));
104 
106 
107  // Set interrupt priority to be positive
108  CHECK_DIF_OK(dif_rv_plic_irq_set_priority(&plic, irq_id, 0x1));
109 
110  // Enable the interrupt
111  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(
113 
114  // Set the threshold for Ibex to 0.
116  &plic, kTopEarlgreyPlicTargetIbex0, 0x0));
117 }
118 
119 /**
120  * The ISR for this test.
121  *
122  * This function overrides the default OTTF external ISR.
123  */
124 void ottf_external_isr(uint32_t *exc_info) {
125  // Find which interrupt fired at PLIC by claiming it.
126  dif_rv_plic_irq_id_t irq_id;
127  CHECK_DIF_OK(
129 
130  // Check it was from OTBN
133  CHECK(peri == kTopEarlgreyPlicPeripheralOtbn,
134  "Interrupt from incorrect peripheral: (exp: %d, obs: %s)",
136 
137  // Check this is the interrupt we expected
138  CHECK(irq_id == kTopEarlgreyPlicIrqIdOtbnDone);
139 
140  // otbn_finished should currently be false (we're supposed to clear it before
141  // starting OTBN)
142  CHECK(!otbn_finished);
143 
144  // Set otbn_finished, which we'll pick up in run_test_with_irqs.
145  otbn_finished = true;
146 }
147 
148 bool test_main(void) {
149  CHECK_STATUS_OK(entropy_testutils_auto_mode_init());
150  plic_init_with_irqs();
151 
152  // Enable the external IRQ (so that we see the interrupt from the PLIC)
153  irq_global_ctrl(true);
154  irq_external_ctrl(true);
155 
157 
158  dif_otbn_t otbn;
159  CHECK_DIF_OK(dif_otbn_init(base_addr, &otbn));
160 
161  run_test_with_irqs(&otbn, kAppErrTest, kDifOtbnStatusIdle,
163 
164  return true;
165 }