1 /*
2  * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief Core dump port implementation for Xtensa based boards.
10  */
11 
12 #include <string.h>
13 #include <stdbool.h>
14 #include "soc/soc_memory_layout.h"
15 #include "freertos/FreeRTOS.h"
16 #include "freertos/task.h"
17 #include "freertos/xtensa_context.h"
18 #include "esp_rom_sys.h"
19 #include "esp_core_dump_common.h"
20 #include "esp_core_dump_port.h"
21 #include "esp_debug_helpers.h"
22 
23 const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_port";
24 
25 #define min(a,b) ((a) < (b) ? (a) : (b))
26 
27 #define COREDUMP_EM_XTENSA                  0x5E
28 #define COREDUMP_INVALID_CAUSE_VALUE        0xFFFF
29 #define COREDUMP_FAKE_STACK_START           0x20000000
30 #define COREDUMP_FAKE_STACK_LIMIT           0x30000000
31 #define COREDUMP_EXTRA_REG_NUM              16
32 
33 #define COREDUMP_GET_REG_PAIR(reg_idx, reg_ptr) {   *(uint32_t*)(reg_ptr++) = (uint32_t)reg_idx; \
34                                                     RSR(reg_idx, *(uint32_t*)(reg_ptr++)); \
35                                                 }
36 
37 #define COREDUMP_GET_EPC(reg, ptr) \
38     if (reg - EPC_1 + 1 <= XCHAL_NUM_INTLEVELS) COREDUMP_GET_REG_PAIR(reg, ptr)
39 
40 #define COREDUMP_GET_EPS(reg, ptr) \
41     if (reg - EPS_2 + 2 <= XCHAL_NUM_INTLEVELS) COREDUMP_GET_REG_PAIR(reg, ptr)
42 
43 // Enumeration of registers of exception stack frame
44 // and solicited stack frame
45 typedef enum
46 {
47     // XT_SOL_EXIT = 0,
48     XT_SOL_PC = 1,
49     XT_SOL_PS = 2,
50     // XT_SOL_NEXT = 3,
51     XT_SOL_AR_START = 4,
52     XT_SOL_AR_NUM = 4,
53     // XT_SOL_FRMSZ = 8,
54     XT_STK_EXIT = 0,
55     XT_STK_PC = 1,
56     XT_STK_PS = 2,
57     XT_STK_AR_START = 3,
58     XT_STK_AR_NUM = 16,
59     XT_STK_SAR = 19,
60     XT_STK_EXCCAUSE = 20,
61     XT_STK_EXCVADDR = 21,
62     XT_STK_LBEG = 22,
63     XT_STK_LEND = 23,
64     XT_STK_LCOUNT = 24,
65     //XT_STK_FRMSZ = 25,
66 } stk_frame_t;
67 
68 // Xtensa ELF core file register set representation ('.reg' section).
69 // Copied from target-side ELF header <xtensa/elf.h>.
70 typedef struct
71 {
72     uint32_t pc;
73     uint32_t ps;
74     uint32_t lbeg;
75     uint32_t lend;
76     uint32_t lcount;
77     uint32_t sar;
78     uint32_t windowstart;
79     uint32_t windowbase;
80     uint32_t reserved[8+48];
81     uint32_t ar[XCHAL_NUM_AREGS];
82 } __attribute__((packed)) xtensa_gregset_t;
83 
84 typedef struct
85 {
86     uint32_t reg_index;
87     uint32_t reg_val;
88 } __attribute__((packed)) core_dump_reg_pair_t;
89 
90 typedef struct
91 {
92     uint32_t crashed_task_tcb;
93     core_dump_reg_pair_t exccause;
94     core_dump_reg_pair_t excvaddr;
95     core_dump_reg_pair_t extra_regs[COREDUMP_EXTRA_REG_NUM];
96 } __attribute__((packed)) xtensa_extra_info_t;
97 
98 // Xtensa Program Status for GDB
99 typedef struct
100 {
101     uint32_t si_signo;
102     uint32_t si_code;
103     uint32_t si_errno;
104     uint16_t pr_cursig;
105     uint16_t pr_pad0;
106     uint32_t pr_sigpend;
107     uint32_t pr_sighold;
108     uint32_t pr_pid;
109     uint32_t pr_ppid;
110     uint32_t pr_pgrp;
111     uint32_t pr_sid;
112     uint64_t pr_utime;
113     uint64_t pr_stime;
114     uint64_t pr_cutime;
115     uint64_t pr_cstime;
116 } __attribute__((packed)) xtensa_pr_status_t;
117 
118 typedef struct
119 {
120     xtensa_pr_status_t pr_status;
121     xtensa_gregset_t regs;
122     // Todo: acc to xtensa_gregset_t number of regs must be 128,
123     // but gdb complains when it less than 129
124     uint32_t reserved;
125 } __attribute__((packed)) xtensa_elf_reg_dump_t;
126 
127 #if CONFIG_ESP_COREDUMP_ENABLE
128 
129 static XtExcFrame s_fake_stack_frame = {
130     .pc   = (UBaseType_t) COREDUMP_FAKE_STACK_START,                        // task entrypoint fake_ptr
131     .a0   = (UBaseType_t) 0,                                                // to terminate GDB backtrace
132     .a1   = (UBaseType_t) (COREDUMP_FAKE_STACK_START + sizeof(XtExcFrame)), // physical top of stack frame
133     .exit = (UBaseType_t) 0,                                                // user exception exit dispatcher
134     .ps = (PS_UM | PS_EXCM),
135     .exccause = (UBaseType_t) COREDUMP_INVALID_CAUSE_VALUE,
136 };
137 
138 /* Keep a track of the number of fake stack distributed. Avoid giving the
139  * same fake address to two different tasks. */
140 static uint32_t s_fake_stacks_num = 0;
141 
142 static xtensa_extra_info_t s_extra_info;
143 
144 /**
145  * The function creates small fake stack for task as deep as exception frame size
146  * It is required for gdb to take task into account but avoid back trace of stack.
147  * The espcoredump.py script is able to recognize that task is broken
148  */
esp_core_dump_get_fake_stack(uint32_t * stk_len)149 static void *esp_core_dump_get_fake_stack(uint32_t *stk_len)
150 {
151     *stk_len = sizeof(s_fake_stack_frame);
152     return (uint8_t*)COREDUMP_FAKE_STACK_START + sizeof(s_fake_stack_frame)*s_fake_stacks_num++;
153 }
154 
155 
esp_core_dump_get_epc_regs(core_dump_reg_pair_t * src)156 static core_dump_reg_pair_t *esp_core_dump_get_epc_regs(core_dump_reg_pair_t* src)
157 {
158     uint32_t* reg_ptr = (uint32_t*)src;
159     // get InterruptException program counter registers
160     COREDUMP_GET_EPC(EPC_1, reg_ptr);
161     COREDUMP_GET_EPC(EPC_2, reg_ptr);
162     COREDUMP_GET_EPC(EPC_3, reg_ptr);
163     COREDUMP_GET_EPC(EPC_4, reg_ptr);
164     COREDUMP_GET_EPC(EPC_5, reg_ptr);
165     COREDUMP_GET_EPC(EPC_6, reg_ptr);
166     COREDUMP_GET_EPC(EPC_7, reg_ptr);
167     return (core_dump_reg_pair_t*)reg_ptr;
168 }
169 
esp_core_dump_get_eps_regs(core_dump_reg_pair_t * src)170 static core_dump_reg_pair_t *esp_core_dump_get_eps_regs(core_dump_reg_pair_t* src)
171 {
172     uint32_t* reg_ptr = (uint32_t*)src;
173     // get InterruptException processor state registers
174     COREDUMP_GET_EPS(EPS_2, reg_ptr);
175     COREDUMP_GET_EPS(EPS_3, reg_ptr);
176     COREDUMP_GET_EPS(EPS_4, reg_ptr);
177     COREDUMP_GET_EPS(EPS_5, reg_ptr);
178     COREDUMP_GET_EPS(EPS_6, reg_ptr);
179     COREDUMP_GET_EPS(EPS_7, reg_ptr);
180     return (core_dump_reg_pair_t*)reg_ptr;
181 }
182 
183 // Returns list of registers (in GDB format) from xtensa stack frame
esp_core_dump_get_regs_from_stack(void * stack_addr,size_t size,xtensa_gregset_t * regs)184 static esp_err_t esp_core_dump_get_regs_from_stack(void* stack_addr,
185                                                size_t size,
186                                                xtensa_gregset_t* regs)
187 {
188     XtExcFrame* exc_frame = (XtExcFrame*)stack_addr;
189     uint32_t* stack_arr = (uint32_t*)stack_addr;
190 
191     if (size < sizeof(XtExcFrame)) {
192         ESP_COREDUMP_LOGE("Too small stack to keep frame: %d bytes!", size);
193         return ESP_FAIL;
194     }
195 
196     // Stack frame type indicator is always the first item
197     uint32_t rc = exc_frame->exit;
198 
199     // is this current crashed task?
200     if (rc == COREDUMP_CURR_TASK_MARKER)
201     {
202         s_extra_info.exccause.reg_val = exc_frame->exccause;
203         s_extra_info.exccause.reg_index = EXCCAUSE;
204         s_extra_info.excvaddr.reg_val = exc_frame->excvaddr;
205         s_extra_info.excvaddr.reg_index = EXCVADDR;
206         // get InterruptException registers into extra_info
207         core_dump_reg_pair_t *regs_ptr = esp_core_dump_get_eps_regs(s_extra_info.extra_regs);
208         esp_core_dump_get_epc_regs(regs_ptr);
209     } else {
210         // initialize EXCCAUSE and EXCVADDR members of frames for all the tasks,
211         // except for the crashed one
212         exc_frame->exccause = COREDUMP_INVALID_CAUSE_VALUE;
213         exc_frame->excvaddr = 0;
214     }
215 
216     if (rc != 0) {
217         regs->pc = exc_frame->pc;
218         regs->ps = exc_frame->ps;
219         for (int i = 0; i < XT_STK_AR_NUM; i++) {
220             regs->ar[i] = stack_arr[XT_STK_AR_START + i];
221         }
222         regs->sar = exc_frame->sar;
223 #if XCHAL_HAVE_LOOPS
224         regs->lbeg = exc_frame->lbeg;
225         regs->lend = exc_frame->lend;
226         regs->lcount = exc_frame->lcount;
227 #endif
228         // FIXME: crashed and some running tasks (e.g. prvIdleTask) have EXCM bit set
229         // and GDB can not unwind callstack properly (it implies not windowed call0)
230         if (regs->ps & PS_UM) {
231             regs->ps &= ~PS_EXCM;
232         }
233     } else {
234         regs->pc = stack_arr[XT_SOL_PC];
235         regs->ps = stack_arr[XT_SOL_PS];
236         for (int i = 0; i < XT_SOL_AR_NUM; i++) {
237             regs->ar[i] = stack_arr[XT_SOL_AR_START + i];
238         }
239         regs->pc = (regs->pc & 0x3fffffff);
240         if (regs->pc & 0x80000000) {
241             regs->pc = (regs->pc & 0x3fffffff);
242         }
243         if (regs->ar[0] & 0x80000000) {
244             regs->ar[0] = (regs->ar[0] & 0x3fffffff);
245         }
246     }
247     return ESP_OK;
248 }
249 
esp_core_dump_port_init(panic_info_t * info)250 inline void esp_core_dump_port_init(panic_info_t *info)
251 {
252     s_extra_info.crashed_task_tcb = COREDUMP_CURR_TASK_MARKER;
253     // Initialize exccause register to default value (required if current task corrupted)
254     s_extra_info.exccause.reg_val = COREDUMP_INVALID_CAUSE_VALUE;
255     s_extra_info.exccause.reg_index = EXCCAUSE;
256 
257     XtExcFrame *s_exc_frame = (XtExcFrame *) info->frame;
258     s_exc_frame->exit = COREDUMP_CURR_TASK_MARKER;
259     if (info->pseudo_excause) {
260         s_exc_frame->exccause += XCHAL_EXCCAUSE_NUM;
261     }
262 }
263 
264 /**
265  * Get the architecture ID.
266  * Check core dump port interface for more information about this function.
267  */
esp_core_dump_get_arch_id()268 inline uint16_t esp_core_dump_get_arch_id()
269 {
270     return COREDUMP_EM_XTENSA;
271 }
272 
esp_core_dump_reset_fake_stacks(void)273 void esp_core_dump_reset_fake_stacks(void)
274 {
275     s_fake_stacks_num = 0;
276 }
277 
278 /* Get the top of the ISR stack.
279  * Check core dump port interface for more information about this function.
280  */
esp_core_dump_get_isr_stack_top(void)281 uint8_t* esp_core_dump_get_isr_stack_top(void) {
282     extern uint8_t port_IntStack;
283     return &port_IntStack;
284 }
285 
esp_core_dump_get_isr_stack_end(void)286  uint32_t esp_core_dump_get_isr_stack_end(void)
287  {
288     uint8_t* isr_top_stack = esp_core_dump_get_isr_stack_top();
289     return (uint32_t)(isr_top_stack + (xPortGetCoreID()+1)*configISR_STACK_SIZE);
290  }
291 
292 
esp_core_dump_task_stack_end_is_sane(uint32_t sp)293 static inline bool esp_core_dump_task_stack_end_is_sane(uint32_t sp)
294 {
295     return esp_ptr_in_dram((void *)sp)
296 #if CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY
297         || esp_stack_ptr_in_extram(sp)
298 #endif
299 #if CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP
300         || esp_ptr_in_rtc_dram_fast((void*) sp)
301 #endif
302     ;
303 }
304 
305 
esp_core_dump_check_stack(core_dump_task_header_t * task)306 bool esp_core_dump_check_stack(core_dump_task_header_t *task)
307 {
308     // Check task's stack
309     if (!esp_stack_ptr_is_sane(task->stack_start) ||
310         !esp_core_dump_task_stack_end_is_sane(task->stack_end) ||
311         (task->stack_start >= task->stack_end) ||
312         ((task->stack_end-task->stack_start) > COREDUMP_MAX_TASK_STACK_SIZE)) {
313         // Check if current task stack is corrupted
314         ESP_COREDUMP_LOG_PROCESS("Invalid stack (%x...%x)!", task->stack_start, task->stack_end);
315         return false;
316     }
317     return true;
318 }
319 
320 /**
321  * Check if the memory segment is sane
322  *
323  * Check the header file for more information.
324  */
esp_core_dump_mem_seg_is_sane(uint32_t addr,uint32_t sz)325 bool esp_core_dump_mem_seg_is_sane(uint32_t addr, uint32_t sz)
326 {
327     //TODO: external SRAM not supported yet
328     return (esp_ptr_in_dram((void *)addr) && esp_ptr_in_dram((void *)(addr+sz-1)))
329         || (esp_ptr_in_rtc_slow((void *)addr) && esp_ptr_in_rtc_slow((void *)(addr+sz-1)))
330         || (esp_ptr_in_rtc_dram_fast((void *)addr) && esp_ptr_in_rtc_dram_fast((void *)(addr+sz-1)))
331         || (esp_ptr_in_iram((void *)addr) && esp_ptr_in_iram((void *)(addr+sz-1)));
332 }
333 
334 /**
335  * Get the stack of a task.
336  * Check core dump port interface for more information about this function.
337  */
esp_core_dump_get_stack(core_dump_task_header_t * task,uint32_t * stk_vaddr,uint32_t * stk_paddr)338 uint32_t esp_core_dump_get_stack(core_dump_task_header_t *task,
339                                  uint32_t* stk_vaddr, uint32_t* stk_paddr)
340 {
341     const uint32_t stack_len = abs(task->stack_start - task->stack_end);
342     const uint32_t stack_addr = min(task->stack_start, task->stack_end);
343 
344     ESP_COREDUMP_DEBUG_ASSERT(stk_paddr != NULL && stk_vaddr != NULL);
345 
346     /* Provide the virtual stack address for any task. */
347     *stk_vaddr = stack_addr;
348 
349     if (stack_addr >= COREDUMP_FAKE_STACK_START &&
350         stack_addr < COREDUMP_FAKE_STACK_LIMIT) {
351         /* In this case, the stack address pointed by the task is a fake stack
352          * generated previously. So it doesn't really point to actual data.
353          * Thus, we must provide the address of the fake stack data. */
354         *stk_paddr = (uint32_t) &s_fake_stack_frame;
355     } else {
356         *stk_paddr = stack_addr;
357     }
358 
359     return stack_len;
360 }
361 
362 /**
363  * Check the task passed as a parameter.
364  * Check core dump port interface for more information about this function.
365  */
esp_core_dump_check_task(core_dump_task_header_t * task)366 bool esp_core_dump_check_task(core_dump_task_header_t *task)
367 {
368     uint32_t stk_size = 0;
369     bool stack_is_valid = false;
370 
371     if (!esp_core_dump_tcb_addr_is_sane((uint32_t)task->tcb_addr)) {
372         ESP_COREDUMP_LOG_PROCESS("Bad TCB addr=%x!", task->tcb_addr);
373         return false;
374     }
375 
376     stack_is_valid = esp_core_dump_check_stack(task);
377     if (!stack_is_valid) {
378         // Skip saving of invalid task if stack corrupted
379         ESP_COREDUMP_LOG_PROCESS("Task (TCB:%x), stack is corrupted (%x, %x)",
380                                     task->tcb_addr,
381                                     task->stack_start,
382                                     task->stack_end);
383         task->stack_start = (uint32_t)esp_core_dump_get_fake_stack(&stk_size);
384         task->stack_end = (uint32_t)(task->stack_start + stk_size);
385         ESP_COREDUMP_LOG_PROCESS("Task (TCB:%x), use start, end (%x, %x)",
386                                             task->tcb_addr,
387                                             task->stack_start,
388                                             task->stack_end);
389     } else {
390         /* This shall be done only if the stack was correct, else, stack_start
391          * would point to a fake address. */
392         XtSolFrame *sol_frame = (XtSolFrame *)task->stack_start;
393         if (sol_frame->exit == 0) {
394             ESP_COREDUMP_LOG_PROCESS("Task (TCB:%x), EXIT/PC/PS/A0/SP %x %x %x %x %x",
395                                         task->tcb_addr,
396                                         sol_frame->exit,
397                                         sol_frame->pc,
398                                         sol_frame->ps,
399                                         sol_frame->a0,
400                                         sol_frame->a1);
401         } else {
402     // to avoid warning that 'exc_frame' is unused when ESP_COREDUMP_LOG_PROCESS does nothing
403     #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
404             XtExcFrame *exc_frame = (XtExcFrame *)task->stack_start;
405             ESP_COREDUMP_LOG_PROCESS("Task (TCB:%x) EXIT/PC/PS/A0/SP %x %x %x %x %x",
406                                         task->tcb_addr,
407                                         exc_frame->exit,
408                                         exc_frame->pc,
409                                         exc_frame->ps,
410                                         exc_frame->a0,
411                                         exc_frame->a1);
412     #endif
413         }
414     }
415     return true;
416 }
417 
418 
419 /**
420  * Get a dump of the task's registers.
421  * Check core dump port interface for more information about this function.
422  */
esp_core_dump_get_task_regs_dump(core_dump_task_header_t * task,void ** reg_dump)423 uint32_t esp_core_dump_get_task_regs_dump(core_dump_task_header_t *task, void **reg_dump)
424 {
425     uint32_t stack_vaddr = 0;
426     uint32_t stack_paddr = 0;
427     uint32_t stack_len = 0;
428     static xtensa_elf_reg_dump_t s_reg_dump = { 0 };
429 
430     ESP_COREDUMP_DEBUG_ASSERT(task != NULL && reg_dump != NULL);
431 
432     stack_len = esp_core_dump_get_stack(task, &stack_vaddr, &stack_paddr);
433 
434     ESP_COREDUMP_LOG_PROCESS("Add regs for task 0x%x", task->tcb_addr);
435 
436     // initialize program status for the task
437     s_reg_dump.pr_status.pr_cursig = 0;
438     s_reg_dump.pr_status.pr_pid = (uint32_t)task->tcb_addr;
439 
440     // fill the gdb registers structure from stack
441     esp_err_t err = esp_core_dump_get_regs_from_stack((void*)stack_paddr,
442                                                         stack_len,
443                                                         &s_reg_dump.regs);
444     if (err != ESP_OK) {
445         ESP_COREDUMP_LOGE("Error while registers processing.");
446     }
447     *reg_dump = &s_reg_dump;
448     return sizeof(s_reg_dump);
449 }
450 
451 
esp_core_dump_port_set_crashed_tcb(uint32_t handle)452 void esp_core_dump_port_set_crashed_tcb(uint32_t handle) {
453     s_extra_info.crashed_task_tcb = handle;
454 }
455 
456 /**
457  * Retrieve the extra information.
458  * Check core dump port interface for more information about this function.
459  */
esp_core_dump_get_extra_info(void ** info)460 uint32_t esp_core_dump_get_extra_info(void **info)
461 {
462     if (info) {
463         *info = &s_extra_info;
464     }
465     return sizeof(s_extra_info);
466 }
467 
468 #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF
469 
esp_core_dump_summary_parse_extra_info(esp_core_dump_summary_t * summary,void * ei_data)470 void esp_core_dump_summary_parse_extra_info(esp_core_dump_summary_t *summary, void *ei_data)
471 {
472     int i;
473     xtensa_extra_info_t *ei = (xtensa_extra_info_t *) ei_data;
474     summary->exc_tcb = ei->crashed_task_tcb;
475     summary->ex_info.exc_vaddr = ei->excvaddr.reg_val;
476     summary->ex_info.exc_cause = ei->exccause.reg_val;
477     ESP_COREDUMP_LOGD("Crash TCB 0x%x", summary->exc_tcb);
478     ESP_COREDUMP_LOGD("excvaddr 0x%x", summary->ex_info.exc_vaddr);
479     ESP_COREDUMP_LOGD("exccause 0x%x", summary->ex_info.exc_cause);
480 
481     memset(summary->ex_info.epcx, 0, sizeof(summary->ex_info.epcx));
482     summary->ex_info.epcx_reg_bits = 0;
483     for (i = 0; i < COREDUMP_EXTRA_REG_NUM; i++ ) {
484         if (ei->extra_regs[i].reg_index >= EPC_1
485             && ei->extra_regs[i].reg_index < (EPC_1 + XCHAL_NUM_INTLEVELS)) {
486             summary->ex_info.epcx[ei->extra_regs[i].reg_index - EPC_1] = ei->extra_regs[i].reg_val;
487             summary->ex_info.epcx_reg_bits |= (1 << (ei->extra_regs[i].reg_index - EPC_1));
488         }
489     }
490 }
491 
esp_core_dump_summary_parse_exc_regs(esp_core_dump_summary_t * summary,void * stack_data)492 void esp_core_dump_summary_parse_exc_regs(esp_core_dump_summary_t *summary, void *stack_data)
493 {
494     int i;
495     long *a_reg;
496     XtExcFrame *stack = (XtExcFrame *) stack_data;
497     summary->exc_pc = esp_cpu_process_stack_pc(stack->pc);
498     ESP_COREDUMP_LOGD("Crashing PC 0x%x", summary->exc_pc);
499 
500     a_reg = &stack->a0;
501     for (i = 0; i < 16; i++) {
502         summary->ex_info.exc_a[i] = a_reg[i];
503         ESP_COREDUMP_LOGD("A[%d] 0x%x", i, summary->ex_info.exc_a[i]);
504     }
505 }
506 
esp_core_dump_summary_parse_backtrace_info(esp_core_dump_bt_info_t * bt_info,const void * vaddr,const void * paddr,uint32_t stack_size)507 void esp_core_dump_summary_parse_backtrace_info(esp_core_dump_bt_info_t *bt_info, const void *vaddr,
508                                                 const void *paddr, uint32_t stack_size)
509 {
510     if (!vaddr || !paddr || !bt_info) {
511         return;
512     }
513 
514     int offset;
515     bool corrupted;
516     esp_backtrace_frame_t frame;
517     XtExcFrame *stack = (XtExcFrame *) paddr;
518     int max_depth = (int) (sizeof(bt_info->bt) / sizeof(bt_info->bt[0]));
519     int index = 0;
520 
521     frame.pc = stack->pc;
522     frame.sp = stack->a1;
523     frame.next_pc = stack->a0;
524 
525     corrupted = !(esp_stack_ptr_is_sane(frame.sp) &&
526                 (esp_ptr_executable((void *)esp_cpu_process_stack_pc(frame.pc)) ||
527                 stack->exccause == EXCCAUSE_INSTR_PROHIBITED)); /* Ignore the first corrupted PC in case of InstrFetchProhibited */
528 
529     /* vaddr is actual stack address when crash occurred. However that stack is now saved
530      * in the flash at a different location. Hence for each SP, we need to adjust the offset
531      * to point to next frame in the flash */
532     offset = (uint32_t) stack - (uint32_t) vaddr;
533 
534     ESP_COREDUMP_LOGD("Crash Backtrace");
535     bt_info->bt[index] = esp_cpu_process_stack_pc(frame.pc);
536     ESP_COREDUMP_LOGD(" 0x%x", bt_info->bt[index]);
537     index++;
538 
539     while (max_depth-- > 0 && frame.next_pc && !corrupted) {
540         /* Check if the Stack Pointer is in valid address range */
541         if (!((uint32_t)frame.sp >= (uint32_t)vaddr &&
542             ((uint32_t)frame.sp <= (uint32_t)vaddr + stack_size))) {
543             corrupted = true;
544             break;
545         }
546         /* Adjusting the SP to address in flash than in actual RAM */
547         frame.sp += offset;
548         if (!esp_backtrace_get_next_frame(&frame)) {
549             corrupted = true;
550         }
551         if (corrupted == false) {
552             bt_info->bt[index] = esp_cpu_process_stack_pc(frame.pc);
553             ESP_COREDUMP_LOGD(" 0x%x", bt_info->bt[index]);
554             index++;
555         }
556     }
557     bt_info->depth = index;
558     bt_info->corrupted = corrupted;
559 }
560 
561 #endif /* #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF */
562 
563 #endif
564