1 /*
2 * Copyright (c) 2020 Espressif Systems (Shanghai) Co., Ltd.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include "xtensa/corebits.h"
8 #include "xtensa_backtrace.h"
9 #include <zephyr/sys/printk.h>
10 #if defined(CONFIG_SOC_SERIES_ESP32)
11 #include "soc/soc_memory_layout.h"
12 #elif defined(CONFIG_SOC_FAMILY_INTEL_ADSP)
13 #include "debug_helpers.h"
14 #elif defined(CONFIG_SOC_XTENSA_DC233C)
15 #include "backtrace_helpers.h"
16 #endif
17 static int mask, cause;
18
z_xtensa_cpu_process_stack_pc(uint32_t pc)19 static inline uint32_t z_xtensa_cpu_process_stack_pc(uint32_t pc)
20 {
21 if (pc & 0x80000000) {
22 /* Top two bits of a0 (return address) specify window increment.
23 * Overwrite to map to address space.
24 */
25 if (cause != EXCCAUSE_INSTR_PROHIBITED) {
26 pc = (pc & 0x3fffffff) | mask;
27 } else {
28 pc = (pc & 0x3fffffff) | 0x40000000;
29 }
30 }
31 /* Minus 3 to get PC of previous instruction
32 * (i.e. instruction executed before return address)
33 */
34 return pc - 3;
35 }
36
z_xtensa_stack_ptr_is_sane(uint32_t sp)37 static inline bool z_xtensa_stack_ptr_is_sane(uint32_t sp)
38 {
39 #if defined(CONFIG_SOC_SERIES_ESP32)
40 return esp_stack_ptr_is_sane(sp);
41 #elif defined(CONFIG_SOC_FAMILY_INTEL_ADSP)
42 return intel_adsp_ptr_is_sane(sp);
43 #elif defined(CONFIG_SOC_XTENSA_DC233C)
44 return xtensa_dc233c_stack_ptr_is_sane(sp);
45 #else
46 #warning "z_xtensa_stack_ptr_is_sane is not defined for this platform"
47 #endif
48 }
49
z_xtensa_ptr_executable(const void * p)50 static inline bool z_xtensa_ptr_executable(const void *p)
51 {
52 #if defined(CONFIG_SOC_SERIES_ESP32)
53 return esp_ptr_executable(p);
54 #elif defined(CONFIG_SOC_FAMILY_INTEL_ADSP)
55 return intel_adsp_ptr_executable(p);
56 #elif defined(CONFIG_SOC_XTENSA_DC233C)
57 return xtensa_dc233c_ptr_executable(p);
58 #else
59 #warning "z_xtensa_ptr_executable is not defined for this platform"
60 #endif
61 }
62
z_xtensa_backtrace_get_next_frame(struct z_xtensa_backtrace_frame_t * frame)63 bool z_xtensa_backtrace_get_next_frame(struct z_xtensa_backtrace_frame_t *frame)
64 {
65 /* Use frame(i-1)'s BS area located below frame(i)'s
66 * sp to get frame(i-1)'s sp and frame(i-2)'s pc
67 */
68
69 /* Base save area consists of 4 words under SP */
70 char *base_save = (char *)frame->sp;
71
72 frame->pc = frame->next_pc;
73 /* If next_pc = 0, indicates frame(i-1) is the last
74 * frame on the stack
75 */
76 frame->next_pc = *((uint32_t *)(base_save - 16));
77 frame->sp = *((uint32_t *)(base_save - 12));
78
79 /* Return true if both sp and pc of frame(i-1) are sane,
80 * false otherwise
81 */
82 return (z_xtensa_stack_ptr_is_sane(frame->sp) &&
83 z_xtensa_ptr_executable((void *)
84 z_xtensa_cpu_process_stack_pc(frame->pc)));
85 }
86
z_xtensa_backtrace_print(int depth,int * interrupted_stack)87 int z_xtensa_backtrace_print(int depth, int *interrupted_stack)
88 {
89 /* Check arguments */
90 if (depth <= 0) {
91 return -1;
92 }
93
94 /* Initialize stk_frame with first frame of stack */
95 struct z_xtensa_backtrace_frame_t stk_frame;
96
97 z_xtensa_backtrace_get_start(&(stk_frame.pc), &(stk_frame.sp),
98 &(stk_frame.next_pc), interrupted_stack);
99 __asm__ volatile("l32i a4, a3, 0");
100 __asm__ volatile("l32i a4, a4, 4");
101 __asm__ volatile("mov %0, a4" : "=r"(cause));
102 if (cause != EXCCAUSE_INSTR_PROHIBITED) {
103 mask = stk_frame.pc & 0xc0000000;
104 }
105 printk("\r\n\r\nBacktrace:");
106 printk("0x%08x:0x%08x ",
107 z_xtensa_cpu_process_stack_pc(stk_frame.pc),
108 stk_frame.sp);
109
110 /* Check if first frame is valid */
111 bool corrupted = !(z_xtensa_stack_ptr_is_sane(stk_frame.sp) &&
112 (z_xtensa_ptr_executable((void *)
113 z_xtensa_cpu_process_stack_pc(stk_frame.pc)) ||
114 /* Ignore the first corrupted PC in case of InstrFetchProhibited */
115 cause == EXCCAUSE_INSTR_PROHIBITED));
116
117 while (depth-- > 0 && stk_frame.next_pc != 0 && !corrupted) {
118 /* Get previous stack frame */
119 if (!z_xtensa_backtrace_get_next_frame(&stk_frame)) {
120 corrupted = true;
121 }
122 printk("0x%08x:0x%08x ", z_xtensa_cpu_process_stack_pc(stk_frame.pc), stk_frame.sp);
123 }
124
125 /* Print backtrace termination marker */
126 int ret = 0;
127
128 if (corrupted) {
129 printk(" |<-CORRUPTED");
130 ret = -1;
131 } else if (stk_frame.next_pc != 0) { /* Backtrace continues */
132 printk(" |<-CONTINUES");
133 }
134 printk("\r\n\r\n");
135 return ret;
136 }
137