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