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 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | /* * Copyright (c) 2020 Espressif Systems (Shanghai) Co., Ltd. * * SPDX-License-Identifier: Apache-2.0 */ #include "xtensa/corebits.h" #include "xtensa_backtrace.h" #include <zephyr/sys/printk.h> #if defined(CONFIG_SOC_SERIES_ESP32) #include <esp_memory_utils.h> #elif defined(CONFIG_SOC_FAMILY_INTEL_ADSP) #include "debug_helpers.h" #elif defined(CONFIG_SOC_XTENSA_DC233C) #include "backtrace_helpers.h" #endif #include <xtensa_asm2_context.h> #include <xtensa_stack.h> 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) { bool valid; #if defined(CONFIG_SOC_SERIES_ESP32) valid = esp_stack_ptr_is_sane(sp); #elif defined(CONFIG_SOC_FAMILY_INTEL_ADSP) valid = intel_adsp_ptr_is_sane(sp); #else /* Platform does not have additional requirements on * whether stack pointer is valid. So use the generic * test below. */ valid = true; #endif if (valid) { valid = !xtensa_is_outside_stack_bounds(sp, 0, UINT32_MAX); } return valid; } 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) { /* Do not continue backtrace when we encounter an invalid stack * frame pointer. */ if (xtensa_is_outside_stack_bounds((uintptr_t)frame->sp, 0, UINT32_MAX)) { return false; } /* 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; } _xtensa_irq_stack_frame_raw_t *frame = (void *)interrupted_stack; _xtensa_irq_bsa_t *bsa; /* Don't dump stack if the stack pointer is invalid as * any frame elements obtained via de-referencing the * frame pointer are probably also invalid. Or worse, * cause another access violation. */ if (!xtensa_is_frame_pointer_valid(frame)) { return -1; } bsa = frame->ptr_to_bsa; cause = bsa->exccause; /* 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); 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; } |