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