Software APIs
alert_handler_ping_timeout_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/runtime/irq.h"
16 #include "sw/device/lib/testing/alert_handler_testutils.h"
17 #include "sw/device/lib/testing/rv_plic_testutils.h"
18 #include "sw/device/lib/testing/test_framework/FreeRTOSConfig.h"
19 #include "sw/device/lib/testing/test_framework/check.h"
21 
22 #include "alert_handler_regs.h" // Generated.
24 #include "sw/device/lib/testing/autogen/isr_testutils.h"
25 
26 OTTF_DEFINE_TEST_CONFIG();
27 
28 static dif_rv_plic_t plic;
29 static dif_alert_handler_t alert_handler;
30 static const uint32_t kPlicTarget = kTopEarlgreyPlicTargetIbex0;
31 
32 static plic_isr_ctx_t plic_ctx = {
33  .rv_plic = &plic,
34  .hart_id = kPlicTarget,
35 };
36 
37 // Depends on the clock domain, sometimes alert handler will trigger a spurious
38 // alert after the alert timeout. (Issue #2321)
39 // So we allow class A interrupt to fire after the real timeout interrupt is
40 // triggered.
41 static alert_handler_isr_ctx_t alert_handler_ctx = {
42  .alert_handler = &alert_handler,
43  .plic_alert_handler_start_irq_id = kTopEarlgreyPlicIrqIdAlertHandlerClassa,
44  .expected_irq = kDifAlertHandlerIrqClassb,
45  .is_only_irq = false,
46 };
47 
48 /**
49  * Initialize the peripherals used in this test.
50  */
51 static void init_peripherals(void) {
52  mmio_region_t base_addr =
54  CHECK_DIF_OK(dif_rv_plic_init(base_addr, &plic));
55 
57  CHECK_DIF_OK(dif_alert_handler_init(base_addr, &alert_handler));
58 
59  // Enable all the alert_handler interrupts used in this test.
60  rv_plic_testutils_irq_range_enable(&plic, kPlicTarget,
63 }
64 
65 /**
66  * Program the alert handler to escalate on alerts upto phase 1 (i.e. wipe
67  * secret) but not trigger reset. Then CPU can check if the correct interrupt
68  * fires and check the local alert cause register.
69  */
70 static void alert_handler_config(void) {
71  // Enable all incoming alerts and configure them to class A.
72  dif_alert_handler_alert_t alerts[ALERT_HANDLER_PARAM_N_ALERTS];
73  dif_alert_handler_class_t alert_classes[ALERT_HANDLER_PARAM_N_ALERTS];
74  for (dif_alert_handler_alert_t i = 0; i < ALERT_HANDLER_PARAM_N_ALERTS; ++i) {
75  alerts[i] = i;
76  alert_classes[i] = kDifAlertHandlerClassA;
77  }
78 
79  // Enable alert ping fail local alert and configure that to classb.
80  dif_alert_handler_local_alert_t loc_alerts[] = {
81  kDifAlertHandlerLocalAlertAlertPingFail};
82  dif_alert_handler_class_t loc_alert_classes[] = {kDifAlertHandlerClassB};
83 
86  .signal = UINT32_MAX, // Take no action.
87  .duration_cycles = 2000}};
88 
89  dif_alert_handler_class_config_t class_config = {
91  .accumulator_threshold =
92  1000, // Set to large number to defer escalation.
93  .irq_deadline_cycles = 0, // Disable IRQ timeouts.
94  .escalation_phases = esc_phases,
95  .escalation_phases_len = ARRAYSIZE(esc_phases),
96  .crashdump_escalation_phase = kDifAlertHandlerClassStatePhase1,
97  };
98 
99  dif_alert_handler_class_config_t class_configs[] = {class_config,
100  class_config};
101 
102  dif_alert_handler_class_t classes[] = {kDifAlertHandlerClassA,
103  kDifAlertHandlerClassB};
104 
105  dif_alert_handler_config_t config = {
106  .alerts = alerts,
107  .alert_classes = alert_classes,
108  .alerts_len = ARRAYSIZE(alerts),
109  .local_alerts = loc_alerts,
110  .local_alert_classes = loc_alert_classes,
111  .local_alerts_len = ARRAYSIZE(loc_alerts),
112  .classes = classes,
113  .class_configs = class_configs,
114  .classes_len = ARRAYSIZE(class_configs),
115  .ping_timeout = 1,
116  };
117 
118  CHECK_STATUS_OK(alert_handler_testutils_configure_all(&alert_handler, config,
120  // Enables alert handler irq.
121  CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
122  &alert_handler, kDifAlertHandlerIrqClassb, kDifToggleEnabled));
123 }
124 
125 /**
126  * External ISR.
127  *
128  * Handles all peripheral interrupts on Ibex. PLIC asserts an external interrupt
129  * line to the CPU, which results in a call to this OTTF ISR. This ISR
130  * overrides the default OTTF implementation.
131  */
132 void ottf_external_isr(uint32_t *exc_info) {
133  top_earlgrey_plic_peripheral_t peripheral_serviced;
134  dif_alert_handler_irq_t irq_serviced;
135  isr_testutils_alert_handler_isr(plic_ctx, alert_handler_ctx,
136  &peripheral_serviced, &irq_serviced);
137  CHECK(peripheral_serviced == kTopEarlgreyPlicPeripheralAlertHandler,
138  "Interrupt from unexpected peripheral: %d", peripheral_serviced);
139 
140  // Only interrupts from class B alerts are expected for this test. Report the
141  // unexpected class.
142  CHECK(irq_serviced == kDifAlertHandlerIrqClassb,
143  "Interrupt from unexpected class: Class %c", 'A' + irq_serviced);
144  // Disable the interrupt after seeing a single ping timeout.
145  CHECK_DIF_OK(dif_alert_handler_irq_set_enabled(
146  &alert_handler, kDifAlertHandlerIrqClassb, kDifToggleDisabled));
147 }
148 
149 bool test_main(void) {
150  init_peripherals();
151 
152  // Stop Ibex from servicing interrupts just before WFI, which would lead to a
153  // long sleep if the test changes to only handle a single ping timeout.
154  irq_global_ctrl(false);
155  irq_external_ctrl(true);
156 
157  alert_handler_config();
158 
160 
161  // Enable the external IRQ at Ibex to jump to servicing it.
162  irq_global_ctrl(true);
163 
164  // Check local alert cause.
165  bool is_cause;
166  dif_alert_handler_local_alert_t exp_local_alert =
167  kDifAlertHandlerLocalAlertAlertPingFail;
169  &alert_handler, exp_local_alert, &is_cause));
170  CHECK(is_cause, "Expect local alert cause: alert_ping_fail!");
171 
172  // Print out information about which alerts have been received.
173  for (size_t id = 0; id < ALERT_HANDLER_PARAM_N_ALERTS; ++id) {
174  bool is_cause;
175  CHECK_DIF_OK(
176  dif_alert_handler_alert_is_cause(&alert_handler, id, &is_cause));
177  if (is_cause) {
178  LOG_INFO("Received alert ID %u.", id);
179  }
180  }
181  return true;
182 }