| /* |
| * Copyright (c) 2020 Espressif Systems (Shanghai) Co., Ltd. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include "xtensa/corebits.h" |
| #include "xtensa_backtrace.h" |
| #include "sys/printk.h" |
| #if defined(CONFIG_SOC_ESP32) |
| #include "soc/soc_memory_layout.h" |
| #endif |
| static int mask, cause; |
| |
| static inline uint32_t z_xtensa_cpu_process_stack_pc(uint32_t pc) |
| { |
| if (pc & 0x80000000) { |
| /* Top two bits of a0 (return address) specify window increment. |
| * Overwrite to map to address space. |
| */ |
| if (cause != EXCCAUSE_INSTR_PROHIBITED) { |
| pc = (pc & 0x3fffffff) | mask; |
| } else { |
| pc = (pc & 0x3fffffff) | 0x40000000; |
| } |
| } |
| /* Minus 3 to get PC of previous instruction |
| * (i.e. instruction executed before return address) |
| */ |
| return pc - 3; |
| } |
| |
| static inline bool z_xtensa_stack_ptr_is_sane(uint32_t sp) |
| { |
| #if defined(CONFIG_SOC_ESP32) |
| return esp_stack_ptr_is_sane(sp); |
| #else |
| #warning "z_xtensa_stack_ptr_is_sane is not defined for this platform" |
| #endif |
| } |
| |
| static inline bool z_xtensa_ptr_executable(const void *p) |
| { |
| #if defined(CONFIG_SOC_ESP32) |
| return esp_ptr_executable(p); |
| #else |
| #warning "z_xtensa_ptr_executable is not defined for this platform" |
| #endif |
| } |
| |
| bool z_xtensa_backtrace_get_next_frame(struct z_xtensa_backtrace_frame_t *frame) |
| { |
| /* Use frame(i-1)'s BS area located below frame(i)'s |
| * sp to get frame(i-1)'s sp and frame(i-2)'s pc |
| */ |
| |
| /* Base save area consists of 4 words under SP */ |
| char *base_save = (char *)frame->sp; |
| |
| frame->pc = frame->next_pc; |
| /* If next_pc = 0, indicates frame(i-1) is the last |
| * frame on the stack |
| */ |
| frame->next_pc = *((uint32_t *)(base_save - 16)); |
| frame->sp = *((uint32_t *)(base_save - 12)); |
| |
| /* Return true if both sp and pc of frame(i-1) are sane, |
| * false otherwise |
| */ |
| return (z_xtensa_stack_ptr_is_sane(frame->sp) && |
| z_xtensa_ptr_executable((void *) |
| z_xtensa_cpu_process_stack_pc(frame->pc))); |
| } |
| |
| int z_xtensa_backtrace_print(int depth, int *interrupted_stack) |
| { |
| /* Check arguments */ |
| if (depth <= 0) { |
| return -1; |
| } |
| |
| /* Initialize stk_frame with first frame of stack */ |
| struct z_xtensa_backtrace_frame_t stk_frame; |
| |
| z_xtensa_backtrace_get_start(&(stk_frame.pc), &(stk_frame.sp), |
| &(stk_frame.next_pc), interrupted_stack); |
| __asm__ volatile("l32i a4, a3, 0"); |
| __asm__ volatile("l32i a4, a4, 4"); |
| __asm__ volatile("mov %0, a4" : "=r"(cause)); |
| if (cause != EXCCAUSE_INSTR_PROHIBITED) { |
| mask = stk_frame.pc & 0xc0000000; |
| } |
| printk("\r\n\r\nBacktrace:"); |
| printk("0x%08X:0x%08X ", |
| z_xtensa_cpu_process_stack_pc(stk_frame.pc), |
| stk_frame.sp); |
| |
| /* Check if first frame is valid */ |
| bool corrupted = !(z_xtensa_stack_ptr_is_sane(stk_frame.sp) && |
| (z_xtensa_ptr_executable((void *) |
| z_xtensa_cpu_process_stack_pc(stk_frame.pc)) || |
| /* Ignore the first corrupted PC in case of InstrFetchProhibited */ |
| cause == EXCCAUSE_INSTR_PROHIBITED)); |
| |
| uint32_t i = (depth <= 0) ? INT32_MAX : depth; |
| |
| while (i-- > 0 && stk_frame.next_pc != 0 && !corrupted) { |
| /* Get previous stack frame */ |
| if (!z_xtensa_backtrace_get_next_frame(&stk_frame)) { |
| corrupted = true; |
| } |
| printk("0x%08X:0x%08X ", z_xtensa_cpu_process_stack_pc(stk_frame.pc), stk_frame.sp); |
| } |
| |
| /* Print backtrace termination marker */ |
| int ret = 0; |
| |
| if (corrupted) { |
| printk(" |<-CORRUPTED"); |
| ret = -1; |
| } else if (stk_frame.next_pc != 0) { /* Backtrace continues */ |
| printk(" |<-CONTINUES"); |
| } |
| printk("\r\n\r\n"); |
| return ret; |
| } |