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