Software APIs
rv_core_ibex_nmi_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 
5 #include <assert.h>
6 #include <limits.h>
7 #include <stdbool.h>
8 #include <stdint.h>
9 
10 #include "dt/dt_alert_handler.h" // Generated
11 #include "dt/dt_aon_timer.h" // Generated
12 #include "dt/dt_pwrmgr.h" // Generated
13 #include "dt/dt_rv_core_ibex.h" // Generated
14 #include "dt/dt_rv_timer.h" // Generated
18 #include "sw/device/lib/dif/dif_rv_core_ibex.h"
21 #include "sw/device/lib/runtime/irq.h"
23 #include "sw/device/lib/testing/alert_handler_testutils.h"
24 #include "sw/device/lib/testing/aon_timer_testutils.h"
25 #include "sw/device/lib/testing/pwrmgr_testutils.h"
26 #include "sw/device/lib/testing/test_framework/check.h"
27 #include "sw/device/lib/testing/test_framework/ottf_isrs.h"
29 
30 OTTF_DEFINE_TEST_CONFIG();
31 
32 typedef void (*isr_handler)(void);
33 
34 static volatile isr_handler expected_isr_handler;
35 static volatile dif_rv_core_ibex_nmi_state_t nmi_state;
36 static volatile dif_alert_handler_class_state_t alert_state;
37 static volatile bool nmi_fired = false;
38 static volatile bool ext_irq_fired = false;
39 static volatile bool irq_is_pending = false;
40 
41 static dif_pwrmgr_t pwrmgr;
42 static dif_rv_core_ibex_t rv_core_ibex;
43 static dif_aon_timer_t aon_timer;
44 static dif_alert_handler_t alert_handler;
45 static dt_pwrmgr_t kPwrmgrDt = kDtPwrmgrAon;
46 
47 /**
48  * Program the alert handler to escalate on alerts upto phase 2 (i.e. reset)
49  * but at the phase 1 (i.e. wipe secrets) the NMI interrupt handler should clear
50  * the escalation.
51  */
52 enum {
53  kEscalationPhase0Micros = 100 * 1000, // 100 ms
54  kEscalationPhase2Micros = 100, // 100 us
55  kIrqDeadlineMicros = 10, // 10 us
56  kWdogBarkMicros = 500, // 500 us
57 };
58 
59 /**
60  * Defines how many times the two NMI tests are repeated back to back.
61  */
62 const int kNumTestIterations = 3;
63 
64 static void alert_handler_config(void) {
65  uint32_t cycles[3] = {0};
66  CHECK_STATUS_OK(alert_handler_testutils_get_cycles_from_us(
67  kEscalationPhase0Micros, &cycles[0]));
68  CHECK_STATUS_OK(alert_handler_testutils_get_cycles_from_us(
69  kEscalationPhase2Micros, &cycles[1]));
70  CHECK_STATUS_OK(alert_handler_testutils_get_cycles_from_us(kIrqDeadlineMicros,
71  &cycles[2]));
74  .signal = 0,
75  .duration_cycles = cycles[0]},
77  .signal = 3,
78  .duration_cycles = cycles[1]}};
79  dif_alert_handler_class_config_t class_config[] = {{
81  .accumulator_threshold = 0,
82  .irq_deadline_cycles = cycles[2],
83  .escalation_phases = esc_phases,
84  .escalation_phases_len = ARRAYSIZE(esc_phases),
85  .crashdump_escalation_phase = kDifAlertHandlerClassStatePhase3,
86  }};
87 
88  dt_alert_id_t alerts[] = {
89  dt_pwrmgr_alert_to_alert_id(kPwrmgrDt, kDtPwrmgrAlertFatalFault)};
90  dif_alert_handler_class_t alert_classes[] = {kDifAlertHandlerClassA};
91  dif_alert_handler_class_t classes[] = {kDifAlertHandlerClassA};
93  .alerts = alerts,
94  .alert_classes = alert_classes,
95  .alerts_len = ARRAYSIZE(alerts),
96  .classes = classes,
97  .class_configs = class_config,
98  .classes_len = ARRAYSIZE(class_config),
99  .ping_timeout = kAlertHandlerTestutilsDefaultPingTimeout,
100  };
101 
102  CHECK_STATUS_OK(
103  alert_handler_testutils_configure_all(&alert_handler, config,
104  /*lock=*/kDifToggleDisabled));
105 }
106 
107 /**
108  * Handle the wdog bark NMI IRQ.
109  */
110 static void wdog_handler(void) {
111  bool is_pending;
112  // The watchdog bark external interrupt is also connected to the NMI input
113  // of rv_core_ibex. We therefore expect the interrupt to be pending on the
114  // peripheral side (the check is done later in the test function).
115  CHECK_DIF_OK(dif_aon_timer_irq_is_pending(
116  &aon_timer, kDifAonTimerIrqWdogTimerBark, &is_pending));
117  irq_is_pending = is_pending;
118 
119  // Stop the watchdog.
120  CHECK_DIF_OK(dif_aon_timer_watchdog_stop(&aon_timer));
121  // In order to handle the NMI we need to acknowledge the interrupt status
122  // bit it at the peripheral side. Note that this test does not use the
123  // PLIC, so there is nothing to acknowledge on the PLIC side.
124  CHECK_DIF_OK(
125  dif_aon_timer_irq_acknowledge(&aon_timer, kDifAonTimerIrqWdogTimerBark));
126 }
127 
128 /**
129  * Handle the alert handler NMI IRQ.
130  */
131 static void alert_handler_handler(void) {
132  bool can_clear;
134  // In this particular case the alert handler is configured to send out an NMI
135  // when class A enters escalation phase 0. We therefore get the alert handler
136  // state and check later in the test function whether class A has indeed
137  // escalated.
139  &alert_handler, kDifAlertHandlerClassA, &state));
140  alert_state = state;
141 
142  // We expect the class to be clearable since it has not been locked.
144  &alert_handler, kDifAlertHandlerClassA, &can_clear));
145  CHECK(can_clear, "Alert handler is locked.");
146 
147  // Clear the class in order to stop the alert handler from sending
148  // an NMI to the processor.
149  CHECK_DIF_OK(dif_alert_handler_escalation_clear(&alert_handler,
150  kDifAlertHandlerClassA));
151 }
152 
153 /**
154  * Execute the wdog bark NMI interrupt test.
155  */
156 static void wdog_nmi_test(void) {
157  // Setup to handle the incoming wdog NMI IRQ.
158  expected_isr_handler = wdog_handler;
159  nmi_fired = false;
160  nmi_state = (dif_rv_core_ibex_nmi_state_t){0};
161 
162  // Setup the wdog bark interrupt.
163  uint32_t count_cycles = 0;
164  CHECK_STATUS_OK(aon_timer_testutils_get_aon_cycles_32_from_us(kWdogBarkMicros,
165  &count_cycles));
166  CHECK_STATUS_OK(
167  aon_timer_testutils_watchdog_config(&aon_timer,
168  /*bark_cycles=*/count_cycles,
169  /*bite_cycles=*/UINT32_MAX,
170  /*pause_in_sleep=*/false));
171  LOG_INFO("Wait for aon_timer NMI");
172  IBEX_SPIN_FOR(nmi_fired, kWdogBarkMicros * 2);
173 
174  // We expect the watchdog bark interrupt to be pending on the peripheral side.
175  CHECK(irq_is_pending, "Expected watchdog bark interrupt to be pending");
176 
177  // Check that the wdog NMI was enabled and barked during the NMI IRQ.
178  CHECK(nmi_state.wdog_enabled && nmi_state.wdog_barked,
179  "Wdog NMI state not expected:\n\t"
180  "wdog_enable:%x\n\twdog_raised:%"
181  "x\n",
182  nmi_state.wdog_enabled, nmi_state.wdog_barked);
183 
184  // Check that the wdog NMI is enable and is cleared.
185  CHECK_DIF_OK(dif_rv_core_ibex_get_nmi_state(
186  &rv_core_ibex, (dif_rv_core_ibex_nmi_state_t *)&nmi_state));
187  CHECK(nmi_state.wdog_enabled && !nmi_state.wdog_barked,
188  "Wdog NMI state not expected:\n\t"
189  "wdog_enable:%x\n\twdog_raised:%"
190  "x\n",
191  nmi_state.wdog_enabled, nmi_state.wdog_barked);
192 }
193 
194 /**
195  * Execute the alert handler NMI interrupt test.
196  */
197 static void alert_handler_nmi_test(void) {
198  alert_handler_config();
199 
200  // Setup to handle the incoming alert handler NMI IRQ.
201  expected_isr_handler = alert_handler_handler;
202  nmi_fired = false;
203  nmi_state = (dif_rv_core_ibex_nmi_state_t){0};
204 
205  // Trigger the alert handler to escalate.
206  CHECK_DIF_OK(dif_pwrmgr_alert_force(&pwrmgr, kDifPwrmgrAlertFatalFault));
207 
208  LOG_INFO("Wait for alert NMI");
209  IBEX_SPIN_FOR(nmi_fired, kEscalationPhase0Micros);
210 
211  // We expect class A to be in escalation phase 0 at this point.
212  CHECK(alert_state == kDifAlertHandlerClassStatePhase0,
213  "Alert handler class A is expected to be in phase 0.");
214 
215  // Check that the alert handler NMI was enabled and raised an alert during the
216  // NMI IRQ.
217  CHECK(nmi_state.alert_enabled && nmi_state.alert_raised,
218  "Alert handler NMI state not expected:\n\t"
219  "alert_enable:%x\n\talert_raised:%x\n",
220  nmi_state.alert_enabled, nmi_state.alert_raised);
221 
222  // Check that the alert handler NMI is enabled and is cleared.
223  CHECK_DIF_OK(dif_rv_core_ibex_get_nmi_state(
224  &rv_core_ibex, (dif_rv_core_ibex_nmi_state_t *)&nmi_state));
225  CHECK(nmi_state.alert_enabled && !nmi_state.alert_raised,
226  "Alert handler NMI state not expected:\n\t"
227  "alert_enable:%x\n\talert_raised:%x\n",
228  nmi_state.alert_enabled, nmi_state.alert_raised);
229 }
230 
231 /**
232  * OTTF external NMI internal IRQ handler.
233  * This functions overrides the OTTF weak definition.
234  * It calls the `expected_isr_handler` function pointer that is previously set
235  * by the test to handler the specific peripheral IRQ.
236  */
237 void ottf_external_nmi_handler(uint32_t *exc_info) {
238  nmi_fired = true;
239 
240  expected_isr_handler();
241 
242  CHECK_DIF_OK(dif_rv_core_ibex_get_nmi_state(
243  &rv_core_ibex, (dif_rv_core_ibex_nmi_state_t *)&nmi_state));
244  CHECK_DIF_OK(dif_rv_core_ibex_clear_nmi_state(&rv_core_ibex,
245  kDifRvCoreIbexNmiSourceAll));
246 }
247 
248 /**
249  * OTTF external IRQ handler
250  * This functions overrides the OTTF weak definition.
251  */
252 void ottf_external_isr(uint32_t *exc_info) { ext_irq_fired = true; }
253 
254 /**
255  * Initialized all peripherals used in this test.
256  */
257 void init_peripherals(void) {
258  static_assert(kDtRvCoreIbexCount >= 1, "This test requires an Ibex core");
259  CHECK_DIF_OK(
260  dif_rv_core_ibex_init_from_dt((dt_rv_core_ibex_t)0, &rv_core_ibex));
261 
262  CHECK_DIF_OK(dif_aon_timer_init_from_dt(kDtAonTimerAon, &aon_timer));
263 
264  static_assert(kDtAlertHandlerCount >= 1, "This test needs an Alert Handler");
265  CHECK_DIF_OK(
266  dif_alert_handler_init_from_dt((dt_alert_handler_t)0, &alert_handler));
267 
268  CHECK_DIF_OK(dif_pwrmgr_init_from_dt(kPwrmgrDt, &pwrmgr));
269 }
270 /**
271  * This test do the following steps:
272  * - Trigger a watchdog bark to generate a NMI.
273  * - Check rv_core_ibex's NMI interrupt register and clear the interrupt.
274  * - Repeat the previous steps with alert handler.
275  */
276 bool test_main(void) {
277  // Disable external interrupts via the PLIC entirely.
278  // The NMI should still get through to the processor regardless.
279  irq_global_ctrl(false);
280  irq_external_ctrl(false);
281  init_peripherals();
282 
283  // Enable both NMIs.
284  CHECK_DIF_OK(
285  dif_rv_core_ibex_enable_nmi(&rv_core_ibex, kDifRvCoreIbexNmiSourceWdog));
286  CHECK_DIF_OK(
287  dif_rv_core_ibex_enable_nmi(&rv_core_ibex, kDifRvCoreIbexNmiSourceAlert));
288 
289  // Execute both NMI tests several times back-to-back to verify that
290  // the causes are successfully cleared in the NMI handlers.
291  for (int k = 0; k < kNumTestIterations; ++k) {
292  LOG_INFO("Test iteration %d begin", k);
293  alert_handler_nmi_test();
294  wdog_nmi_test();
295  LOG_INFO("Test iteration %d end", k);
296  }
297 
298  // Double check that no external interrupts have occurred.
299  CHECK(ext_irq_fired == false, "Unexpected external interrupt triggered.");
300  // Double check that the system has not been reset due to escalation and that
301  // the reset reason is still POR.
302  return UNWRAP(pwrmgr_testutils_is_wakeup_reason(&pwrmgr, 0));
303 }