Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | /* * 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; } |