1 /*
2 * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stdio.h>
8
9 #include "spi_flash_mmap.h"
10
11 #include "soc/extmem_reg.h"
12 #include "esp_private/panic_internal.h"
13 #include "esp_private/panic_reason.h"
14 #include "riscv/rvruntime-frames.h"
15 #include "esp_private/cache_err_int.h"
16 #include "soc/timer_periph.h"
17
18 #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
19 #include "esp_private/esp_memprot_internal.h"
20 #include "esp_memprot.h"
21 #endif
22
23 #if CONFIG_ESP_SYSTEM_USE_EH_FRAME
24 #include "esp_private/eh_frame_parser.h"
25 #include "esp_private/cache_utils.h"
26 #endif
27
28
29 #define DIM(array) (sizeof(array)/sizeof(*array))
30
31 /**
32 * Structure used to define a flag/bit to test in case of cache error.
33 * The message describes the cause of the error when the bit is set in
34 * a given status register.
35 */
36 typedef struct {
37 const uint32_t bit;
38 const char *msg;
39 } register_bit_t;
40
41 /**
42 * Function to check each bits defined in the array reg_bits in the given
43 * status register. The first bit from the array to be set in the status
44 * register will have its associated message printed. This function returns
45 * true. If not bit was set in the register, it returns false.
46 * The order of the bits in the array is important as only the first bit to
47 * be set in the register will have its associated message printed.
48 */
test_and_print_register_bits(const uint32_t status,const register_bit_t * reg_bits,const uint32_t size)49 static inline bool test_and_print_register_bits(const uint32_t status,
50 const register_bit_t *reg_bits,
51 const uint32_t size)
52 {
53 /* Browse the flag/bit array and test each one with the given status
54 * register. */
55 for (int i = 0; i < size; i++) {
56 const uint32_t bit = reg_bits[i].bit;
57 if ((status & bit) == bit) {
58 /* Reason of the panic found, print the reason. */
59 panic_print_str(reg_bits[i].msg);
60 panic_print_str("\r\n");
61
62 return true;
63 }
64 }
65
66 /* Panic cause not found, no message was printed. */
67 return false;
68 }
69
70 /**
71 * Function called when a cache error occurs. It prints details such as the
72 * explanation of why the panic occured.
73 */
print_cache_err_details(const void * frame)74 static inline void print_cache_err_details(const void *frame)
75 {
76 #if !CONFIG_IDF_TARGET_ESP32C6 && !CONFIG_IDF_TARGET_ESP32H2 // ESP32C6-TODO, ESP32H2-TODO: IDF-5657
77 /* Define the array that contains the status (bits) to test on the register
78 * EXTMEM_CORE0_ACS_CACHE_INT_ST_REG. each bit is accompanied by a small
79 * message.
80 * The messages have been pulled from the header file where the status bit
81 * are defined. */
82 const register_bit_t core0_acs_bits[] = {
83 {
84 .bit = EXTMEM_CORE0_DBUS_WR_ICACHE_ST,
85 .msg = "dbus tried to write cache"
86 },
87 {
88 .bit = EXTMEM_CORE0_DBUS_REJECT_ST,
89 .msg = "dbus authentication failed"
90 },
91 {
92 .bit = EXTMEM_CORE0_DBUS_ACS_MSK_ICACHE_ST,
93 .msg = "access to cache while dbus or cache is disabled"
94 },
95 {
96 .bit = EXTMEM_CORE0_IBUS_REJECT_ST,
97 .msg = "ibus authentication failed"
98 },
99 {
100 .bit = EXTMEM_CORE0_IBUS_WR_ICACHE_ST,
101 .msg = "ibus tried to write cache"
102 },
103 {
104 .bit = EXTMEM_CORE0_IBUS_ACS_MSK_ICACHE_ST,
105 .msg = "access to cache while ibus or cache is disabled"
106 },
107 };
108
109 /* Same goes for the register EXTMEM_CACHE_ILG_INT_ST_REG and its bits. */
110 const register_bit_t cache_ilg_bits[] = {
111 {
112 .bit = EXTMEM_MMU_ENTRY_FAULT_ST,
113 .msg = "MMU entry fault"
114 },
115 {
116 .bit = EXTMEM_ICACHE_PRELOAD_OP_FAULT_ST,
117 .msg = "preload configurations fault"
118 },
119 {
120 .bit = EXTMEM_ICACHE_SYNC_OP_FAULT_ST,
121 .msg = "sync configurations fault"
122 },
123 };
124
125 /* Read the status register EXTMEM_CORE0_ACS_CACHE_INT_ST_REG. This status
126 * register is not equal to 0 when a cache access error occured. */
127 const uint32_t core0_status = REG_READ(EXTMEM_CORE0_ACS_CACHE_INT_ST_REG);
128
129 /* If the panic is due to a cache access error, one of the bit of the
130 * register is set. Thus, this function will return true. */
131 bool handled = test_and_print_register_bits(core0_status, core0_acs_bits, DIM(core0_acs_bits));
132
133 /* If the panic was due to a cache illegal error, the previous call returned false and this
134 * EXTMEM_CACHE_ILG_INT_ST_REG register should not me equal to 0.
135 * Check each bit of it and print the message associated if found. */
136 if (!handled) {
137 const uint32_t cache_ilg_status = REG_READ(EXTMEM_CACHE_ILG_INT_ST_REG);
138 handled = test_and_print_register_bits(cache_ilg_status, cache_ilg_bits, DIM(cache_ilg_bits));
139
140 /* If the error was not found, print the both registers value */
141 if (!handled) {
142 panic_print_str("EXTMEM_CORE0_ACS_CACHE_INT_ST_REG = 0x");
143 panic_print_hex(core0_status);
144 panic_print_str("\r\nEXTMEM_CACHE_ILG_INT_ST_REG = 0x");
145 panic_print_hex(cache_ilg_status);
146 panic_print_str("\r\n");
147 }
148 }
149 #endif
150 }
151
152
153 /**
154 * Function called when a memory protection error occurs (PMS). It prints details such as the
155 * explanation of why the panic occured.
156 */
157 #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
158
159 static esp_memp_intr_source_t s_memp_intr = {MEMPROT_TYPE_INVALID, -1};
160
161 #define PRINT_MEMPROT_ERROR(err) \
162 do { \
163 panic_print_str("N/A (error "); \
164 panic_print_str(esp_err_to_name(err)); \
165 panic_print_str(")"); \
166 } while(0)
167
print_memprot_err_details(const void * frame)168 static inline void print_memprot_err_details(const void *frame __attribute__((unused)))
169 {
170 if (s_memp_intr.mem_type == MEMPROT_TYPE_INVALID && s_memp_intr.core == -1) {
171 panic_print_str(" - no details available -\r\n");
172 return;
173 }
174
175 //common memprot fault info
176 panic_print_str(" memory type: ");
177 panic_print_str(esp_mprot_mem_type_to_str(s_memp_intr.mem_type));
178
179 panic_print_str("\r\n faulting address: ");
180 void *faulting_addr;
181 esp_err_t res = esp_mprot_get_violate_addr(s_memp_intr.mem_type, &faulting_addr, s_memp_intr.core);
182 if (res == ESP_OK) {
183 panic_print_str("0x");
184 panic_print_hex((int)faulting_addr);
185 } else {
186 PRINT_MEMPROT_ERROR(res);
187 }
188
189 panic_print_str( "\r\n world: ");
190 esp_mprot_pms_world_t world;
191 res = esp_mprot_get_violate_world(s_memp_intr.mem_type, &world, s_memp_intr.core);
192 if (res == ESP_OK) {
193 panic_print_str(esp_mprot_pms_world_to_str(world));
194 } else {
195 PRINT_MEMPROT_ERROR(res);
196 }
197
198 panic_print_str( "\r\n operation type: ");
199 uint32_t operation;
200 res = esp_mprot_get_violate_operation(s_memp_intr.mem_type, &operation, s_memp_intr.core);
201 if (res == ESP_OK) {
202 panic_print_str(esp_mprot_oper_type_to_str(operation));
203 } else {
204 PRINT_MEMPROT_ERROR(res);
205 }
206
207 if (esp_mprot_has_byte_enables(s_memp_intr.mem_type)) {
208 panic_print_str("\r\n byte-enables: " );
209 uint32_t byte_enables;
210 res = esp_mprot_get_violate_byte_enables(s_memp_intr.mem_type, &byte_enables, s_memp_intr.core);
211 if (res == ESP_OK) {
212 panic_print_hex(byte_enables);
213 } else {
214 PRINT_MEMPROT_ERROR(res);
215 }
216 }
217
218 panic_print_str("\r\n");
219 }
220 #endif
221
panic_print_registers(const void * f,int core)222 void panic_print_registers(const void *f, int core)
223 {
224 uint32_t *regs = (uint32_t *)f;
225
226 // only print ABI name
227 const char *desc[] = {
228 "MEPC ", "RA ", "SP ", "GP ", "TP ", "T0 ", "T1 ", "T2 ",
229 "S0/FP ", "S1 ", "A0 ", "A1 ", "A2 ", "A3 ", "A4 ", "A5 ",
230 "A6 ", "A7 ", "S2 ", "S3 ", "S4 ", "S5 ", "S6 ", "S7 ",
231 "S8 ", "S9 ", "S10 ", "S11 ", "T3 ", "T4 ", "T5 ", "T6 ",
232 "MSTATUS ", "MTVEC ", "MCAUSE ", "MTVAL ", "MHARTID "
233 };
234
235 panic_print_str("Core ");
236 panic_print_dec(((RvExcFrame *)f)->mhartid);
237 panic_print_str(" register dump:");
238
239 for (int x = 0; x < sizeof(desc) / sizeof(desc[0]); x += 4) {
240 panic_print_str("\r\n");
241 for (int y = 0; y < 4 && x + y < sizeof(desc) / sizeof(desc[0]); y++) {
242 if (desc[x + y][0] != 0) {
243 panic_print_str(desc[x + y]);
244 panic_print_str(": 0x");
245 panic_print_hex(regs[x + y]);
246 panic_print_str(" ");
247 }
248 }
249 }
250 }
251
252 /**
253 * This function will be called when a SoC-level panic occurs.
254 * SoC-level panics include cache errors and watchdog interrupts.
255 */
panic_soc_fill_info(void * f,panic_info_t * info)256 void panic_soc_fill_info(void *f, panic_info_t *info)
257 {
258 RvExcFrame *frame = (RvExcFrame *) f;
259
260 /* Please keep in sync with PANIC_RSN_* defines */
261 static const char *pseudo_reason[PANIC_RSN_COUNT] = {
262 "Unknown reason",
263 "Interrupt wdt timeout on CPU0",
264 #if SOC_CPU_NUM > 1
265 "Interrupt wdt timeout on CPU1",
266 #endif
267 "Cache error",
268 #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
269 "Memory protection fault",
270 #endif
271 };
272
273 info->reason = pseudo_reason[0];
274 info->addr = (void *) frame->mepc;
275
276 /* The mcause has been set by the CPU when the panic occured.
277 * All SoC-level panic will call this function, thus, this register
278 * lets us know which error was triggered. */
279 if (frame->mcause == ETS_CACHEERR_INUM) {
280 /* Panic due to a cache error, multiple cache error are possible,
281 * assign function print_cache_err_details to our structure's
282 * details field. As its name states, it will give more details
283 * about why the error happened. */
284
285 info->core = esp_cache_err_get_cpuid();
286 info->reason = pseudo_reason[PANIC_RSN_CACHEERR];
287 info->details = print_cache_err_details;
288
289 } else if (frame->mcause == ETS_INT_WDT_INUM) {
290 /* Watchdog interrupt occured, get the core on which it happened
291 * and update the reason/message accordingly. */
292
293 const int core = esp_cache_err_get_cpuid();
294 info->core = core;
295 info->exception = PANIC_EXCEPTION_IWDT;
296
297 #if SOC_CPU_NUM > 1
298 _Static_assert(PANIC_RSN_INTWDT_CPU0 + 1 == PANIC_RSN_INTWDT_CPU1,
299 "PANIC_RSN_INTWDT_CPU1 must be equal to PANIC_RSN_INTWDT_CPU0 + 1");
300 #endif
301 info->reason = pseudo_reason[PANIC_RSN_INTWDT_CPU0 + core];
302 }
303 #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
304 else if (frame->mcause == ETS_MEMPROT_ERR_INUM) {
305 info->reason = pseudo_reason[PANIC_RSN_MEMPROT];
306 info->details = print_memprot_err_details;
307 info->core = esp_mprot_get_active_intr(&s_memp_intr) == ESP_OK ? s_memp_intr.core : -1;
308 }
309 #endif
310 }
311
panic_arch_fill_info(void * frame,panic_info_t * info)312 void panic_arch_fill_info(void *frame, panic_info_t *info)
313 {
314 RvExcFrame *regs = (RvExcFrame *) frame;
315 info->core = 0;
316 info->exception = PANIC_EXCEPTION_FAULT;
317
318 //Please keep in sync with PANIC_RSN_* defines
319 static const char *reason[] = {
320 "Instruction address misaligned",
321 "Instruction access fault",
322 "Illegal instruction",
323 "Breakpoint",
324 "Load address misaligned",
325 "Load access fault",
326 "Store address misaligned",
327 "Store access fault",
328 "Environment call from U-mode",
329 "Environment call from S-mode",
330 NULL,
331 "Environment call from M-mode",
332 "Instruction page fault",
333 "Load page fault",
334 NULL,
335 "Store page fault",
336 };
337
338 if (regs->mcause < (sizeof(reason) / sizeof(reason[0]))) {
339 if (reason[regs->mcause] != NULL) {
340 info->reason = (reason[regs->mcause]);
341 }
342 }
343
344 info->description = "Exception was unhandled.";
345
346 info->addr = (void *) regs->mepc;
347 }
348
panic_print_basic_backtrace(const void * frame,int core)349 static void panic_print_basic_backtrace(const void *frame, int core)
350 {
351 // Basic backtrace
352 panic_print_str("\r\nStack memory:\r\n");
353 uint32_t sp = (uint32_t)((RvExcFrame *)frame)->sp;
354 const int per_line = 8;
355 for (int x = 0; x < 1024; x += per_line * sizeof(uint32_t)) {
356 uint32_t *spp = (uint32_t *)(sp + x);
357 panic_print_hex(sp + x);
358 panic_print_str(": ");
359 for (int y = 0; y < per_line; y++) {
360 panic_print_str("0x");
361 panic_print_hex(spp[y]);
362 panic_print_str(y == per_line - 1 ? "\r\n" : " ");
363 }
364 }
365 }
366
panic_print_backtrace(const void * frame,int core)367 void panic_print_backtrace(const void *frame, int core)
368 {
369 #if CONFIG_ESP_SYSTEM_USE_EH_FRAME
370 if (!spi_flash_cache_enabled()) {
371 panic_print_str("\r\nWarning: SPI Flash cache is disabled, cannot process eh_frame parsing. "
372 "Falling back to basic backtrace.\r\n");
373 panic_print_basic_backtrace(frame, core);
374 } else {
375 esp_eh_frame_print_backtrace(frame);
376 }
377 #else
378 panic_print_basic_backtrace(frame, core);
379 #endif
380 }
381
panic_get_address(const void * f)382 uint32_t panic_get_address(const void *f)
383 {
384 return ((RvExcFrame *)f)->mepc;
385 }
386
panic_get_cause(const void * f)387 uint32_t panic_get_cause(const void *f)
388 {
389 return ((RvExcFrame *)f)->mcause;
390 }
391
panic_set_address(void * f,uint32_t addr)392 void panic_set_address(void *f, uint32_t addr)
393 {
394 ((RvExcFrame *)f)->mepc = addr;
395 }
396