1 /*
2 * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "freertos/xtensa_context.h"
8 #include "freertos/FreeRTOS.h"
9 #include "freertos/task.h"
10
11 #include "esp_debug_helpers.h"
12
13 #include "esp_private/panic_internal.h"
14 #include "esp_private/panic_reason.h"
15 #include "soc/soc.h"
16
17 #include "esp_private/cache_err_int.h"
18
19 #include "sdkconfig.h"
20
21 #if !CONFIG_IDF_TARGET_ESP32
22 #include "soc/extmem_reg.h"
23 #include "soc/ext_mem_defs.h"
24 #include "soc/rtc_cntl_reg.h"
25 #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
26 #ifdef CONFIG_IDF_TARGET_ESP32S2
27 #include "esp32s2/memprot.h"
28 #else
29 #include "esp_memprot.h"
30 #endif
31 #endif
32 #endif // CONFIG_IDF_TARGET_ESP32
33
panic_print_registers(const void * f,int core)34 void panic_print_registers(const void *f, int core)
35 {
36 XtExcFrame *frame = (XtExcFrame *) f;
37 int *regs = (int *)frame;
38 (void)regs;
39
40 const char *sdesc[] = {
41 "PC ", "PS ", "A0 ", "A1 ", "A2 ", "A3 ", "A4 ", "A5 ",
42 "A6 ", "A7 ", "A8 ", "A9 ", "A10 ", "A11 ", "A12 ", "A13 ",
43 "A14 ", "A15 ", "SAR ", "EXCCAUSE", "EXCVADDR", "LBEG ", "LEND ", "LCOUNT "
44 };
45
46 /* only dump registers for 'real' crashes, if crashing via abort()
47 the register window is no longer useful.
48 */
49 panic_print_str("Core ");
50 panic_print_dec(core);
51 panic_print_str(" register dump:");
52
53 for (int x = 0; x < 24; x += 4) {
54 panic_print_str("\r\n");
55 for (int y = 0; y < 4; y++) {
56 if (sdesc[x + y][0] != 0) {
57 panic_print_str(sdesc[x + y]);
58 panic_print_str(": 0x");
59 panic_print_hex(regs[x + y + 1]);
60 panic_print_str(" ");
61 }
62 }
63 }
64
65 // If the core which triggers the interrupt watchpoint was in ISR context, dump the epc registers.
66 if (xPortInterruptedFromISRContext()
67 #if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
68 && ((core == 0 && frame->exccause == PANIC_RSN_INTWDT_CPU0) ||
69 (core == 1 && frame->exccause == PANIC_RSN_INTWDT_CPU1))
70 #endif //!CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
71 ) {
72
73 panic_print_str("\r\n");
74
75 uint32_t __value;
76 panic_print_str("Core ");
77 panic_print_dec(core);
78 panic_print_str(" was running in ISR context:\r\n");
79
80 __asm__("rsr.epc1 %0" : "=a"(__value));
81 panic_print_str("EPC1 : 0x");
82 panic_print_hex(__value);
83
84 __asm__("rsr.epc2 %0" : "=a"(__value));
85 panic_print_str(" EPC2 : 0x");
86 panic_print_hex(__value);
87
88 __asm__("rsr.epc3 %0" : "=a"(__value));
89 panic_print_str(" EPC3 : 0x");
90 panic_print_hex(__value);
91
92 __asm__("rsr.epc4 %0" : "=a"(__value));
93 panic_print_str(" EPC4 : 0x");
94 panic_print_hex(__value);
95 }
96 }
97
print_illegal_instruction_details(const void * f)98 static void print_illegal_instruction_details(const void *f)
99 {
100 XtExcFrame *frame = (XtExcFrame *) f;
101 /* Print out memory around the instruction word */
102 uint32_t epc = frame->pc;
103 epc = (epc & ~0x3) - 4;
104
105 /* check that the address was sane */
106 if (epc < SOC_IROM_MASK_LOW || epc >= SOC_IROM_HIGH) {
107 return;
108 }
109 volatile uint32_t *pepc = (uint32_t *)epc;
110 (void)pepc;
111
112 panic_print_str("Memory dump at 0x");
113 panic_print_hex(epc);
114 panic_print_str(": ");
115
116 panic_print_hex(*pepc);
117 panic_print_str(" ");
118 panic_print_hex(*(pepc + 1));
119 panic_print_str(" ");
120 panic_print_hex(*(pepc + 2));
121 }
122
123
print_debug_exception_details(const void * f)124 static void print_debug_exception_details(const void *f)
125 {
126 int debug_rsn;
127 asm("rsr.debugcause %0":"=r"(debug_rsn));
128 panic_print_str("Debug exception reason: ");
129 if (debug_rsn & XCHAL_DEBUGCAUSE_ICOUNT_MASK) {
130 panic_print_str("SingleStep ");
131 }
132 if (debug_rsn & XCHAL_DEBUGCAUSE_IBREAK_MASK) {
133 panic_print_str("HwBreakpoint ");
134 }
135 if (debug_rsn & XCHAL_DEBUGCAUSE_DBREAK_MASK) {
136 //Unlike what the ISA manual says, this core seemingly distinguishes from a DBREAK
137 //reason caused by watchdog 0 and one caused by watchdog 1 by setting bit 8 of the
138 //debugcause if the cause is watchpoint 1 and clearing it if it's watchpoint 0.
139 if (debug_rsn & (1 << 8)) {
140 #if CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK
141 int core = 0;
142
143 #if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
144 if (f == g_exc_frames[1]) {
145 core = 1;
146 }
147 #endif
148
149 const char *name = pcTaskGetName(xTaskGetCurrentTaskHandleForCPU(core));
150 panic_print_str("Stack canary watchpoint triggered (");
151 panic_print_str(name);
152 panic_print_str(") ");
153 #else
154 panic_print_str("Watchpoint 1 triggered ");
155 #endif
156 } else {
157 panic_print_str("Watchpoint 0 triggered ");
158 }
159 }
160 if (debug_rsn & XCHAL_DEBUGCAUSE_BREAK_MASK) {
161 panic_print_str("BREAK instr ");
162 }
163 if (debug_rsn & XCHAL_DEBUGCAUSE_BREAKN_MASK) {
164 panic_print_str("BREAKN instr ");
165 }
166 if (debug_rsn & XCHAL_DEBUGCAUSE_DEBUGINT_MASK) {
167 panic_print_str("DebugIntr ");
168 }
169 }
170
171 #if CONFIG_IDF_TARGET_ESP32S2
print_cache_err_details(const void * f)172 static inline void print_cache_err_details(const void *f)
173 {
174 uint32_t vaddr = 0, size = 0;
175 uint32_t status[2];
176 status[0] = REG_READ(EXTMEM_CACHE_DBG_STATUS0_REG);
177 status[1] = REG_READ(EXTMEM_CACHE_DBG_STATUS1_REG);
178 for (int i = 0; i < 32; i++) {
179 switch (status[0] & BIT(i)) {
180 case EXTMEM_IC_SYNC_SIZE_FAULT_ST:
181 vaddr = REG_READ(EXTMEM_PRO_ICACHE_MEM_SYNC0_REG);
182 size = REG_READ(EXTMEM_PRO_ICACHE_MEM_SYNC1_REG);
183 panic_print_str("Icache sync parameter configuration error, the error address and size is 0x");
184 panic_print_hex(vaddr);
185 panic_print_str("(0x");
186 panic_print_hex(size);
187 panic_print_str(")\r\n");
188 break;
189 case EXTMEM_IC_PRELOAD_SIZE_FAULT_ST:
190 vaddr = REG_READ(EXTMEM_PRO_ICACHE_PRELOAD_ADDR_REG);
191 size = REG_READ(EXTMEM_PRO_ICACHE_PRELOAD_SIZE_REG);
192 panic_print_str("Icache preload parameter configuration error, the error address and size is 0x");
193 panic_print_hex(vaddr);
194 panic_print_str("(0x");
195 panic_print_hex(size);
196 panic_print_str(")\r\n");
197 break;
198 case EXTMEM_ICACHE_REJECT_ST:
199 vaddr = REG_READ(EXTMEM_PRO_ICACHE_REJECT_VADDR_REG);
200 panic_print_str("Icache reject error occurred while accessing the address 0x");
201 panic_print_hex(vaddr);
202
203 if (REG_READ(EXTMEM_PRO_CACHE_MMU_FAULT_CONTENT_REG) & MMU_INVALID) {
204 panic_print_str(" (invalid mmu entry)");
205 }
206 panic_print_str("\r\n");
207 break;
208 default:
209 break;
210 }
211 switch (status[1] & BIT(i)) {
212 case EXTMEM_DC_SYNC_SIZE_FAULT_ST:
213 vaddr = REG_READ(EXTMEM_PRO_DCACHE_MEM_SYNC0_REG);
214 size = REG_READ(EXTMEM_PRO_DCACHE_MEM_SYNC1_REG);
215 panic_print_str("Dcache sync parameter configuration error, the error address and size is 0x");
216 panic_print_hex(vaddr);
217 panic_print_str("(0x");
218 panic_print_hex(size);
219 panic_print_str(")\r\n");
220 break;
221 case EXTMEM_DC_PRELOAD_SIZE_FAULT_ST:
222 vaddr = REG_READ(EXTMEM_PRO_DCACHE_PRELOAD_ADDR_REG);
223 size = REG_READ(EXTMEM_PRO_DCACHE_PRELOAD_SIZE_REG);
224 panic_print_str("Dcache preload parameter configuration error, the error address and size is 0x");
225 panic_print_hex(vaddr);
226 panic_print_str("(0x");
227 panic_print_hex(size);
228 panic_print_str(")\r\n");
229 break;
230 case EXTMEM_DCACHE_WRITE_FLASH_ST:
231 panic_print_str("Write back error occurred while dcache tries to write back to flash\r\n");
232 break;
233 case EXTMEM_DCACHE_REJECT_ST:
234 vaddr = REG_READ(EXTMEM_PRO_DCACHE_REJECT_VADDR_REG);
235 panic_print_str("Dcache reject error occurred while accessing the address 0x");
236 panic_print_hex(vaddr);
237
238 if (REG_READ(EXTMEM_PRO_CACHE_MMU_FAULT_CONTENT_REG) & MMU_INVALID) {
239 panic_print_str(" (invalid mmu entry)");
240 }
241 panic_print_str("\r\n");
242 break;
243 case EXTMEM_MMU_ENTRY_FAULT_ST:
244 vaddr = REG_READ(EXTMEM_PRO_CACHE_MMU_FAULT_VADDR_REG);
245 panic_print_str("MMU entry fault error occurred while accessing the address 0x");
246 panic_print_hex(vaddr);
247
248 if (REG_READ(EXTMEM_PRO_CACHE_MMU_FAULT_CONTENT_REG) & MMU_INVALID) {
249 panic_print_str(" (invalid mmu entry)");
250 }
251 panic_print_str("\r\n");
252 break;
253 default:
254 break;
255 }
256 }
257 }
258
259 #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
260 #define MEMPROT_OP_INVALID 0xFFFFFFFF
print_memprot_err_details(const void * f)261 static inline void print_memprot_err_details(const void *f)
262 {
263 uint32_t *fault_addr;
264 uint32_t op_type = MEMPROT_OP_INVALID, op_subtype;
265 const char *operation_type;
266
267 mem_type_prot_t mem_type = esp_memprot_get_active_intr_memtype();
268 if (mem_type != MEMPROT_NONE) {
269 if (esp_memprot_get_fault_status(mem_type, &fault_addr, &op_type, &op_subtype) != ESP_OK) {
270 op_type = MEMPROT_OP_INVALID;
271 }
272 }
273
274 if (op_type == MEMPROT_OP_INVALID) {
275 operation_type = "Unknown";
276 fault_addr = (uint32_t *)MEMPROT_OP_INVALID;
277 } else {
278 if (op_type == 0) {
279 operation_type = (mem_type == MEMPROT_IRAM0_SRAM && op_subtype == 0) ? "Instruction fetch" : "Read";
280 } else {
281 operation_type = "Write";
282 }
283 }
284
285 panic_print_str(operation_type);
286 panic_print_str(" operation at address 0x");
287 panic_print_hex((uint32_t)fault_addr);
288 panic_print_str(" not permitted (");
289 panic_print_str(esp_memprot_type_to_str(mem_type));
290 panic_print_str(")\r\n");
291 }
292 #endif
293
294 #elif CONFIG_IDF_TARGET_ESP32S3
print_cache_err_details(const void * f)295 static inline void print_cache_err_details(const void *f)
296 {
297 uint32_t vaddr = 0, size = 0;
298 uint32_t status;
299 status = REG_READ(EXTMEM_CACHE_ILG_INT_ST_REG);
300 for (int i = 0; i < 32; i++) {
301 switch (status & BIT(i)) {
302 case EXTMEM_ICACHE_SYNC_OP_FAULT_ST:
303 //TODO, which size should fetch
304 //vaddr = REG_READ(EXTMEM_ICACHE_MEM_SYNC0_REG);
305 //size = REG_READ(EXTMEM_ICACHE_MEM_SYNC1_REG);
306 panic_print_str("Icache sync parameter configuration error, the error address and size is 0x");
307 panic_print_hex(vaddr);
308 panic_print_str("(0x");
309 panic_print_hex(size);
310 panic_print_str(")\r\n");
311 break;
312 case EXTMEM_ICACHE_PRELOAD_OP_FAULT_ST:
313 //TODO, which size should fetch
314 vaddr = REG_READ(EXTMEM_ICACHE_PRELOAD_ADDR_REG);
315 size = REG_READ(EXTMEM_ICACHE_PRELOAD_SIZE_REG);
316 panic_print_str("Icache preload parameter configuration error, the error address and size is 0x");
317 panic_print_hex(vaddr);
318 panic_print_str("(0x");
319 panic_print_hex(size);
320 panic_print_str(")\r\n");
321 break;
322 case EXTMEM_DCACHE_SYNC_OP_FAULT_ST:
323 //TODO, which size should fetch
324 //vaddr = REG_READ(EXTMEM_DCACHE_MEM_SYNC0_REG);
325 //size = REG_READ(EXTMEM_DCACHE_MEM_SYNC1_REG);
326 panic_print_str("Dcache sync parameter configuration error, the error address and size is 0x");
327 panic_print_hex(vaddr);
328 panic_print_str("(0x");
329 panic_print_hex(size);
330 panic_print_str(")\r\n");
331 break;
332 case EXTMEM_DCACHE_PRELOAD_OP_FAULT_ST:
333 //TODO, which size should fetch
334 vaddr = REG_READ(EXTMEM_DCACHE_PRELOAD_ADDR_REG);
335 size = REG_READ(EXTMEM_DCACHE_PRELOAD_SIZE_REG);
336 panic_print_str("Dcache preload parameter configuration error, the error address and size is 0x");
337 panic_print_hex(vaddr);
338 panic_print_str("(0x");
339 panic_print_hex(size);
340 panic_print_str(")\r\n");
341 break;
342 case EXTMEM_DCACHE_WRITE_FLASH_ST:
343 panic_print_str("Write back error occurred while dcache tries to write back to flash\r\n");
344 break;
345 case EXTMEM_MMU_ENTRY_FAULT_ST:
346 vaddr = REG_READ(EXTMEM_CACHE_MMU_FAULT_VADDR_REG);
347 panic_print_str("MMU entry fault error occurred while accessing the address 0x");
348 panic_print_hex(vaddr);
349
350 if (REG_READ(EXTMEM_CACHE_MMU_FAULT_CONTENT_REG) & MMU_INVALID) {
351 panic_print_str(" (invalid mmu entry)");
352 }
353 panic_print_str("\r\n");
354 break;
355 default:
356 break;
357 }
358 }
359 panic_print_str("\r\n");
360 }
361 #endif
362
363
panic_arch_fill_info(void * f,panic_info_t * info)364 void panic_arch_fill_info(void *f, panic_info_t *info)
365 {
366 XtExcFrame *frame = (XtExcFrame *) f;
367 static const char *reason[] = {
368 "IllegalInstruction", "Syscall", "InstructionFetchError", "LoadStoreError",
369 "Level1Interrupt", "Alloca", "IntegerDivideByZero", "PCValue",
370 "Privileged", "LoadStoreAlignment", "res", "res",
371 "InstrPDAddrError", "LoadStorePIFDataError", "InstrPIFAddrError", "LoadStorePIFAddrError",
372 "InstTLBMiss", "InstTLBMultiHit", "InstFetchPrivilege", "res",
373 "InstrFetchProhibited", "res", "res", "res",
374 "LoadStoreTLBMiss", "LoadStoreTLBMultihit", "LoadStorePrivilege", "res",
375 "LoadProhibited", "StoreProhibited", "res", "res",
376 "Cp0Dis", "Cp1Dis", "Cp2Dis", "Cp3Dis",
377 "Cp4Dis", "Cp5Dis", "Cp6Dis", "Cp7Dis"
378 };
379
380 if (frame->exccause < (sizeof(reason) / sizeof(char *))) {
381 info->reason = (reason[frame->exccause]);
382 } else {
383 info->reason = "Unknown";
384 }
385
386 info->description = "Exception was unhandled.";
387
388 if (frame->exccause == EXCCAUSE_ILLEGAL) {
389 info->details = print_illegal_instruction_details;
390 }
391
392 info->addr = ((void *) ((XtExcFrame *) frame)->pc);
393 }
394
panic_soc_fill_info(void * f,panic_info_t * info)395 void panic_soc_fill_info(void *f, panic_info_t *info)
396 {
397 // [refactor-todo] this should be in the common port panic_handler.c, once
398 // these special exceptions are supported in there.
399 XtExcFrame *frame = (XtExcFrame *) f;
400 if (frame->exccause == PANIC_RSN_INTWDT_CPU0) {
401 info->core = 0;
402 info->exception = PANIC_EXCEPTION_IWDT;
403 } else if (frame->exccause == PANIC_RSN_INTWDT_CPU1) {
404 info->core = 1;
405 info->exception = PANIC_EXCEPTION_IWDT;
406 } else if (frame->exccause == PANIC_RSN_CACHEERR) {
407 info->core = esp_cache_err_get_cpuid();
408 } else {}
409
410 //Please keep in sync with PANIC_RSN_* defines
411 static const char *pseudo_reason[] = {
412 "Unknown reason",
413 "Unhandled debug exception",
414 "Double exception",
415 "Unhandled kernel exception",
416 "Coprocessor exception",
417 "Interrupt wdt timeout on CPU0",
418 "Interrupt wdt timeout on CPU1",
419 "Cache disabled but cached memory region accessed",
420 };
421
422 info->reason = pseudo_reason[0];
423 info->description = NULL;
424
425 if (frame->exccause <= PANIC_RSN_MAX) {
426 info->reason = pseudo_reason[frame->exccause];
427 }
428
429 if (frame->exccause == PANIC_RSN_DEBUGEXCEPTION) {
430 info->details = print_debug_exception_details;
431 info->exception = PANIC_EXCEPTION_DEBUG;
432 }
433
434 //MV note: ESP32S3 PMS handling?
435
436 #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
437 if (frame->exccause == PANIC_RSN_CACHEERR) {
438 #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE && CONFIG_IDF_TARGET_ESP32S2
439 if ( esp_memprot_is_intr_ena_any() ) {
440 info->details = print_memprot_err_details;
441 info->reason = "Memory protection fault";
442 } else
443 #endif
444 {
445 info->details = print_cache_err_details;
446 }
447 }
448 #endif
449 }
450
panic_get_address(const void * f)451 uint32_t panic_get_address(const void *f)
452 {
453 return ((XtExcFrame *)f)->pc;
454 }
455
panic_get_cause(const void * f)456 uint32_t panic_get_cause(const void *f)
457 {
458 return ((XtExcFrame *)f)->exccause;
459 }
460
panic_set_address(void * f,uint32_t addr)461 void panic_set_address(void *f, uint32_t addr)
462 {
463 ((XtExcFrame *)f)->pc = addr;
464 }
465
panic_print_backtrace(const void * f,int core)466 void panic_print_backtrace(const void *f, int core)
467 {
468 XtExcFrame *xt_frame = (XtExcFrame *) f;
469 esp_backtrace_frame_t frame = {.pc = xt_frame->pc, .sp = xt_frame->a1, .next_pc = xt_frame->a0, .exc_frame = xt_frame};
470 esp_backtrace_print_from_frame(100, &frame, true);
471 }
472