Software APIs
aon_timer_wdog_lc_escalate_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 
18 #include "sw/device/lib/runtime/irq.h"
20 #include "sw/device/lib/testing/alert_handler_testutils.h"
21 #include "sw/device/lib/testing/aon_timer_testutils.h"
22 #include "sw/device/lib/testing/rstmgr_testutils.h"
23 #include "sw/device/lib/testing/rv_plic_testutils.h"
24 #include "sw/device/lib/testing/test_framework/FreeRTOSConfig.h"
25 #include "sw/device/lib/testing/test_framework/check.h"
27 
29 
30 OTTF_DEFINE_TEST_CONFIG();
31 
32 /**
33  * Program the alert handler to escalate on alerts upto phase 2 (i.e. reset) but
34  * the phase 1 (i.e. wipe secrets) should occur and last during the time the
35  * wdog is programmed to bark.
36  */
37 enum {
38  kWdogBarkMicros = 3 * 100, // 300 us
39  kWdogBiteMicros = 4 * 100, // 400 us
40  kEscalationPhase0Micros = 1 * 100, // 100 us
41  // The cpu value is set slightly larger, since if they are the same it is
42  // possible busy_spin_micros in execute_test can complete before the
43  // interrupt.
44  kEscalationPhase0MicrosCpu = kEscalationPhase0Micros + 20, // 120 us
45  kEscalationPhase1Micros = 5 * 100, // 500 us
46  kEscalationPhase2Micros = 100, // 100 us
47 };
48 
49 static_assert(
50  kWdogBarkMicros < kWdogBiteMicros &&
51  kWdogBarkMicros > kEscalationPhase0Micros &&
52  kWdogBarkMicros < (kEscalationPhase0Micros + kEscalationPhase1Micros) &&
53  kWdogBiteMicros < (kEscalationPhase0Micros + kEscalationPhase1Micros),
54  "The wdog bark and bite shall happens during the escalation phase 1");
55 
56 /**
57  * Objects to access the peripherals used in this test via dif API.
58  */
59 static const uint32_t kPlicTarget = kTopEarlgreyPlicTargetIbex0;
60 static dif_aon_timer_t aon_timer;
61 static dif_rv_plic_t plic;
62 static dif_pwrmgr_t pwrmgr;
63 static dif_rstmgr_t rstmgr;
64 static dif_alert_handler_t alert_handler;
65 
66 /**
67  * External ISR.
68  *
69  * Handles all peripheral interrupts on Ibex. PLIC asserts an external interrupt
70  * line to the CPU, which results in a call to this OTTF ISR. This ISR
71  * overrides the default OTTF implementation.
72  */
73 void ottf_external_isr(uint32_t *exc_info) {
74  dif_rv_plic_irq_id_t irq_id;
75  CHECK_DIF_OK(dif_rv_plic_irq_claim(&plic, kPlicTarget, &irq_id));
76 
79 
80  if (peripheral == kTopEarlgreyPlicPeripheralAonTimerAon) {
81  uint32_t irq =
82  (irq_id - (dif_rv_plic_irq_id_t)
84 
85  // We should not get aon timer interrupts since escalation suppresses them.
86  LOG_ERROR("Unexpected aon timer interrupt %d", irq);
87  } else if (peripheral == kTopEarlgreyPlicPeripheralAlertHandler) {
88  // Check the class.
91  &alert_handler, kDifAlertHandlerClassA, &state));
92  CHECK(state == kDifAlertHandlerClassStatePhase0, "Wrong phase %d", state);
93 
94  uint32_t irq =
95  (irq_id -
97 
98  // Deals with the alert cause: we expect it to be from the pwrmgr.
100  bool is_cause = false;
101  CHECK_DIF_OK(
102  dif_alert_handler_alert_is_cause(&alert_handler, alert, &is_cause));
103  CHECK(is_cause);
104 
105  // Acknowledge the cause and irq; neither of them affect escalation.
106  CHECK_DIF_OK(dif_alert_handler_alert_acknowledge(&alert_handler, alert));
107  CHECK_DIF_OK(
108  dif_alert_handler_alert_is_cause(&alert_handler, alert, &is_cause));
109  CHECK(!is_cause);
110 
111  CHECK_DIF_OK(dif_alert_handler_irq_acknowledge(&alert_handler, irq));
112  }
113 
114  // Complete the IRQ by writing the IRQ source to the Ibex specific CC
115  // register.
116  CHECK_DIF_OK(dif_rv_plic_irq_complete(&plic, kPlicTarget, irq_id));
117 }
118 
119 /**
120  * Initialize the peripherals used in this test.
121  */
122 void init_peripherals(void) {
123  mmio_region_t base_addr =
125  CHECK_DIF_OK(dif_pwrmgr_init(base_addr, &pwrmgr));
126 
128  CHECK_DIF_OK(dif_rstmgr_init(base_addr, &rstmgr));
129 
131  CHECK_DIF_OK(dif_aon_timer_init(base_addr, &aon_timer));
132 
134  CHECK_DIF_OK(dif_rv_plic_init(base_addr, &plic));
135 
136  rv_plic_testutils_irq_range_enable(
139 
141  CHECK_DIF_OK(dif_alert_handler_init(base_addr, &alert_handler));
142 }
143 
144 static uint32_t udiv64_slow_into_u32(uint64_t a, uint64_t b,
145  uint64_t *rem_out) {
146  const uint64_t result = udiv64_slow(a, b, rem_out);
147  CHECK(result <= UINT32_MAX, "Result of division must fit in uint32_t");
148  return (uint32_t)result;
149 }
150 
151 /**
152  * Program the alert handler to escalate on alerts upto phase 2 (i.e. reset) but
153  * the phase 1 (i.e. wipe secrets) should occur and last during the time the
154  * wdog is programed to bark.
155  */
156 static void alert_handler_config(void) {
158  dif_alert_handler_class_t alert_classes[] = {kDifAlertHandlerClassA};
159 
160  dif_alert_handler_escalation_phase_t esc_phases[] = {
162  .signal = 0,
163  .duration_cycles = udiv64_slow_into_u32(
164  kEscalationPhase0Micros * kClockFreqPeripheralHz, 1000000, NULL)},
166  .signal = 1,
167  .duration_cycles = udiv64_slow_into_u32(
168  kEscalationPhase1Micros * kClockFreqPeripheralHz, 1000000, NULL)},
170  .signal = 3,
171  .duration_cycles = udiv64_slow_into_u32(
172  kEscalationPhase2Micros * kClockFreqPeripheralHz, 1000000, NULL)}};
173 
174  dif_alert_handler_class_config_t class_config[] = {{
176  .accumulator_threshold = 0,
177  .irq_deadline_cycles =
178  udiv64_slow_into_u32(10 * kClockFreqPeripheralHz, 1000000, NULL),
179  .escalation_phases = esc_phases,
180  .escalation_phases_len = ARRAYSIZE(esc_phases),
181  .crashdump_escalation_phase = kDifAlertHandlerClassStatePhase3,
182  }};
183 
184  dif_alert_handler_class_t classes[] = {kDifAlertHandlerClassA};
185  dif_alert_handler_config_t config = {
186  .alerts = alerts,
187  .alert_classes = alert_classes,
188  .alerts_len = ARRAYSIZE(alerts),
189  .classes = classes,
190  .class_configs = class_config,
191  .classes_len = ARRAYSIZE(class_config),
192  .ping_timeout = kAlertHandlerTestutilsDefaultPingTimeout,
193  };
194 
195  CHECK_STATUS_OK(alert_handler_testutils_configure_all(&alert_handler, config,
197  // Enables alert handler irq.
198  CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
199  &alert_handler, kDifAlertHandlerIrqClassa, kDifToggleEnabled));
200 }
201 
202 /**
203  * Execute the aon timer interrupt test.
204  */
205 static void execute_test(dif_aon_timer_t *aon_timer) {
206  uint32_t bark_cycles = 0;
207  CHECK_STATUS_OK(aon_timer_testutils_get_aon_cycles_32_from_us(kWdogBarkMicros,
208  &bark_cycles));
209  uint32_t bite_cycles = 0;
210  CHECK_STATUS_OK(aon_timer_testutils_get_aon_cycles_32_from_us(kWdogBiteMicros,
211  &bite_cycles));
212 
213  LOG_INFO(
214  "Wdog will bark after %u/%u us/cycles and bite after %u/%u us/cycles",
215  (uint32_t)kWdogBarkMicros, bark_cycles, (uint32_t)kWdogBiteMicros,
216  bite_cycles);
217 
218  // Setup the wdog bark and bite timeouts.
219  CHECK_STATUS_OK(
220  aon_timer_testutils_watchdog_config(aon_timer, bark_cycles, bite_cycles,
221  /*pause_in_sleep=*/false));
222 
223  // Trigger the alert handler to escalate.
224  dif_pwrmgr_alert_t alert = kDifPwrmgrAlertFatalFault;
225  CHECK_DIF_OK(dif_pwrmgr_alert_force(&pwrmgr, alert));
226  busy_spin_micros(kEscalationPhase0MicrosCpu);
227 
228  // This should never run since escalation turns the CPU off.
229  CHECK(false, "The alert handler failed to escalate");
230 }
231 
232 bool test_main(void) {
233  // Enable global and external IRQ at Ibex.
234  irq_global_ctrl(true);
235  irq_external_ctrl(true);
236 
237  init_peripherals();
238 
239  // Enable all the AON interrupts used in this test.
240  rv_plic_testutils_irq_range_enable(&plic, kPlicTarget,
243 
244  alert_handler_config();
245 
246  // Check if there was a HW reset caused by the escalation.
248  rst_info = rstmgr_testutils_reason_get();
249  rstmgr_testutils_reason_clear();
250 
251  CHECK(rst_info == kDifRstmgrResetInfoPor ||
252  rst_info == kDifRstmgrResetInfoEscalation,
253  "Wrong reset reason %02X", rst_info);
254 
255  if (rst_info == kDifRstmgrResetInfoPor) {
256  LOG_INFO("Booting for the first time, starting test");
257  execute_test(&aon_timer);
258  } else if (rst_info == kDifRstmgrResetInfoEscalation) {
259  LOG_INFO("Booting for the second time due to escalation reset");
260 
261  return true;
262  } else {
263  LOG_ERROR("Unexpected rst_info=0x%x", rst_info);
264  }
265  return false;
266 }