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