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 <esp_memory_utils.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 
18 #include <xtensa_asm2_context.h>
19 #include <xtensa_stack.h>
20 
21 static int mask, cause;
22 
xtensa_cpu_process_stack_pc(uint32_t pc)23 static inline uint32_t xtensa_cpu_process_stack_pc(uint32_t pc)
24 {
25 	if (pc & 0x80000000) {
26 		/* Top two bits of a0 (return address) specify window increment.
27 		 * Overwrite to map to address space.
28 		 */
29 		if (cause != EXCCAUSE_INSTR_PROHIBITED) {
30 			pc = (pc & 0x3fffffff) | mask;
31 		} else {
32 			pc = (pc & 0x3fffffff) | 0x40000000;
33 		}
34 	}
35 	/* Minus 3 to get PC of previous instruction
36 	 * (i.e. instruction executed before return address)
37 	 */
38 	return pc - 3;
39 }
40 
xtensa_stack_ptr_is_sane(uint32_t sp)41 static inline bool xtensa_stack_ptr_is_sane(uint32_t sp)
42 {
43 	bool valid;
44 
45 #if defined(CONFIG_SOC_SERIES_ESP32)
46 	valid = esp_stack_ptr_is_sane(sp);
47 #elif defined(CONFIG_SOC_FAMILY_INTEL_ADSP)
48 	valid = intel_adsp_ptr_is_sane(sp);
49 #else
50 	/* Platform does not have additional requirements on
51 	 * whether stack pointer is valid. So use the generic
52 	 * test below.
53 	 */
54 	valid = true;
55 #endif
56 
57 	if (valid) {
58 		valid = !xtensa_is_outside_stack_bounds(sp, 0, UINT32_MAX);
59 	}
60 
61 	return valid;
62 }
63 
xtensa_ptr_executable(const void * p)64 static inline bool xtensa_ptr_executable(const void *p)
65 {
66 #if defined(CONFIG_SOC_SERIES_ESP32)
67 	return esp_ptr_executable(p);
68 #elif defined(CONFIG_SOC_FAMILY_INTEL_ADSP)
69 	return intel_adsp_ptr_executable(p);
70 #elif defined(CONFIG_SOC_XTENSA_DC233C)
71 	return xtensa_dc233c_ptr_executable(p);
72 #else
73 #warning "xtensa_ptr_executable is not defined for this platform"
74 #endif
75 }
76 
xtensa_backtrace_get_next_frame(struct xtensa_backtrace_frame_t * frame)77 bool xtensa_backtrace_get_next_frame(struct xtensa_backtrace_frame_t *frame)
78 {
79 	/* Do not continue backtrace when we encounter an invalid stack
80 	 * frame pointer.
81 	 */
82 	if (xtensa_is_outside_stack_bounds((uintptr_t)frame->sp, 0, UINT32_MAX)) {
83 		return false;
84 	}
85 
86 	/* Use frame(i-1)'s BS area located below frame(i)'s
87 	 * sp to get frame(i-1)'s sp and frame(i-2)'s pc
88 	 */
89 
90 	/* Base save area consists of 4 words under SP */
91 	char *base_save = (char *)frame->sp;
92 
93 	frame->pc = frame->next_pc;
94 	/* If next_pc = 0, indicates frame(i-1) is the last
95 	 * frame on the stack
96 	 */
97 	frame->next_pc = *((uint32_t *)(base_save - 16));
98 	frame->sp =  *((uint32_t *)(base_save - 12));
99 
100 	/* Return true if both sp and pc of frame(i-1) are sane,
101 	 * false otherwise
102 	 */
103 	return (xtensa_stack_ptr_is_sane(frame->sp) &&
104 			xtensa_ptr_executable((void *)
105 				xtensa_cpu_process_stack_pc(frame->pc)));
106 }
107 
xtensa_backtrace_print(int depth,int * interrupted_stack)108 int xtensa_backtrace_print(int depth, int *interrupted_stack)
109 {
110 	/* Check arguments */
111 	if (depth <= 0) {
112 		return -1;
113 	}
114 
115 	_xtensa_irq_stack_frame_raw_t *frame = (void *)interrupted_stack;
116 	_xtensa_irq_bsa_t *bsa;
117 
118 	/* Don't dump stack if the stack pointer is invalid as
119 	 * any frame elements obtained via de-referencing the
120 	 * frame pointer are probably also invalid. Or worse,
121 	 * cause another access violation.
122 	 */
123 	if (!xtensa_is_frame_pointer_valid(frame)) {
124 		return -1;
125 	}
126 
127 	bsa = frame->ptr_to_bsa;
128 	cause = bsa->exccause;
129 
130 	/* Initialize stk_frame with first frame of stack */
131 	struct xtensa_backtrace_frame_t stk_frame;
132 
133 	xtensa_backtrace_get_start(&(stk_frame.pc), &(stk_frame.sp),
134 			&(stk_frame.next_pc), interrupted_stack);
135 
136 	if (cause != EXCCAUSE_INSTR_PROHIBITED) {
137 		mask = stk_frame.pc & 0xc0000000;
138 	}
139 	printk("\r\n\r\nBacktrace:");
140 	printk("0x%08x:0x%08x ",
141 			xtensa_cpu_process_stack_pc(stk_frame.pc),
142 			stk_frame.sp);
143 
144 	/* Check if first frame is valid */
145 	bool corrupted = !(xtensa_stack_ptr_is_sane(stk_frame.sp) &&
146 				(xtensa_ptr_executable((void *)
147 				xtensa_cpu_process_stack_pc(stk_frame.pc)) ||
148 	/* Ignore the first corrupted PC in case of InstrFetchProhibited */
149 				cause == EXCCAUSE_INSTR_PROHIBITED));
150 
151 	while (depth-- > 0 && stk_frame.next_pc != 0 && !corrupted) {
152 		/* Get previous stack frame */
153 		if (!xtensa_backtrace_get_next_frame(&stk_frame)) {
154 			corrupted = true;
155 		}
156 		printk("0x%08x:0x%08x ", xtensa_cpu_process_stack_pc(stk_frame.pc), stk_frame.sp);
157 	}
158 
159 	/* Print backtrace termination marker */
160 	int ret = 0;
161 
162 	if (corrupted) {
163 		printk(" |<-CORRUPTED");
164 		ret =  -1;
165 	} else if (stk_frame.next_pc != 0) {    /* Backtrace continues */
166 		printk(" |<-CONTINUES");
167 	}
168 	printk("\r\n\r\n");
169 	return ret;
170 }
171