blob: 11704f5efc720a73eaf0041dfede5ce1df1b68cb [file] [log] [blame]
Jaedon Kima7bd7952022-05-02 21:25:31 +00001// Copyright lowRISC contributors.
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5#include "sw/device/lib/base/abs_mmio.h"
Hugo McNally8c57b1f2022-09-29 11:33:09 +01006#include "sw/device/lib/base/macros.h"
Jaedon Kima7bd7952022-05-02 21:25:31 +00007#include "sw/device/lib/base/mmio.h"
8#include "sw/device/lib/dif/dif_pwrmgr.h"
9#include "sw/device/lib/dif/dif_rstmgr.h"
Hugo McNally8c57b1f2022-09-29 11:33:09 +010010#include "sw/device/lib/dif/dif_rv_core_ibex.h"
Jaedon Kima7bd7952022-05-02 21:25:31 +000011#include "sw/device/lib/runtime/log.h"
12#include "sw/device/lib/testing/aon_timer_testutils.h"
Jaedon Kima7bd7952022-05-02 21:25:31 +000013#include "sw/device/lib/testing/rstmgr_testutils.h"
14#include "sw/device/lib/testing/test_framework/check.h"
15#include "sw/device/lib/testing/test_framework/ottf_isrs.h"
Jaedon Kima7bd7952022-05-02 21:25:31 +000016#include "sw/device/lib/testing/test_framework/ottf_main.h"
17
18#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
19
Alphan Ulusoy1801d3f2022-06-17 12:56:57 -040020OTTF_DEFINE_TEST_CONFIG();
21
Jaedon Kima7bd7952022-05-02 21:25:31 +000022/**
23 * RSTMGR CPU INFO TEST
Hugo McNally62c7b502022-10-17 20:35:48 +010024 *
25 * This has three stages:
26 *
27 * 1. After the first startup, a illegal memory access is performed.
28 * In the exception handler, a software reset is triggered.
29 *
30 * 2. After the software reset, the CPU info dump is checked against
31 * the expected values for this single fault. The watch dog is then set up
32 * and another illegal memory access is performed. Only this time
33 * the exception handler performs another illegal read.
34 * Causing the ibex to be haulted by the alert handler.
35 * The watch dog will eventually trigger a reset.
36 *
37 * 3. After the watchdog reset, the CPU info dump is checked against
38 * the expected values for this double fault.
Jaedon Kima7bd7952022-05-02 21:25:31 +000039 */
40
Hugo McNally8c57b1f2022-09-29 11:33:09 +010041// CPU Dump Size and Unmapped Addresses.
Jaedon Kima7bd7952022-05-02 21:25:31 +000042enum {
Hugo McNally8c57b1f2022-09-29 11:33:09 +010043 kCpuDumpSize = 8,
Hugo McNally62c7b502022-10-17 20:35:48 +010044 kIllegalAddr0 = 0xF0000000,
Hugo McNally8c57b1f2022-09-29 11:33:09 +010045 kIllegalAddr1 = 0xF0000004,
Timothy Chen9b3ebf42022-11-02 23:24:43 -070046 kIllegalAddr2 = 0x00000008,
Jaedon Kima7bd7952022-05-02 21:25:31 +000047};
48
Hugo McNally62c7b502022-10-17 20:35:48 +010049// Declaring the labels used to calculate the expected current and next pc
50// after a double fault.
Timothy Chen9b3ebf42022-11-02 23:24:43 -070051extern const uint32_t _ottf_interrupt_vector;
Hugo McNally8c57b1f2022-09-29 11:33:09 +010052
53// The labels to points in the code of which the memory address is needed.
Hugo McNally62c7b502022-10-17 20:35:48 +010054extern const char kSingleFaultAddrLower[];
55extern const char kSingleFaultAddrUpper[];
56extern const char kSingleFaultAddrCurrentPc[];
57extern const char kSingleFaultAddrNextPc[];
Hugo McNally8c57b1f2022-09-29 11:33:09 +010058extern const char kDoubleFaultFirstAddrLower[];
59extern const char kDoubleFaultFirstAddrUpper[];
60extern const char kDoubleFaultSecondAddrLower[];
61extern const char kDoubleFaultSecondAddrUpper[];
Jaedon Kima7bd7952022-05-02 21:25:31 +000062
Hugo McNally62c7b502022-10-17 20:35:48 +010063// A handle to the reset manager.
64static dif_rstmgr_t rstmgr;
65
66// This variable is used to ensure loads from an address aren't optimised out.
Hugo McNally8c57b1f2022-09-29 11:33:09 +010067volatile static uint32_t addr_val;
Jaedon Kima7bd7952022-05-02 21:25:31 +000068
69/**
Hugo McNally62c7b502022-10-17 20:35:48 +010070 * When true, the exception handler will trigger another fault,
71 * causing a double fault,
72 * otherwise it triggers a software reset.
73 */
74volatile static bool double_fault;
75
76/**
Jaedon Kima7bd7952022-05-02 21:25:31 +000077 * Overrides the default OTTF exception handler.
78 */
79void ottf_exception_handler(void) {
Hugo McNally62c7b502022-10-17 20:35:48 +010080 if (double_fault) {
81 OT_ADDRESSABLE_LABEL(kDoubleFaultSecondAddrLower);
Timothy Chen9b3ebf42022-11-02 23:24:43 -070082 mmio_region_write32(mmio_region_from_addr(kIllegalAddr2), 0, 0);
Hugo McNally62c7b502022-10-17 20:35:48 +010083 OT_ADDRESSABLE_LABEL(kDoubleFaultSecondAddrUpper);
84 } else {
85 CHECK_DIF_OK(dif_rstmgr_software_device_reset(&rstmgr));
86 // Write to `addr_val` so that the 'last data access' address is
87 // a known value (the address of addr_val).
88 addr_val = 1;
89 OT_ADDRESSABLE_LABEL(kSingleFaultAddrCurrentPc);
90 wait_for_interrupt(); // Wait for the reset.
91 OT_ADDRESSABLE_LABEL(kSingleFaultAddrNextPc);
92 addr_val = 2;
93 }
94 CHECK(false,
95 "This point should be unreachable; "
96 "a reset or another fault should have occured.");
Hugo McNally8c57b1f2022-09-29 11:33:09 +010097}
Timothy Chen69f0bbe2022-06-15 17:44:40 -070098
Hugo McNally8c57b1f2022-09-29 11:33:09 +010099/**
100 * Gets, parses and returns the cpu info crash dump.
101 *
Hugo McNally8c57b1f2022-09-29 11:33:09 +0100102 * @param ibex A handle to the ibex.
103 * @return The cpu info crash dump.
104 */
105static dif_rv_core_ibex_crash_dump_info_t get_dump(
Hugo McNally62c7b502022-10-17 20:35:48 +0100106 const dif_rv_core_ibex_t *ibex) {
Hugo McNally8c57b1f2022-09-29 11:33:09 +0100107 size_t size_read;
108 dif_rstmgr_cpu_info_dump_segment_t dump[DIF_RSTMGR_CPU_INFO_MAX_SIZE];
Jaedon Kima7bd7952022-05-02 21:25:31 +0000109
Hugo McNally8c57b1f2022-09-29 11:33:09 +0100110 CHECK_DIF_OK(dif_rstmgr_cpu_info_dump_read(
Hugo McNally62c7b502022-10-17 20:35:48 +0100111 &rstmgr, dump, DIF_RSTMGR_CPU_INFO_MAX_SIZE, &size_read));
Hugo McNally8c57b1f2022-09-29 11:33:09 +0100112 CHECK(size_read == kCpuDumpSize,
113 "The observed cpu info dump's size was %d, "
114 "but it was expected to be %d",
115 size_read, kCpuDumpSize);
Timothy Chen69f0bbe2022-06-15 17:44:40 -0700116
Hugo McNally8c57b1f2022-09-29 11:33:09 +0100117 dif_rv_core_ibex_crash_dump_info_t output;
118 CHECK_DIF_OK(
119 dif_rv_core_ibex_parse_crash_dump(ibex, dump, size_read, &output));
120 return output;
121}
Jaedon Kima7bd7952022-05-02 21:25:31 +0000122
Hugo McNally8c57b1f2022-09-29 11:33:09 +0100123/**
124 * Holds the expected cpu info dump values for the current state.
125 */
126typedef struct rstmgr_cpu_info_test_exp_state {
127 uint32_t mtval; ///< The last exception address.
128 uint32_t mpec_l; ///< The last exception PC lower bound.
129 uint32_t mpec_u; ///< The last exception PC upper bound.
130 uint32_t mdaa; ///< The last data access address.
131 uint32_t mnpc; ///< The next PC.
132 uint32_t mcpc; ///< The current PC.
133} rstmgr_cpu_info_test_exp_state_t;
134
135/**
136 * Holds the expected cpu info dump values for the previous state.
137 */
138typedef struct rstmgr_cpu_info_test_exp_prev_state {
139 uint32_t mtval; ///< The exception address for the previous crash.
140 uint32_t
141 mpec_l; ///< The last exception PC lower bound for the previous crash.
142 uint32_t
143 mpec_u; ///< The last exception PC upper bound for the previous crash.
144} rstmgr_cpu_info_test_exp_prev_state_t;
145
146/**
147 * Checks the 'current' section of the cpu info dump against the given expected
148 * values.
149 *
150 * @param obs_state The cpu info crash dump's current state values.
151 * @param exp_state The expected values of the current state.
152 */
153static void check_state(dif_rv_core_ibex_crash_dump_state_t obs_state,
154 rstmgr_cpu_info_test_exp_state_t exp_state) {
155 CHECK(exp_state.mtval == obs_state.mtval,
156 "Last Exception Access Addr: Expected 0x%x != Observed 0x%x",
157 exp_state.mtval, obs_state.mtval);
158 CHECK(exp_state.mcpc == obs_state.mcpc,
159 "Current PC: Expected 0x%x != Observed 0x%x", exp_state.mcpc,
160 obs_state.mcpc);
161 CHECK(exp_state.mnpc == obs_state.mnpc,
162 "Next PC: Expected 0x%x != Observed 0x%x", exp_state.mnpc,
163 obs_state.mnpc);
164 CHECK(exp_state.mdaa == obs_state.mdaa,
165 "Last Data Access Addr: Expected 0x%x != Observed 0x%x", exp_state.mdaa,
166 obs_state.mdaa);
167 CHECK(
168 exp_state.mpec_l <= obs_state.mpec && obs_state.mpec < exp_state.mpec_u,
169 "The Observed MPEC, 0x%x, was not in the expected range of [0x%x, 0x%x)",
170 obs_state.mpec, exp_state.mpec_l, exp_state.mpec_u);
171}
172
173/**
174 * Checks the 'previous' section of the cpu info dump against the given expected
175 * values.
176 *
177 * @param obs_prev_state The cpu info crash dump's previous state values.
178 * @param exp_prev_state The expected values of the previous state.
179 */
180static void check_prev_state(
181 dif_rv_core_ibex_previous_crash_dump_state_t obs_prev_state,
182 rstmgr_cpu_info_test_exp_prev_state_t exp_prev_state) {
183 CHECK(exp_prev_state.mtval == obs_prev_state.mtval,
184 "Last Exception Access Addr: Expected 0x%x != Observed 0x%x",
185 exp_prev_state.mtval, obs_prev_state.mtval);
186 CHECK(exp_prev_state.mpec_l <= obs_prev_state.mpec &&
187 obs_prev_state.mpec < exp_prev_state.mpec_u,
188 "The Observed Previous MPEC, 0x%x, "
189 "was not in the expected range of [0x%x, 0x%x)",
190 obs_prev_state.mpec, exp_prev_state.mpec_l, exp_prev_state.mpec_u);
Jaedon Kima7bd7952022-05-02 21:25:31 +0000191}
192
193bool test_main(void) {
Hugo McNally62c7b502022-10-17 20:35:48 +0100194 dif_rv_core_ibex_crash_dump_info_t dump;
195
Jaedon Kima7bd7952022-05-02 21:25:31 +0000196 dif_aon_timer_t aon_timer;
197 dif_pwrmgr_t pwrmgr;
Hugo McNally8c57b1f2022-09-29 11:33:09 +0100198 dif_rv_core_ibex_t ibex;
Jaedon Kima7bd7952022-05-02 21:25:31 +0000199
Hugo McNally8c57b1f2022-09-29 11:33:09 +0100200 // Initialize Handles.
Jaedon Kima7bd7952022-05-02 21:25:31 +0000201 CHECK_DIF_OK(dif_rstmgr_init(
202 mmio_region_from_addr(TOP_EARLGREY_RSTMGR_AON_BASE_ADDR), &rstmgr));
203 CHECK_DIF_OK(dif_aon_timer_init(
204 mmio_region_from_addr(TOP_EARLGREY_AON_TIMER_AON_BASE_ADDR), &aon_timer));
205 CHECK_DIF_OK(dif_pwrmgr_init(
206 mmio_region_from_addr(TOP_EARLGREY_PWRMGR_AON_BASE_ADDR), &pwrmgr));
Hugo McNally8c57b1f2022-09-29 11:33:09 +0100207 CHECK_DIF_OK(dif_rv_core_ibex_init(
208 mmio_region_from_addr(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR), &ibex));
Jaedon Kima7bd7952022-05-02 21:25:31 +0000209
Hugo McNally62c7b502022-10-17 20:35:48 +0100210 switch (rstmgr_testutils_reason_get()) {
211 case kDifRstmgrResetInfoPor: // The first power-up.
212 LOG_INFO("Triggering single fault.");
Jaedon Kima7bd7952022-05-02 21:25:31 +0000213
Hugo McNally62c7b502022-10-17 20:35:48 +0100214 // Enable cpu info.
215 CHECK_DIF_OK(dif_rstmgr_cpu_info_set_enabled(&rstmgr, kDifToggleEnabled));
Jaedon Kima7bd7952022-05-02 21:25:31 +0000216
Hugo McNally62c7b502022-10-17 20:35:48 +0100217 double_fault = false;
218 OT_ADDRESSABLE_LABEL(kSingleFaultAddrLower);
219 addr_val = mmio_region_read32(mmio_region_from_addr(kIllegalAddr0), 0);
220 OT_ADDRESSABLE_LABEL(kSingleFaultAddrUpper);
221 CHECK(false,
222 "This should be unreachable; a single fault should have occured.");
223 break;
Jaedon Kima7bd7952022-05-02 21:25:31 +0000224
Hugo McNally62c7b502022-10-17 20:35:48 +0100225 case kDifRstmgrResetInfoSw: // The power-up after the single fault.
226 LOG_INFO("Checking CPU info dump after single fault.");
Hugo McNally8c57b1f2022-09-29 11:33:09 +0100227
Hugo McNally62c7b502022-10-17 20:35:48 +0100228 dump = get_dump(&ibex);
Jaedon Kima7bd7952022-05-02 21:25:31 +0000229
Hugo McNally62c7b502022-10-17 20:35:48 +0100230 CHECK(
231 dump.double_fault == kDifToggleDisabled,
232 "CPU Info dump shows a double fault after experiencing only a single "
233 "fault.");
Jaedon Kima7bd7952022-05-02 21:25:31 +0000234
Hugo McNally62c7b502022-10-17 20:35:48 +0100235 check_state(dump.fault_state,
236 (rstmgr_cpu_info_test_exp_state_t){
237 .mtval = (uint32_t)kIllegalAddr0,
238 .mpec_l = (uint32_t)kSingleFaultAddrLower,
239 .mpec_u = (uint32_t)kSingleFaultAddrUpper,
240 .mdaa = (uint32_t)&addr_val,
241 .mcpc = (uint32_t)kSingleFaultAddrCurrentPc,
242 .mnpc = (uint32_t)kSingleFaultAddrNextPc,
243 });
Jaedon Kima7bd7952022-05-02 21:25:31 +0000244
Hugo McNally62c7b502022-10-17 20:35:48 +0100245 LOG_INFO("Setting up watch dog and triggering a double fault.");
246 uint32_t bark_cycles = aon_timer_testutils_get_aon_cycles_from_us(100);
247 uint32_t bite_cycles = aon_timer_testutils_get_aon_cycles_from_us(100);
Jaedon Kima7bd7952022-05-02 21:25:31 +0000248
Hugo McNally62c7b502022-10-17 20:35:48 +0100249 // Set wdog as a reset source.
250 CHECK_DIF_OK(dif_pwrmgr_set_request_sources(
251 &pwrmgr, kDifPwrmgrReqTypeReset, kDifPwrmgrResetRequestSourceTwo,
252 kDifToggleEnabled));
253 // Setup the watchdog bark and bite timeouts.
254 aon_timer_testutils_watchdog_config(&aon_timer, bark_cycles, bite_cycles,
255 false);
256 // Enable cpu info.
257 CHECK_DIF_OK(dif_rstmgr_cpu_info_set_enabled(&rstmgr, kDifToggleEnabled));
Jaedon Kima7bd7952022-05-02 21:25:31 +0000258
Hugo McNally62c7b502022-10-17 20:35:48 +0100259 double_fault = true;
260 OT_ADDRESSABLE_LABEL(kDoubleFaultFirstAddrLower);
261 addr_val = mmio_region_read32(mmio_region_from_addr(kIllegalAddr1), 0);
262 OT_ADDRESSABLE_LABEL(kDoubleFaultFirstAddrUpper);
263 CHECK(false,
264 "This should be unreachable; a double fault should have occured.");
265 break;
Hugo McNally8c57b1f2022-09-29 11:33:09 +0100266
Hugo McNally62c7b502022-10-17 20:35:48 +0100267 case kDifRstmgrResetInfoWatchdog: // The power-up after the double fault.
268 LOG_INFO("Checking CPU info dump after double fault.");
269
270 dump = get_dump(&ibex);
271
272 CHECK(dump.double_fault == kDifToggleEnabled,
273 "CPU Info dump doesn't show a double fault has happened.");
274
Timothy Chen9b3ebf42022-11-02 23:24:43 -0700275 // After #15219 was merged, the execution stops more predictably
276 // once fetch_en is dropped due to a double fault.
277 // The current pc should now always be the instruction after the
278 // instruction that issues the illegal load.
279 // The next pc is always the exception handler, because that's
280 // where execution would have gone if it had not halted
Hugo McNally62c7b502022-10-17 20:35:48 +0100281 check_state(dump.fault_state,
282 (rstmgr_cpu_info_test_exp_state_t){
283 .mtval = (uint32_t)kIllegalAddr2,
284 .mpec_l = (uint32_t)kDoubleFaultSecondAddrLower,
285 .mpec_u = (uint32_t)kDoubleFaultSecondAddrUpper,
286 .mdaa = (uint32_t)kIllegalAddr2,
Timothy Chen9b3ebf42022-11-02 23:24:43 -0700287 .mcpc = (uint32_t)kDoubleFaultSecondAddrLower + 4,
288 .mnpc = (uint32_t)&_ottf_interrupt_vector,
Hugo McNally62c7b502022-10-17 20:35:48 +0100289 });
290
291 check_prev_state(dump.previous_fault_state,
292 (rstmgr_cpu_info_test_exp_prev_state_t){
293 .mtval = (uint32_t)kIllegalAddr1,
294 .mpec_l = (uint32_t)kDoubleFaultFirstAddrLower,
295 .mpec_u = (uint32_t)kDoubleFaultFirstAddrUpper,
296 });
297
Hugo McNally62c7b502022-10-17 20:35:48 +0100298 return true;
299
300 default:
301 CHECK(false, "Device was reset by an unexpected source.");
302 break;
Jaedon Kima7bd7952022-05-02 21:25:31 +0000303 }
Hugo McNally8c57b1f2022-09-29 11:33:09 +0100304 return false;
Jaedon Kima7bd7952022-05-02 21:25:31 +0000305}