/* * Copyright (c) 2020 Espressif Systems (Shanghai) Co., Ltd. * * SPDX-License-Identifier: Apache-2.0 */ #include "xtensa/corebits.h" #include "xtensa_backtrace.h" #include #if defined(CONFIG_SOC_SERIES_ESP32) #include #elif defined(CONFIG_SOC_FAMILY_INTEL_ADSP) #include "debug_helpers.h" #elif defined(CONFIG_SOC_XTENSA_DC233C) #include "backtrace_helpers.h" #endif static int mask, cause; static inline uint32_t 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 xtensa_stack_ptr_is_sane(uint32_t sp) { #if defined(CONFIG_SOC_SERIES_ESP32) return esp_stack_ptr_is_sane(sp); #elif defined(CONFIG_SOC_FAMILY_INTEL_ADSP) return intel_adsp_ptr_is_sane(sp); #elif defined(CONFIG_SOC_XTENSA_DC233C) return xtensa_dc233c_stack_ptr_is_sane(sp); #else #warning "xtensa_stack_ptr_is_sane is not defined for this platform" #endif } static inline bool xtensa_ptr_executable(const void *p) { #if defined(CONFIG_SOC_SERIES_ESP32) return esp_ptr_executable(p); #elif defined(CONFIG_SOC_FAMILY_INTEL_ADSP) return intel_adsp_ptr_executable(p); #elif defined(CONFIG_SOC_XTENSA_DC233C) return xtensa_dc233c_ptr_executable(p); #else #warning "xtensa_ptr_executable is not defined for this platform" #endif } bool xtensa_backtrace_get_next_frame(struct 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 (xtensa_stack_ptr_is_sane(frame->sp) && xtensa_ptr_executable((void *) xtensa_cpu_process_stack_pc(frame->pc))); } int xtensa_backtrace_print(int depth, int *interrupted_stack) { /* Check arguments */ if (depth <= 0) { return -1; } /* Initialize stk_frame with first frame of stack */ struct xtensa_backtrace_frame_t stk_frame; 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 ", xtensa_cpu_process_stack_pc(stk_frame.pc), stk_frame.sp); /* Check if first frame is valid */ bool corrupted = !(xtensa_stack_ptr_is_sane(stk_frame.sp) && (xtensa_ptr_executable((void *) xtensa_cpu_process_stack_pc(stk_frame.pc)) || /* Ignore the first corrupted PC in case of InstrFetchProhibited */ cause == EXCCAUSE_INSTR_PROHIBITED)); while (depth-- > 0 && stk_frame.next_pc != 0 && !corrupted) { /* Get previous stack frame */ if (!xtensa_backtrace_get_next_frame(&stk_frame)) { corrupted = true; } printk("0x%08x:0x%08x ", 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; }