Software APIs
rstmgr_cpu_info_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 
5 #include "dt/dt_pwrmgr.h" // Generated
6 #include "dt/dt_rstmgr.h" // Generated
7 #include "dt/dt_rv_core_ibex.h" // Generated
13 #include "sw/device/lib/dif/dif_rv_core_ibex.h"
15 #include "sw/device/lib/testing/aon_timer_testutils.h"
16 #include "sw/device/lib/testing/rstmgr_testutils.h"
17 #include "sw/device/lib/testing/rv_core_ibex_testutils.h"
18 #include "sw/device/lib/testing/test_framework/check.h"
19 #include "sw/device/lib/testing/test_framework/ottf_isrs.h"
21 
22 OTTF_DEFINE_TEST_CONFIG();
23 
24 /**
25  * RSTMGR CPU INFO TEST
26  *
27  * This has three stages:
28  *
29  * 1. After the first startup, an illegal memory access is performed.
30  * In the exception handler, a software reset is triggered.
31  *
32  * 2. After the software reset, the CPU info dump is checked against
33  * the expected values for this single fault. The watch dog is then set up
34  * and another illegal memory access is performed. Only this time
35  * the exception handler performs another illegal read.
36  * Causing the ibex to be halted by the alert handler.
37  * The watch dog will eventually trigger a reset.
38  *
39  * 3. After the watchdog reset, the CPU info dump is checked against
40  * the expected values for this double fault.
41  */
42 
43 // CPU Dump Size and Unmapped Addresses.
44 enum {
45  kCpuDumpSize = 8,
46  kIllegalAddr0 = 0xF0000000,
47  kIllegalAddr1 = 0xF0000004,
48  kIllegalAddr2 = 0x00000008,
49 };
50 
51 // Declaring the labels used to calculate the expected current and next pc
52 // after a double fault.
53 extern const uint32_t _ottf_interrupt_vector;
54 
55 // The labels to points in the code of which the memory address is needed.
56 extern const char kSingleFaultAddr[];
57 extern const char kSingleFaultAddrCurrentPc[];
58 extern const char kSingleFaultAddrNextPc[];
59 extern const char kDoubleFaultFirstAddr[];
60 extern const char kDoubleFaultSecondAddr[];
61 
62 // A handle to the reset manager.
63 static dif_rstmgr_t rstmgr;
64 
65 // This variable is used to ensure loads from an address aren't optimised out.
66 volatile static uint32_t addr_val;
67 
68 /**
69  * When true, the exception handler will trigger another fault,
70  * causing a double fault,
71  * otherwise it triggers a software reset.
72  */
73 volatile static bool double_fault;
74 
75 /**
76  * Overrides the default OTTF exception handler.
77  */
78 void ottf_exception_handler(uint32_t *exc_info) {
79  if (double_fault) {
80  OT_ADDRESSABLE_LABEL(kDoubleFaultSecondAddr);
81  mmio_region_write32(mmio_region_from_addr(kIllegalAddr2), 0, 0);
82  } else {
83  CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr));
84  // Write to `addr_val` so that the 'last data access' address is
85  // a known value (the address of addr_val).
86  addr_val = 1;
87  OT_ADDRESSABLE_LABEL(kSingleFaultAddrCurrentPc);
88  wait_for_interrupt(); // Wait for the reset.
89  OT_ADDRESSABLE_LABEL(kSingleFaultAddrNextPc);
90  addr_val = 2;
91  }
92  CHECK(false,
93  "This point should be unreachable; "
94  "a reset or another fault should have occured.");
95 }
96 
97 /**
98  * Gets, parses and returns the cpu info crash dump.
99  *
100  * @param ibex A handle to the ibex.
101  * @return The cpu info crash dump.
102  */
103 static dif_rv_core_ibex_crash_dump_info_t get_dump(
104  const dif_rv_core_ibex_t *ibex) {
105  size_t size_read;
107 
108  CHECK_DIF_OK(dif_rstmgr_cpu_info_dump_read(
109  &rstmgr, dump, DIF_RSTMGR_CPU_INFO_MAX_SIZE, &size_read));
110  CHECK(size_read == kCpuDumpSize,
111  "The observed cpu info dump's size was %d, "
112  "but it was expected to be %d",
113  size_read, kCpuDumpSize);
114 
116  CHECK_DIF_OK(
117  dif_rv_core_ibex_parse_crash_dump(ibex, dump, size_read, &output));
118  return output;
119 }
120 
121 /**
122  * Checks the 'current' section of the cpu info dump against the given expected
123  * values.
124  *
125  * For double faults the PCs may differ from the expected ones due to pipeline
126  * stalls.
127  *
128  * @param obs_state The cpu info crash dump's current state values.
129  * @param exp_state The expected values of the current state.
130  * @param double_fault The states correspond to a double fault.
131  */
132 static void check_state(dif_rv_core_ibex_crash_dump_state_t obs_state,
134  dif_toggle_t double_fault) {
135  RV_CORE_IBEX_TESTUTILS_PRINT_CRASH_DUMP(obs_state);
136  RV_CORE_IBEX_TESTUTILS_PRINT_CRASH_DUMP(exp_state);
137 
138  CHECK(exp_state.mtval == obs_state.mtval,
139  "Last Exception Access Addr: Expected 0x%x != Observed 0x%x",
140  exp_state.mtval, obs_state.mtval);
141  // Check the current pc is either the expected or expected + 4, since the
142  // pipeline may have stalled.
143  CHECK(exp_state.mcpc == obs_state.mcpc ||
144  (double_fault == kDifToggleEnabled &&
145  exp_state.mcpc - 4 == obs_state.mcpc),
146  "Current PC: Observed 0x%x not within 4 bytes of Expected 0x%x",
147  obs_state.mcpc, exp_state.mcpc);
148  CHECK(exp_state.mnpc == obs_state.mnpc,
149  "Next PC: Expected 0x%x != Observed 0x%x", exp_state.mnpc,
150  obs_state.mnpc);
151  CHECK(exp_state.mdaa == obs_state.mdaa,
152  "Last Data Access Addr: Expected 0x%x != Observed 0x%x", exp_state.mdaa,
153  obs_state.mdaa);
154  CHECK(exp_state.mpec == obs_state.mpec,
155  "The Observed MPEC, 0x%x, was not in the expected 0x%x", obs_state.mpec,
156  exp_state.mpec);
157 }
158 
159 /**
160  * Checks the 'previous' section of the cpu info dump against the given expected
161  * values.
162  *
163  * @param obs_prev_state The cpu info crash dump's previous state values.
164  * @param exp_prev_state The expected values of the previous state.
165  */
166 static void check_prev_state(
169  RV_CORE_IBEX_TESTUTILS_PRINT_CRASH_PREVIOUS_DUMP(obs_prev_state);
170  RV_CORE_IBEX_TESTUTILS_PRINT_CRASH_PREVIOUS_DUMP(exp_prev_state);
171  CHECK(exp_prev_state.mtval == obs_prev_state.mtval,
172  "Last Exception Access Addr: Expected 0x%x != Observed 0x%x",
173  exp_prev_state.mtval, obs_prev_state.mtval);
174  CHECK(exp_prev_state.mpec == obs_prev_state.mpec,
175  "The Observed Previous MPEC, 0x%x, "
176  "was not in the expected 0x%x",
177  obs_prev_state.mpec, exp_prev_state.mpec);
178 }
179 
180 bool test_main(void) {
182 
183  dif_aon_timer_t aon_timer;
184  dif_pwrmgr_t pwrmgr;
185  dif_rv_core_ibex_t ibex;
186  dt_rv_core_ibex_t ibex_dt = (dt_rv_core_ibex_t)0;
187  static_assert(kDtRvCoreIbexCount >= 1,
188  "This test requires at least 1 Ibex core.");
189 
190  // Initialize Handles.
191  CHECK_DIF_OK(dif_rstmgr_init_from_dt(kDtRstmgrAon, &rstmgr));
192  CHECK_DIF_OK(dif_aon_timer_init_from_dt(kDtAonTimerAon, &aon_timer));
193  CHECK_DIF_OK(dif_pwrmgr_init_from_dt(kDtPwrmgrAon, &pwrmgr));
194  CHECK_DIF_OK(dif_rv_core_ibex_init_from_dt(ibex_dt, &ibex));
195 
196  switch (rstmgr_testutils_reason_get()) {
197  case kDifRstmgrResetInfoPor: // The first power-up.
198  LOG_INFO("Triggering single fault.");
199 
200  // Enable cpu info.
201  CHECK_DIF_OK(dif_rstmgr_cpu_info_set_enabled(&rstmgr, kDifToggleEnabled));
202 
203  double_fault = false;
204  OT_ADDRESSABLE_LABEL(kSingleFaultAddr);
205  addr_val = mmio_region_read32(mmio_region_from_addr(kIllegalAddr0), 0);
206  CHECK(false,
207  "This should be unreachable; a single fault should have occurred.");
208  break;
209 
210  case kDifRstmgrResetInfoSw: // The power-up after the single fault.
211  LOG_INFO("Checking CPU info dump after single fault.");
212 
213  dump = get_dump(&ibex);
214 
215  CHECK(
217  "CPU Info dump shows a double fault after experiencing only a single "
218  "fault.");
219 
220  check_state(dump.fault_state,
222  .mtval = (uint32_t)kIllegalAddr0,
223  .mpec = (uint32_t)kSingleFaultAddr + 4,
224  .mdaa = (uint32_t)&addr_val,
225  .mcpc = (uint32_t)kSingleFaultAddrCurrentPc,
226  .mnpc = (uint32_t)kSingleFaultAddrNextPc,
227  },
229 
230  LOG_INFO("Setting up watch dog and triggering a double fault.");
231  // When we boot through the ROM the watchdog is configured to generate an
232  // NMI on barking, so we want the watchdog to bark before the first
233  // exception to avoid the NMI messing with the fault dump.
234  uint32_t bark_cycles = 0;
235  CHECK_STATUS_OK(
236  aon_timer_testutils_get_aon_cycles_32_from_us(1, &bark_cycles));
237  uint32_t bite_cycles = 0;
238  CHECK_STATUS_OK(
239  aon_timer_testutils_get_aon_cycles_32_from_us(100, &bite_cycles));
240 
241  // Set wdog as a reset source.
242  CHECK_DIF_OK(dif_pwrmgr_set_request_sources(
243  &pwrmgr, kDifPwrmgrReqTypeReset, kDifPwrmgrResetRequestSourceTwo,
245  // Setup the watchdog bark and bite timeouts.
246  CHECK_STATUS_OK(aon_timer_testutils_watchdog_config(
247  &aon_timer, bark_cycles, bite_cycles, false));
248  // Enable cpu info.
249  CHECK_DIF_OK(dif_rstmgr_cpu_info_set_enabled(&rstmgr, kDifToggleEnabled));
250 
251  double_fault = true;
252  OT_ADDRESSABLE_LABEL(kDoubleFaultFirstAddr);
253  addr_val = mmio_region_read32(mmio_region_from_addr(kIllegalAddr1), 0);
254  CHECK(false,
255  "This should be unreachable; a double fault should have occured.");
256  break;
257 
258  case kDifRstmgrResetInfoWatchdog: // The power-up after the double fault.
259  LOG_INFO("Checking CPU info dump after double fault.");
260 
261  dump = get_dump(&ibex);
262 
263  CHECK(dump.double_fault == kDifToggleEnabled,
264  "CPU Info dump doesn't show a double fault has happened.");
265 
266  // After #15219 was merged, the execution stops more predictably
267  // once fetch_en is dropped due to a double fault.
268  // The current pc should now correspond to the instruction after the
269  // cause of the illegal load or that same instruction, depending on
270  // whether the pipeline stalled.
271  // The next pc is always the exception handler, because that's
272  // where execution would have gone if it had not halted
273  check_state(dump.fault_state,
275  .mtval = (uint32_t)kIllegalAddr2,
276  .mpec = (uint32_t)kDoubleFaultSecondAddr,
277  .mdaa = (uint32_t)kIllegalAddr2,
278  .mcpc = (uint32_t)kDoubleFaultSecondAddr + 4,
279  .mnpc = (uint32_t)&_ottf_interrupt_vector,
280  },
282 
283  check_prev_state(dump.previous_fault_state,
285  .mtval = (uint32_t)kIllegalAddr1,
286  .mpec = (uint32_t)kDoubleFaultFirstAddr + 4,
287  });
288 
289  return true;
290 
291  default:
292  CHECK(false, "Device was reset by an unexpected source.");
293  break;
294  }
295  return false;
296 }