Software APIs
ottf_isrs.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 
5 #include "sw/device/lib/testing/test_framework/ottf_isrs.h"
6 
7 #include "dt/dt_sram_ctrl.h"
15 #include "sw/device/lib/testing/test_framework/check.h"
16 
17 dif_rv_plic_t ottf_plic;
18 
19 // Fault reasons from
20 // https://riscv.org/wp-content/uploads/2017/05/riscv-privileged-v1.10.pdf
21 static const char *exception_reason[] = {
22  "Instruction Misaligned",
23  "Instruction Access",
24  "Illegal Instruction",
25  "Breakpoint",
26  "Load Address Misaligned",
27  "Load Access Fault",
28  "Store Address Misaligned",
29  "Store Access Fault",
30  "U-mode Ecall",
31  "S-mode Ecall",
32  "Reserved",
33  "M-mode Ecall",
34  "Instruction Page Fault",
35  "Load Page Fault",
36  "Reserved",
37  "Store Page Fault",
38  "Reserved",
39  "Reserved",
40  "Reserved",
41  "Reserved",
42  "Reserved",
43  "Reserved",
44  "Reserved",
45  "Reserved",
46  "Reserved",
47  "Reserved",
48  "Reserved",
49  "Reserved",
50  "Reserved",
51  "Reserved",
52  "Reserved",
53  "Reserved",
54 };
55 
56 static const char *exc_frame[] = {
57  "mepc", " ra", " t0", " t1", " t2", " s0", " s1", " a0",
58  " a1", " a2", " a3", " a4", " a5", " a6", " a7", " s2",
59  " s3", " s4", " s5", " s6", " s7", " s8", " s9", " s10",
60  " s11", " t3", " t4", " t5", " t6", "msts",
61 };
62 
63 void ottf_generic_fault_print(uint32_t *exc_info, const char *reason,
64  uint32_t mcause) {
65  enum { kExcWords = 30 };
66  uint32_t mepc = ibex_mepc_read();
67  uint32_t mtval = ibex_mtval_read();
68  if (exc_info) {
69  base_printf("===== Exception Frame @ %08x =====", exc_info);
70  for (size_t i = 0; i < kExcWords; ++i) {
71  if (i % 4 == 0) {
72  base_printf("\n");
73  }
74  const char *name = exc_frame[i];
75  if (name == NULL)
76  continue;
77  base_printf(" %4s=%08x", name, exc_info[i]);
78  }
79  uint32_t *sp = exc_info + kExcWords;
80  base_printf("\n");
81  uint32_t ram_base_addr =
82  dt_sram_ctrl_reg_block(kDtSramCtrlMain, kDtSramCtrlRegBlockRam);
83  uint32_t *ram_start = (uint32_t *)ram_base_addr;
84  // FIXME replace this with DT when possible.
85 #if defined(OPENTITAN_IS_EARLGREY)
87 #elif defined(OPENTITAN_IS_DARJEELING)
88  uint32_t ram_size = TOP_DARJEELING_SRAM_CTRL_MAIN_RAM_SIZE_BYTES;
89 #else
90 #error unsupported top
91 #endif
92  uint32_t *ram_end = (uint32_t *)(ram_base_addr + ram_size);
93 
94  extern const char _text_start[], _text_end[];
95  const uint32_t text_start = (uint32_t)_text_start;
96  const uint32_t text_end = (uint32_t)_text_end;
97  base_printf("===== Call Stack =====\n");
98  for (; sp >= ram_start && sp < ram_end; ++sp) {
99  uint32_t val = *sp;
100  if (val >= text_start && val < text_end) {
101  base_printf(" %08x\n", val);
102  }
103  }
104  }
105  LOG_ERROR("FAULT: %s. MCAUSE=%08x MEPC=%08x MTVAL=%08x", reason, mcause, mepc,
106  mtval);
107 }
108 
109 static void generic_fault_handler(uint32_t *exc_info) {
110  uint32_t mcause = ibex_mcause_read();
111  ottf_generic_fault_print(exc_info, exception_reason[mcause & kIbexExcMax],
112  mcause);
113  abort();
114 }
115 
116 /**
117  * The weak `pxCurrentTCB` symbol below enable the OTTF ISRs to be used without
118  * the OTTF itself. This enables us to maintain one set of default ISRs for
119  * testing, while also enabling writing tests that do not make use of the OTTF.
120  * See the `crt_test` in `sw/device/tests/crt_test.c` for an example.
121  */
122 OT_WEAK
123 void *pxCurrentTCB = NULL;
124 
125 OT_WEAK
126 void ottf_exception_handler(uint32_t *exc_info) {
127  uint32_t mcause = ibex_mcause_read();
128 
129  switch ((ibex_exc_t)(mcause & kIbexExcMax)) {
130  case kIbexExcInstrMisaligned:
131  ottf_instr_misaligned_fault_handler(exc_info);
132  break;
133  case kIbexExcInstrAccessFault:
134  ottf_instr_access_fault_handler(exc_info);
135  break;
136  case kIbexExcIllegalInstrFault:
137  ottf_illegal_instr_fault_handler(exc_info);
138  break;
139  case kIbexExcBreakpoint:
140  ottf_breakpoint_handler(exc_info);
141  break;
142  case kIbexExcLoadAccessFault:
143  ottf_load_store_fault_handler(exc_info);
144  break;
145  case kIbexExcStoreAccessFault:
146  ottf_load_store_fault_handler(exc_info);
147  break;
148  case kIbexExcMachineECall:
149  ottf_machine_ecall_handler(exc_info);
150  break;
151  case kIbexExcUserECall:
152  ottf_user_ecall_handler(exc_info);
153  break;
154  default:
155  generic_fault_handler(exc_info);
156  }
157 }
158 
159 OT_WEAK
160 OT_ALIAS("generic_fault_handler")
161 void ottf_instr_misaligned_fault_handler(uint32_t *exc_info);
162 
163 OT_WEAK
164 OT_ALIAS("generic_fault_handler")
165 void ottf_instr_access_fault_handler(uint32_t *exc_info);
166 
167 OT_WEAK
168 OT_ALIAS("generic_fault_handler")
169 void ottf_illegal_instr_fault_handler(uint32_t *exc_info);
170 
171 OT_WEAK
172 OT_ALIAS("generic_fault_handler")
173 void ottf_breakpoint_handler(uint32_t *exc_info);
174 
175 OT_WEAK
176 OT_ALIAS("generic_fault_handler")
177 void ottf_load_store_fault_handler(uint32_t *exc_info);
178 
179 OT_WEAK
180 OT_ALIAS("generic_fault_handler")
181 void ottf_machine_ecall_handler(uint32_t *exc_info);
182 
183 OT_WEAK
184 OT_ALIAS("generic_fault_handler")
185 void ottf_user_ecall_handler(uint32_t *exc_info);
186 
187 OT_WEAK
188 void ottf_software_isr(uint32_t *exc_info) {
189  ottf_generic_fault_print(exc_info, "Software IRQ", ibex_mcause_read());
190  abort();
191 }
192 
193 OT_WEAK
194 void ottf_timer_isr(uint32_t *exc_info) {
195  ottf_generic_fault_print(exc_info, "Timer IRQ", ibex_mcause_read());
196  abort();
197 }
198 
199 OT_WEAK
200 bool ottf_console_flow_control_isr(uint32_t *exc_info) { return false; }
201 
202 OT_WEAK
203 void ottf_external_isr(uint32_t *exc_info) {
204  const uint32_t kPlicTarget = 0;
205  dif_rv_plic_irq_id_t plic_irq_id;
206  CHECK_DIF_OK(dif_rv_plic_irq_claim(&ottf_plic, kPlicTarget, &plic_irq_id));
207 
208  dt_instance_id_t devid = dt_plic_id_to_instance_id(plic_irq_id);
209  // See if the test code wants to handle it.
210  bool handled = ottf_handle_irq(exc_info, devid, plic_irq_id);
211  // If not, see if that interrupt corresponds to an OTTF console IRQ.
212  if (handled || ottf_console_flow_control_isr(exc_info)) {
213  // Complete the IRQ at PLIC.
214  CHECK_DIF_OK(
215  dif_rv_plic_irq_complete(&ottf_plic, kPlicTarget, plic_irq_id));
216  return;
217  }
218 
219  LOG_ERROR("unhandled IRQ: plic_id=%d, instance ID=%d", plic_irq_id, devid);
220  ottf_generic_fault_print(exc_info, "External IRQ", ibex_mcause_read());
221  abort();
222 }
223 
224 static void generic_internal_irq_handler(uint32_t *exc_info) {
225  ottf_generic_fault_print(exc_info, "Internal IRQ", ibex_mcause_read());
226  abort();
227 }
228 
229 OT_WEAK
230 bool ottf_handle_irq(uint32_t *exc_info, dt_instance_id_t devid,
231  dif_rv_plic_irq_id_t plic_id) {
232  return false;
233 }
234 
235 OT_WEAK
236 OT_ALIAS("generic_internal_irq_handler")
237 void ottf_external_nmi_handler(uint32_t *exc_info);
238 
239 OT_WEAK
240 OT_ALIAS("generic_internal_irq_handler")
241 void ottf_load_integrity_error_handler(uint32_t *exc_info);
242 
243 OT_WEAK
244 void ottf_internal_isr(uint32_t *exc_info) {
245  uint32_t mcause = ibex_mcause_read();
246  switch ((ibex_internal_irq_t)(mcause)) {
247  case kIbexInternalIrqLoadInteg:
248  ottf_load_integrity_error_handler(exc_info);
249  break;
250  case kIbexInternalIrqNmi:
251  ottf_external_nmi_handler(exc_info);
252  break;
253  default:
254  generic_internal_irq_handler(exc_info);
255  }
256 }