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