Software APIs
alert_handler_ping_ok_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 
14 #include "sw/device/lib/dif/dif_rv_core_ibex.h"
17 #include "sw/device/lib/runtime/irq.h"
19 #include "sw/device/lib/testing/alert_handler_testutils.h"
20 #include "sw/device/lib/testing/pwrmgr_testutils.h"
21 #include "sw/device/lib/testing/test_framework/check.h"
22 #include "sw/device/lib/testing/test_framework/ottf_isrs.h"
24 
26 #include "sw/device/lib/testing/autogen/isr_testutils.h"
27 
28 OTTF_DEFINE_TEST_CONFIG();
29 
30 static const dt_pwrmgr_t kPwrmgrDt = 0;
31 static_assert(kDtPwrmgrCount == 1, "this test expects a pwrmgr");
32 static const dt_rv_timer_t kRvTimerDt = 0;
33 static_assert(kDtRvTimerCount >= 1, "this test expects at least one rv_timer");
34 static const dt_alert_handler_t kAlertHandlerDt = 0;
35 static_assert(kDtAlertHandlerCount == 1, "this test expects an alert_handler");
36 static const dt_rv_core_ibex_t kRvCoreIbexDt = 0;
37 static_assert(kDtRvCoreIbexCount == 1, "this test expects exactly one Ibex");
38 
39 typedef struct counters {
40  uint32_t alert_raised;
41  uint32_t wdog_barked;
42  uint64_t elapsed_ticks;
43 } counters_t;
44 
45 static volatile counters_t counters = {0};
46 
47 static volatile dif_rv_core_ibex_nmi_state_t nmi_state;
48 static volatile bool ext_irq_fired = false;
49 
50 static dif_pwrmgr_t pwrmgr;
51 static dif_rv_core_ibex_t rv_core_ibex;
52 static dif_alert_handler_t alert_handler;
53 static dif_rv_timer_t timer;
54 
55 /**
56  * Program the alert handler to escalate on alerts upto phase 2 (i.e. reset)
57  * but at the phase 1 (i.e. wipe secrets) the NMI interrupt handler should clear
58  * the escalation.
59  */
60 enum {
61  kEscalationPhase0Micros = 100 * 1000, // 100 ms
62  kEscalationPhase2Micros = 100, // 100 us
63  kIrqDeadlineMicros = 10, // 10 us
64  kTick1us = 1 * 1000 * 1000, // 1MHz - 1us.
66  kTimeThresholdUs = 5000, // 5ms expressed in microseconds.
67 };
68 
69 static status_t set_tick(uint32_t tick_hz) {
70  dif_rv_timer_tick_params_t tick_params;
72  &tick_params));
73  TRY(dif_rv_timer_set_tick_params(&timer, kHart, tick_params));
74  return OK_STATUS();
75 }
76 
77 static void alerts_configure_all(const dif_alert_handler_t *alert_handler,
79  dif_toggle_t locked) {
80  // Check lengths of alert, local alert, and class arrays.
81  CHECK((config.alerts_len > 0 && config.alerts != NULL &&
82  config.alert_classes != NULL) ||
83  (config.alerts_len == 0 && config.alerts == NULL &&
84  config.alert_classes == NULL));
85  CHECK((config.local_alerts_len > 0 && config.local_alerts != NULL &&
86  config.local_alert_classes != NULL) ||
87  (config.local_alerts_len == 0 && config.local_alerts == NULL &&
88  config.local_alert_classes == NULL));
89  CHECK((config.classes_len > 0 && config.classes != NULL &&
90  config.class_configs != NULL) ||
91  (config.classes_len == 0 && config.classes == NULL &&
92  config.class_configs == NULL));
93 
94  // Check that the provided ping timeout actually fits in the timeout
95  // register, which is smaller than a native word length.
96  CHECK(config.ping_timeout <=
97  ALERT_HANDLER_PING_TIMEOUT_CYC_SHADOWED_PING_TIMEOUT_CYC_SHADOWED_MASK);
98 
99  // Configure and enable the requested alerts.
100  for (int i = 0; i < config.alerts_len; ++i) {
102  alert_handler, config.alerts[i], config.alert_classes[i],
103  kDifToggleEnabled, locked));
104  }
105 
106  // Configure and enable the requested local alerts.
107  for (int i = 0; i < config.local_alerts_len; ++i) {
109  alert_handler, config.local_alerts[i], config.local_alert_classes[i],
110  kDifToggleEnabled, locked));
111  }
112 
113  // Configure and enable the requested classes.
114  for (int i = 0; i < config.classes_len; ++i) {
116  alert_handler, config.classes[i], config.class_configs[i],
117  kDifToggleEnabled, locked));
118  }
119 }
120 
121 static void alert_handler_config(void) {
122  uint32_t cycles[3] = {0};
123  CHECK_STATUS_OK(alert_handler_testutils_get_cycles_from_us(
124  kEscalationPhase0Micros, &cycles[0]));
125  CHECK_STATUS_OK(alert_handler_testutils_get_cycles_from_us(
126  kEscalationPhase2Micros, &cycles[1]));
127  CHECK_STATUS_OK(alert_handler_testutils_get_cycles_from_us(kIrqDeadlineMicros,
128  &cycles[2]));
129  dif_alert_handler_escalation_phase_t esc_phases[] = {
131  .signal = 0,
132  .duration_cycles = cycles[0]},
134  .signal = 3,
135  .duration_cycles = cycles[1]}};
136  dif_alert_handler_class_config_t class_config[] = {{
138  .accumulator_threshold = 0,
139  .irq_deadline_cycles = cycles[2],
140  .escalation_phases = esc_phases,
141  .escalation_phases_len = ARRAYSIZE(esc_phases),
142  .crashdump_escalation_phase = kDifAlertHandlerClassStatePhase3,
143  }};
144 
146  dif_alert_handler_class_t alert_classes[] = {kDifAlertHandlerClassA};
147  dif_alert_handler_class_t classes[] = {kDifAlertHandlerClassA};
148  dif_alert_handler_config_t config = {
149  .alerts = alerts,
150  .alert_classes = alert_classes,
151  .alerts_len = ARRAYSIZE(alerts),
152  .classes = classes,
153  .class_configs = class_config,
154  .classes_len = ARRAYSIZE(class_config),
155  .ping_timeout = kAlertHandlerTestutilsDefaultPingTimeout,
156  };
157 
158  alerts_configure_all(&alert_handler, config, /*lock=*/kDifToggleEnabled);
159 
160  CHECK_STATUS_OK(set_tick(kTick1us));
161  counters.elapsed_ticks = 0;
162  CHECK_DIF_OK(
163  dif_rv_timer_counter_write(&timer, kHart, counters.elapsed_ticks));
164  CHECK_DIF_OK(dif_rv_timer_counter_read(&timer, kHart,
165  (uint64_t *)&counters.elapsed_ticks));
166  CHECK(counters.elapsed_ticks == 0, "Failed to write the counter");
167 
168  CHECK_DIF_OK(
171  &alert_handler, config.ping_timeout, kDifToggleEnabled,
173 }
174 
175 /**
176  * Execute the alert handler NMI interrupt test.
177  */
178 static void alert_handler_ping_ok_test(void) {
179  alert_handler_config();
180 
181  uint64_t theshold_us = kTimeThresholdUs;
183  theshold_us = theshold_us * 10;
184  CHECK(theshold_us > kTimeThresholdUs, "Threshold overflow");
185  }
186 
187  do {
188  CHECK_DIF_OK(dif_rv_timer_counter_read(
189  &timer, kHart, (uint64_t *)&counters.elapsed_ticks));
190  } while (counters.elapsed_ticks < theshold_us);
191 
192  CHECK_DIF_OK(
194 }
195 
196 /**
197  * OTTF external NMI internal IRQ handler.
198  * This functions overrides the OTTF weak definition.
199  */
200 void ottf_external_nmi_handler(uint32_t *exc_info) {
201  CHECK_DIF_OK(dif_rv_core_ibex_get_nmi_state(
202  &rv_core_ibex, (dif_rv_core_ibex_nmi_state_t *)&nmi_state));
203 
204  if (nmi_state.wdog_barked) {
205  counters.wdog_barked++;
206  }
207  if (nmi_state.alert_raised) {
208  counters.alert_raised++;
209  }
210 
211  CHECK_DIF_OK(dif_rv_core_ibex_clear_nmi_state(&rv_core_ibex,
212  kDifRvCoreIbexNmiSourceAll));
213 }
214 
215 /**
216  * OTTF external IRQ handler
217  * This functions overrides the OTTF weak definition.
218  */
219 void ottf_external_isr(uint32_t *exc_info) { ext_irq_fired = true; }
220 
221 void init_peripherals(void) {
222  CHECK_DIF_OK(dif_rv_core_ibex_init_from_dt(kRvCoreIbexDt, &rv_core_ibex));
223 
224  CHECK_DIF_OK(dif_alert_handler_init_from_dt(kAlertHandlerDt, &alert_handler));
225 
226  CHECK_DIF_OK(dif_pwrmgr_init_from_dt(kPwrmgrDt, &pwrmgr));
227 
228  CHECK_DIF_OK(dif_rv_timer_init_from_dt(kRvTimerDt, &timer));
229  CHECK_DIF_OK(dif_rv_timer_reset(&timer));
230 }
231 
232 bool test_main(void) {
233  // Disable external interrupts via the PLIC entirely.
234  // The NMI should still get through to the processor regardless.
235  irq_global_ctrl(false);
236  irq_external_ctrl(false);
237  init_peripherals();
238 
239  CHECK_DIF_OK(
240  dif_rv_core_ibex_enable_nmi(&rv_core_ibex, kDifRvCoreIbexNmiSourceAll));
241 
242  alert_handler_ping_ok_test();
243 
244  // Double check that no external interrupts have occurred.
245  CHECK(ext_irq_fired == false, "Unexpected external interrupt triggered.");
246  CHECK(counters.alert_raised == 0, "Unexpected alert raised.");
247  CHECK(counters.wdog_barked == 0, "Unexpected watchdog bark.");
248  // Double check that the system has not been reset due to escalation and that
249  // the reset reason is still POR.
250  return UNWRAP(pwrmgr_testutils_is_wakeup_reason(&pwrmgr, 0));
251 }