1 /*
2  * Copyright (c) 2021 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <string.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/debug/coredump.h>
10 #include <xtensa_asm2_context.h>
11 #include <zephyr/offsets.h>
12 
13 #define ARCH_HDR_VER			1
14 #define XTENSA_BLOCK_HDR_VER		2
15 
16 enum xtensa_soc_code {
17 	XTENSA_SOC_UNKNOWN = 0,
18 	XTENSA_SOC_SAMPLE_CONTROLLER,
19 	XTENSA_SOC_ESP32,
20 	XTENSA_SOC_INTEL_ADSP,
21 	XTENSA_SOC_ESP32S2,
22 	XTENSA_SOC_ESP32S3,
23 	XTENSA_SOC_DC233C,
24 };
25 
26 struct xtensa_arch_block {
27 	/* Each Xtensa SOC can omit registers (e.g. loop
28 	 * registers) or assign different index numbers
29 	 * in xtensa-config.c. GDB identifies registers
30 	 * based on these indices
31 	 *
32 	 * (This must be the first field or the GDB server
33 	 * won't be able to unpack the struct while parsing)
34 	 */
35 	uint8_t		soc;
36 
37 	/* Future versions of Xtensa coredump may expand
38 	 * minimum set of registers
39 	 *
40 	 * (This should stay the second field for the same
41 	 * reason as the first once we have more versions)
42 	 */
43 	uint16_t	version;
44 
45 	uint8_t		toolchain;
46 
47 	struct {
48 		/* Minimum set shown by GDB 'info registers',
49 		 * skipping user-defined register EXPSTATE
50 		 *
51 		 * WARNING: IF YOU CHANGE THE ORDER OF THE REGISTERS,
52 		 * YOU MUST UPDATE THE ORDER OF THE REGISTERS IN
53 		 * EACH OF THE XtensaSoc_ RegNum enums IN
54 		 * scripts/coredump/gdbstubs/arch/xtensa.py TO MATCH.
55 		 * See xtensa.py's map_register function for details
56 		 */
57 		uint32_t	pc;
58 		uint32_t	exccause;
59 		uint32_t	excvaddr;
60 		uint32_t	sar;
61 		uint32_t	ps;
62 #if XCHAL_HAVE_S32C1I
63 		uint32_t	scompare1;
64 #endif
65 		uint32_t	a0;
66 		uint32_t	a1;
67 		uint32_t	a2;
68 		uint32_t	a3;
69 		uint32_t	a4;
70 		uint32_t	a5;
71 		uint32_t	a6;
72 		uint32_t	a7;
73 		uint32_t	a8;
74 		uint32_t	a9;
75 		uint32_t	a10;
76 		uint32_t	a11;
77 		uint32_t	a12;
78 		uint32_t	a13;
79 		uint32_t	a14;
80 		uint32_t	a15;
81 #if XCHAL_HAVE_LOOPS
82 		uint32_t	lbeg;
83 		uint32_t	lend;
84 		uint32_t	lcount;
85 #endif
86 	} r;
87 } __packed;
88 
89 /*
90  * This might be too large for stack space if defined
91  * inside function. So do it here.
92  */
93 static struct xtensa_arch_block arch_blk;
94 
arch_coredump_info_dump(const struct arch_esf * esf)95 void arch_coredump_info_dump(const struct arch_esf *esf)
96 {
97 	struct coredump_arch_hdr_t hdr = {
98 		.id = COREDUMP_ARCH_HDR_ID,
99 		.hdr_version = ARCH_HDR_VER,
100 		.num_bytes = sizeof(arch_blk),
101 	};
102 
103 	/* Nothing to process */
104 	if (esf == NULL) {
105 		return;
106 	}
107 
108 	(void)memset(&arch_blk, 0, sizeof(arch_blk));
109 
110 	arch_blk.version = XTENSA_BLOCK_HDR_VER;
111 
112 	#if CONFIG_SOC_XTENSA_SAMPLE_CONTROLLER
113 		arch_blk.soc = XTENSA_SOC_SAMPLE_CONTROLLER;
114 	#elif CONFIG_SOC_FAMILY_INTEL_ADSP
115 		arch_blk.soc = XTENSA_SOC_INTEL_ADSP;
116 	#elif CONFIG_SOC_SERIES_ESP32
117 		arch_blk.soc = XTENSA_SOC_ESP32;
118 	#elif CONFIG_SOC_SERIES_ESP32S2
119 		arch_blk.soc = XTENSA_SOC_ESP32S2;
120 	#elif CONFIG_SOC_SERIES_ESP32S3
121 		arch_blk.soc = XTENSA_SOC_ESP32S3;
122 	#elif CONFIG_SOC_XTENSA_DC233C
123 		arch_blk.soc = XTENSA_SOC_DC233C;
124 	#else
125 		arch_blk.soc = XTENSA_SOC_UNKNOWN;
126 	#endif
127 
128 	/* Set in top-level CMakeLists.txt for use with Xtensa coredump */
129 	arch_blk.toolchain = XTENSA_TOOLCHAIN_VARIANT;
130 
131 	__asm__ volatile("rsr.exccause %0" : "=r"(arch_blk.r.exccause));
132 
133 	_xtensa_irq_stack_frame_raw_t *frame = (void *)esf;
134 	_xtensa_irq_bsa_t *bsa = frame->ptr_to_bsa;
135 	uintptr_t num_high_regs;
136 	int regs_blk_remaining;
137 
138 	/* Calculate number of high registers. */
139 	num_high_regs = (uint8_t *)bsa - (uint8_t *)frame + sizeof(void *);
140 	num_high_regs /= sizeof(uintptr_t);
141 
142 	/* And high registers are always comes in 4 in a block. */
143 	regs_blk_remaining = (int)num_high_regs / 4;
144 
145 	arch_blk.r.pc = bsa->pc;
146 	__asm__ volatile("rsr.excvaddr %0" : "=r"(arch_blk.r.excvaddr));
147 	arch_blk.r.ps = bsa->ps;
148 #if XCHAL_HAVE_S32C1I
149 	arch_blk.r.scompare1 = bsa->scompare1;
150 #endif
151 	arch_blk.r.sar = bsa->sar;
152 	arch_blk.r.a0 = bsa->a0;
153 	arch_blk.r.a1 = (uint32_t)((char *)bsa) + sizeof(*bsa);
154 	arch_blk.r.a2 = bsa->a2;
155 	arch_blk.r.a3 = bsa->a3;
156 	if (regs_blk_remaining > 0) {
157 		regs_blk_remaining--;
158 
159 		arch_blk.r.a4 = frame->blks[regs_blk_remaining].r0;
160 		arch_blk.r.a5 = frame->blks[regs_blk_remaining].r1;
161 		arch_blk.r.a6 = frame->blks[regs_blk_remaining].r2;
162 		arch_blk.r.a7 = frame->blks[regs_blk_remaining].r3;
163 	}
164 	if (regs_blk_remaining > 0) {
165 		regs_blk_remaining--;
166 
167 		arch_blk.r.a8 = frame->blks[regs_blk_remaining].r0;
168 		arch_blk.r.a9 = frame->blks[regs_blk_remaining].r1;
169 		arch_blk.r.a10 = frame->blks[regs_blk_remaining].r2;
170 		arch_blk.r.a11 = frame->blks[regs_blk_remaining].r3;
171 	}
172 	if (regs_blk_remaining > 0) {
173 		arch_blk.r.a12 = frame->blks[regs_blk_remaining].r0;
174 		arch_blk.r.a13 = frame->blks[regs_blk_remaining].r1;
175 		arch_blk.r.a14 = frame->blks[regs_blk_remaining].r2;
176 		arch_blk.r.a15 = frame->blks[regs_blk_remaining].r3;
177 	}
178 	#if XCHAL_HAVE_LOOPS
179 	arch_blk.r.lbeg = bsa->lbeg;
180 	arch_blk.r.lend = bsa->lend;
181 	arch_blk.r.lcount = bsa->lcount;
182 	#endif
183 
184 	/* Send for output */
185 	coredump_buffer_output((uint8_t *)&hdr, sizeof(hdr));
186 	coredump_buffer_output((uint8_t *)&arch_blk, sizeof(arch_blk));
187 }
188 
arch_coredump_tgt_code_get(void)189 uint16_t arch_coredump_tgt_code_get(void)
190 {
191 	return COREDUMP_TGT_XTENSA;
192 }
193 
194 #if defined(CONFIG_DEBUG_COREDUMP_DUMP_THREAD_PRIV_STACK)
arch_coredump_priv_stack_dump(struct k_thread * thread)195 void arch_coredump_priv_stack_dump(struct k_thread *thread)
196 {
197 	struct xtensa_thread_stack_header *hdr_stack_obj;
198 	uintptr_t start_addr, end_addr;
199 
200 	hdr_stack_obj = (struct xtensa_thread_stack_header *)thread->stack_obj;
201 
202 	start_addr = (uintptr_t)&hdr_stack_obj->privilege_stack[0];
203 	end_addr = start_addr + sizeof(hdr_stack_obj->privilege_stack);
204 
205 	coredump_memory_dump(start_addr, end_addr);
206 }
207 #endif /* CONFIG_DEBUG_COREDUMP_DUMP_THREAD_PRIV_STACK */
208