xref: /rk3399_rockchip-uboot/arch/arm/lib/stacktrace.c (revision b8dc613cbc483a8abfcf4203e4fa0e18f60b1d27)
1d0df954bSJoseph Chen // SPDX-License-Identifier: BSD-2-Clause
2d0df954bSJoseph Chen /*
3d0df954bSJoseph Chen  * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
4d0df954bSJoseph Chen  *
5d0df954bSJoseph Chen  * This file is taken and modified from the OP-TEE project.
6d0df954bSJoseph Chen  */
7d0df954bSJoseph Chen 
8d0df954bSJoseph Chen #include <common.h>
9d0df954bSJoseph Chen #include <stacktrace.h>
10d0df954bSJoseph Chen #include <asm/sections.h>
11d0df954bSJoseph Chen 
12d0df954bSJoseph Chen DECLARE_GLOBAL_DATA_PTR;
13d0df954bSJoseph Chen 
14d0df954bSJoseph Chen /* The register names */
15d0df954bSJoseph Chen #define	FP	11
16d0df954bSJoseph Chen #define	SP	13
17d0df954bSJoseph Chen #define	LR	14
18d0df954bSJoseph Chen #define	PC	15
19d0df954bSJoseph Chen 
20d0df954bSJoseph Chen /*
21d0df954bSJoseph Chen  * Definitions for the instruction interpreter.
22d0df954bSJoseph Chen  *
23d0df954bSJoseph Chen  * The ARM EABI specifies how to perform the frame unwinding in the
24d0df954bSJoseph Chen  * Exception Handling ABI for the ARM Architecture document. To perform
25d0df954bSJoseph Chen  * the unwind we need to know the initial frame pointer, stack pointer,
26d0df954bSJoseph Chen  * link register and program counter. We then find the entry within the
27d0df954bSJoseph Chen  * index table that points to the function the program counter is within.
28d0df954bSJoseph Chen  * This gives us either a list of three instructions to process, a 31-bit
29d0df954bSJoseph Chen  * relative offset to a table of instructions, or a value telling us
30d0df954bSJoseph Chen  * we can't unwind any further.
31d0df954bSJoseph Chen  *
32d0df954bSJoseph Chen  * When we have the instructions to process we need to decode them
33d0df954bSJoseph Chen  * following table 4 in section 9.3. This describes a collection of bit
34d0df954bSJoseph Chen  * patterns to encode that steps to take to update the stack pointer and
35d0df954bSJoseph Chen  * link register to the correct values at the start of the function.
36d0df954bSJoseph Chen  */
37d0df954bSJoseph Chen 
38d0df954bSJoseph Chen /* A special case when we are unable to unwind past this function */
39d0df954bSJoseph Chen #define	EXIDX_CANTUNWIND	1
40d0df954bSJoseph Chen 
41d0df954bSJoseph Chen /*
42d0df954bSJoseph Chen  * Entry types.
43d0df954bSJoseph Chen  * These are the only entry types that have been seen in the kernel.
44d0df954bSJoseph Chen  */
45d0df954bSJoseph Chen #define	ENTRY_MASK		0xff000000
46d0df954bSJoseph Chen #define	ENTRY_ARM_SU16		0x80000000
47d0df954bSJoseph Chen #define	ENTRY_ARM_LU16		0x81000000
48d0df954bSJoseph Chen 
49d0df954bSJoseph Chen /* Instruction masks. */
50d0df954bSJoseph Chen #define	INSN_VSP_MASK		0xc0
51d0df954bSJoseph Chen #define	INSN_VSP_SIZE_MASK	0x3f
52d0df954bSJoseph Chen #define	INSN_STD_MASK		0xf0
53d0df954bSJoseph Chen #define	INSN_STD_DATA_MASK	0x0f
54d0df954bSJoseph Chen #define	INSN_POP_TYPE_MASK	0x08
55d0df954bSJoseph Chen #define	INSN_POP_COUNT_MASK	0x07
56d0df954bSJoseph Chen #define	INSN_VSP_LARGE_INC_MASK	0xff
57d0df954bSJoseph Chen 
58d0df954bSJoseph Chen /* Instruction definitions */
59d0df954bSJoseph Chen #define	INSN_VSP_INC		0x00
60d0df954bSJoseph Chen #define	INSN_VSP_DEC		0x40
61d0df954bSJoseph Chen #define	INSN_POP_MASKED		0x80
62d0df954bSJoseph Chen #define	INSN_VSP_REG		0x90
63d0df954bSJoseph Chen #define	INSN_POP_COUNT		0xa0
64d0df954bSJoseph Chen #define	INSN_FINISH		0xb0
65d0df954bSJoseph Chen #define	INSN_POP_REGS		0xb1
66d0df954bSJoseph Chen #define	INSN_VSP_LARGE_INC	0xb2
67d0df954bSJoseph Chen 
68d0df954bSJoseph Chen #define SHIFT_U32(v, shift)	((uint32_t)(v) << (shift))
69d0df954bSJoseph Chen 
70d0df954bSJoseph Chen /* The state of the unwind process (32-bit mode) */
71d0df954bSJoseph Chen struct unwind_state_arm32 {
72d0df954bSJoseph Chen 	uint32_t registers[16];
73d0df954bSJoseph Chen 	uint32_t start_pc;
74d0df954bSJoseph Chen 	ulong insn;
75d0df954bSJoseph Chen 	unsigned int entries;
76d0df954bSJoseph Chen 	unsigned int byte;
77d0df954bSJoseph Chen 	uint16_t update_mask;
78d0df954bSJoseph Chen };
79d0df954bSJoseph Chen 
80d0df954bSJoseph Chen /* An item in the exception index table */
81d0df954bSJoseph Chen struct unwind_idx {
82d0df954bSJoseph Chen 	uint32_t offset;
83d0df954bSJoseph Chen 	uint32_t insn;
84d0df954bSJoseph Chen };
85d0df954bSJoseph Chen 
read_pc(void)86d0df954bSJoseph Chen static __always_inline uint32_t read_pc(void)
87d0df954bSJoseph Chen {
88d0df954bSJoseph Chen 	uint32_t val;
89d0df954bSJoseph Chen 
90d0df954bSJoseph Chen 	asm volatile ("adr %0, ." : "=r" (val));
91d0df954bSJoseph Chen 	return val;
92d0df954bSJoseph Chen }
93d0df954bSJoseph Chen 
read_sp(void)94d0df954bSJoseph Chen static __always_inline uint32_t read_sp(void)
95d0df954bSJoseph Chen {
96d0df954bSJoseph Chen 	uint32_t val;
97d0df954bSJoseph Chen 
98d0df954bSJoseph Chen 	asm volatile ("mov %0, sp" : "=r" (val));
99d0df954bSJoseph Chen 	return val;
100d0df954bSJoseph Chen }
101d0df954bSJoseph Chen 
read_lr(void)102d0df954bSJoseph Chen static __always_inline uint32_t read_lr(void)
103d0df954bSJoseph Chen {
104d0df954bSJoseph Chen 	uint32_t val;
105d0df954bSJoseph Chen 
106d0df954bSJoseph Chen 	asm volatile ("mov %0, lr" : "=r" (val));
107d0df954bSJoseph Chen 	return val;
108d0df954bSJoseph Chen }
109d0df954bSJoseph Chen 
read_fp(void)110d0df954bSJoseph Chen static __always_inline uint32_t read_fp(void)
111d0df954bSJoseph Chen {
112d0df954bSJoseph Chen 	uint32_t val;
113d0df954bSJoseph Chen 
114d0df954bSJoseph Chen 	asm volatile ("mov %0, fp" : "=r" (val));
115d0df954bSJoseph Chen 	return val;
116d0df954bSJoseph Chen }
117d0df954bSJoseph Chen 
read_r7(void)118d0df954bSJoseph Chen static __always_inline uint32_t read_r7(void)
119d0df954bSJoseph Chen {
120d0df954bSJoseph Chen 	uint32_t val;
121d0df954bSJoseph Chen 
122d0df954bSJoseph Chen 	asm volatile ("mov %0, r7" : "=r" (val));
123d0df954bSJoseph Chen 	return val;
124d0df954bSJoseph Chen }
125d0df954bSJoseph Chen 
copy_in(void * dst,const void * src,size_t n,bool kernel_data)126d0df954bSJoseph Chen static bool copy_in(void *dst, const void *src, size_t n, bool kernel_data)
127d0df954bSJoseph Chen {
128d0df954bSJoseph Chen 	if (!kernel_data)
129d0df954bSJoseph Chen 		return false;
130d0df954bSJoseph Chen 
131d0df954bSJoseph Chen 	memcpy(dst, src, n);
132d0df954bSJoseph Chen 
133d0df954bSJoseph Chen 	return true;
134d0df954bSJoseph Chen }
135d0df954bSJoseph Chen 
136d0df954bSJoseph Chen /* Expand a 31-bit signed value to a 32-bit signed value */
expand_prel31(uint32_t prel31)137d0df954bSJoseph Chen static int32_t expand_prel31(uint32_t prel31)
138d0df954bSJoseph Chen {
139d0df954bSJoseph Chen 	return prel31 | SHIFT_U32(prel31 & BIT(30), 1);
140d0df954bSJoseph Chen }
141d0df954bSJoseph Chen 
142d0df954bSJoseph Chen /*
143d0df954bSJoseph Chen  * Perform a binary search of the index table to find the function
144d0df954bSJoseph Chen  * with the largest address that doesn't exceed addr.
145d0df954bSJoseph Chen  */
find_index(uint32_t addr,ulong exidx,size_t exidx_sz)146d0df954bSJoseph Chen static struct unwind_idx *find_index(uint32_t addr, ulong exidx,
147d0df954bSJoseph Chen 				     size_t exidx_sz)
148d0df954bSJoseph Chen {
149d0df954bSJoseph Chen 	ulong idx_start, idx_end;
150d0df954bSJoseph Chen 	unsigned int min, mid, max;
151d0df954bSJoseph Chen 	struct unwind_idx *start;
152d0df954bSJoseph Chen 	struct unwind_idx *item;
153d0df954bSJoseph Chen 	int32_t prel31_addr;
154d0df954bSJoseph Chen 	ulong func_addr;
155d0df954bSJoseph Chen 
156d0df954bSJoseph Chen 	start = (struct unwind_idx *)exidx;
157d0df954bSJoseph Chen 	idx_start = exidx;
158d0df954bSJoseph Chen 	idx_end = exidx + exidx_sz;
159d0df954bSJoseph Chen 
160d0df954bSJoseph Chen 	min = 0;
161d0df954bSJoseph Chen 	max = (idx_end - idx_start) / sizeof(struct unwind_idx);
162d0df954bSJoseph Chen 
163d0df954bSJoseph Chen 	while (min != max) {
164d0df954bSJoseph Chen 		mid = min + (max - min + 1) / 2;
165d0df954bSJoseph Chen 
166d0df954bSJoseph Chen 		item = &start[mid];
167d0df954bSJoseph Chen 
168d0df954bSJoseph Chen 		prel31_addr = expand_prel31(item->offset);
169d0df954bSJoseph Chen 		func_addr = (ulong)&item->offset + prel31_addr;
170d0df954bSJoseph Chen 
171d0df954bSJoseph Chen 		if (func_addr <= addr) {
172d0df954bSJoseph Chen 			min = mid;
173d0df954bSJoseph Chen 		} else {
174d0df954bSJoseph Chen 			max = mid - 1;
175d0df954bSJoseph Chen 		}
176d0df954bSJoseph Chen 	}
177d0df954bSJoseph Chen 
178d0df954bSJoseph Chen 	return &start[min];
179d0df954bSJoseph Chen }
180d0df954bSJoseph Chen 
181d0df954bSJoseph Chen /* Reads the next byte from the instruction list */
unwind_exec_read_byte(struct unwind_state_arm32 * state,uint32_t * ret_insn,bool kernel_stack)182d0df954bSJoseph Chen static bool unwind_exec_read_byte(struct unwind_state_arm32 *state,
183d0df954bSJoseph Chen 				  uint32_t *ret_insn, bool kernel_stack)
184d0df954bSJoseph Chen {
185d0df954bSJoseph Chen 	uint32_t insn;
186d0df954bSJoseph Chen 
187d0df954bSJoseph Chen 	if (!copy_in(&insn, (void *)state->insn, sizeof(insn), kernel_stack))
188d0df954bSJoseph Chen 		return false;
189d0df954bSJoseph Chen 
190d0df954bSJoseph Chen 	/* Read the unwind instruction */
191d0df954bSJoseph Chen 	*ret_insn = (insn >> (state->byte * 8)) & 0xff;
192d0df954bSJoseph Chen 
193d0df954bSJoseph Chen 	/* Update the location of the next instruction */
194d0df954bSJoseph Chen 	if (state->byte == 0) {
195d0df954bSJoseph Chen 		state->byte = 3;
196d0df954bSJoseph Chen 		state->insn += sizeof(uint32_t);
197d0df954bSJoseph Chen 		state->entries--;
198d0df954bSJoseph Chen 	} else {
199d0df954bSJoseph Chen 		state->byte--;
200d0df954bSJoseph Chen 	}
201d0df954bSJoseph Chen 
202d0df954bSJoseph Chen 	return true;
203d0df954bSJoseph Chen }
204d0df954bSJoseph Chen 
pop_vsp(uint32_t * reg,ulong * vsp,bool kernel_stack,ulong stack,size_t stack_size)205d0df954bSJoseph Chen static bool pop_vsp(uint32_t *reg, ulong *vsp, bool kernel_stack,
206d0df954bSJoseph Chen 		    ulong stack, size_t stack_size)
207d0df954bSJoseph Chen {
208d0df954bSJoseph Chen 	if (*vsp > gd->start_addr_sp ||
209d0df954bSJoseph Chen 	    *vsp < gd->start_addr_sp - CONFIG_SYS_STACK_SIZE)
210d0df954bSJoseph Chen 		return false;
211d0df954bSJoseph Chen 
212d0df954bSJoseph Chen 	if (!copy_in(reg, (void *)*vsp, sizeof(*reg), kernel_stack))
213d0df954bSJoseph Chen 		return false;
214d0df954bSJoseph Chen 
215d0df954bSJoseph Chen 	(*vsp) += sizeof(*reg);
216d0df954bSJoseph Chen 
217d0df954bSJoseph Chen 	return true;
218d0df954bSJoseph Chen }
219d0df954bSJoseph Chen 
220d0df954bSJoseph Chen /* Executes the next instruction on the list */
unwind_exec_insn(struct unwind_state_arm32 * state,bool kernel_stack,ulong stack,size_t stack_size)221d0df954bSJoseph Chen static bool unwind_exec_insn(struct unwind_state_arm32 *state,
222d0df954bSJoseph Chen 			     bool kernel_stack, ulong stack,
223d0df954bSJoseph Chen 			     size_t stack_size)
224d0df954bSJoseph Chen {
225d0df954bSJoseph Chen 	uint32_t insn;
226d0df954bSJoseph Chen 	ulong vsp = state->registers[SP];
227d0df954bSJoseph Chen 	int update_vsp = 0;
228d0df954bSJoseph Chen 
229d0df954bSJoseph Chen 	/* Read the next instruction */
230d0df954bSJoseph Chen 	if (!unwind_exec_read_byte(state, &insn, kernel_stack))
231d0df954bSJoseph Chen 		return false;
232d0df954bSJoseph Chen 
233d0df954bSJoseph Chen 	if ((insn & INSN_VSP_MASK) == INSN_VSP_INC) {
234d0df954bSJoseph Chen 		state->registers[SP] += ((insn & INSN_VSP_SIZE_MASK) << 2) + 4;
235d0df954bSJoseph Chen 
236d0df954bSJoseph Chen 	} else if ((insn & INSN_VSP_MASK) == INSN_VSP_DEC) {
237d0df954bSJoseph Chen 		state->registers[SP] -= ((insn & INSN_VSP_SIZE_MASK) << 2) + 4;
238d0df954bSJoseph Chen 
239d0df954bSJoseph Chen 	} else if ((insn & INSN_STD_MASK) == INSN_POP_MASKED) {
240d0df954bSJoseph Chen 		uint32_t mask;
241d0df954bSJoseph Chen 		unsigned int reg;
242d0df954bSJoseph Chen 
243d0df954bSJoseph Chen 		/* Load the mask */
244d0df954bSJoseph Chen 		if (!unwind_exec_read_byte(state, &mask, kernel_stack))
245d0df954bSJoseph Chen 			return false;
246d0df954bSJoseph Chen 		mask |= (insn & INSN_STD_DATA_MASK) << 8;
247d0df954bSJoseph Chen 
248d0df954bSJoseph Chen 		/* We have a refuse to unwind instruction */
249d0df954bSJoseph Chen 		if (mask == 0)
250d0df954bSJoseph Chen 			return false;
251d0df954bSJoseph Chen 
252d0df954bSJoseph Chen 		/* Update SP */
253d0df954bSJoseph Chen 		update_vsp = 1;
254d0df954bSJoseph Chen 
255d0df954bSJoseph Chen 		/* Load the registers */
256d0df954bSJoseph Chen 		for (reg = 4; mask && reg < 16; mask >>= 1, reg++) {
257d0df954bSJoseph Chen 			if (mask & 1) {
258d0df954bSJoseph Chen 				if (!pop_vsp(&state->registers[reg], &vsp,
259d0df954bSJoseph Chen 					     kernel_stack, stack, stack_size))
260d0df954bSJoseph Chen 					return false;
261d0df954bSJoseph Chen 				state->update_mask |= 1 << reg;
262d0df954bSJoseph Chen 
263d0df954bSJoseph Chen 				/* If we have updated SP kep its value */
264d0df954bSJoseph Chen 				if (reg == SP)
265d0df954bSJoseph Chen 					update_vsp = 0;
266d0df954bSJoseph Chen 			}
267d0df954bSJoseph Chen 		}
268d0df954bSJoseph Chen 
269d0df954bSJoseph Chen 	} else if ((insn & INSN_STD_MASK) == INSN_VSP_REG &&
270d0df954bSJoseph Chen 	    ((insn & INSN_STD_DATA_MASK) != 13) &&
271d0df954bSJoseph Chen 	    ((insn & INSN_STD_DATA_MASK) != 15)) {
272d0df954bSJoseph Chen 		/* sp = register */
273d0df954bSJoseph Chen 		state->registers[SP] =
274d0df954bSJoseph Chen 		    state->registers[insn & INSN_STD_DATA_MASK];
275d0df954bSJoseph Chen 
276d0df954bSJoseph Chen 	} else if ((insn & INSN_STD_MASK) == INSN_POP_COUNT) {
277d0df954bSJoseph Chen 		unsigned int count, reg;
278d0df954bSJoseph Chen 
279d0df954bSJoseph Chen 		/* Read how many registers to load */
280d0df954bSJoseph Chen 		count = insn & INSN_POP_COUNT_MASK;
281d0df954bSJoseph Chen 
282d0df954bSJoseph Chen 		/* Update sp */
283d0df954bSJoseph Chen 		update_vsp = 1;
284d0df954bSJoseph Chen 
285d0df954bSJoseph Chen 		/* Pop the registers */
286d0df954bSJoseph Chen 		for (reg = 4; reg <= 4 + count; reg++) {
287d0df954bSJoseph Chen 			if (!pop_vsp(&state->registers[reg], &vsp,
288d0df954bSJoseph Chen 				     kernel_stack, stack, stack_size))
289d0df954bSJoseph Chen 				return false;
290d0df954bSJoseph Chen 			state->update_mask |= 1 << reg;
291d0df954bSJoseph Chen 		}
292d0df954bSJoseph Chen 
293d0df954bSJoseph Chen 		/* Check if we are in the pop r14 version */
294d0df954bSJoseph Chen 		if ((insn & INSN_POP_TYPE_MASK) != 0) {
295d0df954bSJoseph Chen 			if (!pop_vsp(&state->registers[14], &vsp, kernel_stack,
296d0df954bSJoseph Chen 				     stack, stack_size))
297d0df954bSJoseph Chen 				return false;
298d0df954bSJoseph Chen 		}
299d0df954bSJoseph Chen 
300d0df954bSJoseph Chen 	} else if (insn == INSN_FINISH) {
301d0df954bSJoseph Chen 		/* Stop processing */
302d0df954bSJoseph Chen 		state->entries = 0;
303d0df954bSJoseph Chen 
304d0df954bSJoseph Chen 	} else if (insn == INSN_POP_REGS) {
305d0df954bSJoseph Chen 		uint32_t mask;
306d0df954bSJoseph Chen 		unsigned int reg;
307d0df954bSJoseph Chen 
308d0df954bSJoseph Chen 		if (!unwind_exec_read_byte(state, &mask, kernel_stack))
309d0df954bSJoseph Chen 			return false;
310d0df954bSJoseph Chen 		if (mask == 0 || (mask & 0xf0) != 0)
311d0df954bSJoseph Chen 			return false;
312d0df954bSJoseph Chen 
313d0df954bSJoseph Chen 		/* Update SP */
314d0df954bSJoseph Chen 		update_vsp = 1;
315d0df954bSJoseph Chen 
316d0df954bSJoseph Chen 		/* Load the registers */
317d0df954bSJoseph Chen 		for (reg = 0; mask && reg < 4; mask >>= 1, reg++) {
318d0df954bSJoseph Chen 			if (mask & 1) {
319d0df954bSJoseph Chen 				if (!pop_vsp(&state->registers[reg], &vsp,
320d0df954bSJoseph Chen 					     kernel_stack, stack, stack_size))
321d0df954bSJoseph Chen 					return false;
322d0df954bSJoseph Chen 				state->update_mask |= 1 << reg;
323d0df954bSJoseph Chen 			}
324d0df954bSJoseph Chen 		}
325d0df954bSJoseph Chen 
326d0df954bSJoseph Chen 	} else if ((insn & INSN_VSP_LARGE_INC_MASK) == INSN_VSP_LARGE_INC) {
327d0df954bSJoseph Chen 		uint32_t uleb128;
328d0df954bSJoseph Chen 
329d0df954bSJoseph Chen 		/* Read the increment value */
330d0df954bSJoseph Chen 		if (!unwind_exec_read_byte(state, &uleb128, kernel_stack))
331d0df954bSJoseph Chen 			return false;
332d0df954bSJoseph Chen 
333d0df954bSJoseph Chen 		state->registers[SP] += 0x204 + (uleb128 << 2);
334d0df954bSJoseph Chen 
335d0df954bSJoseph Chen 	} else {
336d0df954bSJoseph Chen 		/* We hit a new instruction that needs to be implemented */
337d0df954bSJoseph Chen 		printf("Unhandled instruction %.2x\n", insn);
338d0df954bSJoseph Chen 		return false;
339d0df954bSJoseph Chen 	}
340d0df954bSJoseph Chen 
341d0df954bSJoseph Chen 	if (update_vsp)
342d0df954bSJoseph Chen 		state->registers[SP] = vsp;
343d0df954bSJoseph Chen 
344d0df954bSJoseph Chen 	return true;
345d0df954bSJoseph Chen }
346d0df954bSJoseph Chen 
347d0df954bSJoseph Chen /* Performs the unwind of a function */
unwind_tab(struct unwind_state_arm32 * state,bool kernel_stack,ulong stack,size_t stack_size)348d0df954bSJoseph Chen static bool unwind_tab(struct unwind_state_arm32 *state, bool kernel_stack,
349d0df954bSJoseph Chen 		       ulong stack, size_t stack_size)
350d0df954bSJoseph Chen {
351d0df954bSJoseph Chen 	uint32_t entry;
352d0df954bSJoseph Chen 	uint32_t insn;
353d0df954bSJoseph Chen 
354d0df954bSJoseph Chen 	/* Set PC to a known value */
355d0df954bSJoseph Chen 	state->registers[PC] = 0;
356d0df954bSJoseph Chen 
357d0df954bSJoseph Chen 	if (!copy_in(&insn, (void *)state->insn, sizeof(insn), kernel_stack)) {
358d0df954bSJoseph Chen 		printf("Bad insn addr %p", (void *)state->insn);
359d0df954bSJoseph Chen 		return true;
360d0df954bSJoseph Chen 	}
361d0df954bSJoseph Chen 
362d0df954bSJoseph Chen 	/* Read the personality */
363d0df954bSJoseph Chen 	entry = insn & ENTRY_MASK;
364d0df954bSJoseph Chen 
365d0df954bSJoseph Chen 	if (entry == ENTRY_ARM_SU16) {
366d0df954bSJoseph Chen 		state->byte = 2;
367d0df954bSJoseph Chen 		state->entries = 1;
368d0df954bSJoseph Chen 	} else if (entry == ENTRY_ARM_LU16) {
369d0df954bSJoseph Chen 		state->byte = 1;
370d0df954bSJoseph Chen 		state->entries = ((insn >> 16) & 0xFF) + 1;
371d0df954bSJoseph Chen 	} else {
372d0df954bSJoseph Chen 		printf("Unknown entry: %x\n", entry);
373d0df954bSJoseph Chen 		return true;
374d0df954bSJoseph Chen 	}
375d0df954bSJoseph Chen 
376d0df954bSJoseph Chen 	while (state->entries > 0) {
377d0df954bSJoseph Chen 		if (!unwind_exec_insn(state, kernel_stack, stack, stack_size))
378d0df954bSJoseph Chen 			return true;
379d0df954bSJoseph Chen 	}
380d0df954bSJoseph Chen 
381d0df954bSJoseph Chen 	/*
382d0df954bSJoseph Chen 	 * The program counter was not updated, load it from the link register.
383d0df954bSJoseph Chen 	 */
384d0df954bSJoseph Chen 	if (state->registers[PC] == 0) {
385d0df954bSJoseph Chen 		state->registers[PC] = state->registers[LR];
386d0df954bSJoseph Chen 
387d0df954bSJoseph Chen 		/*
388d0df954bSJoseph Chen 		 * If the program counter changed, flag it in the update mask.
389d0df954bSJoseph Chen 		 */
390d0df954bSJoseph Chen 		if (state->start_pc != state->registers[PC])
391d0df954bSJoseph Chen 			state->update_mask |= 1 << PC;
392d0df954bSJoseph Chen 
393d0df954bSJoseph Chen 		/* Check again */
394d0df954bSJoseph Chen 		if (state->registers[PC] == 0)
395d0df954bSJoseph Chen 			return true;
396d0df954bSJoseph Chen 	}
397d0df954bSJoseph Chen 
398d0df954bSJoseph Chen 	return false;
399d0df954bSJoseph Chen }
400d0df954bSJoseph Chen 
unwind_stack_arm32(struct unwind_state_arm32 * state,ulong exidx,size_t exidx_sz,bool kernel_stack,ulong stack,size_t stack_size)401d0df954bSJoseph Chen bool unwind_stack_arm32(struct unwind_state_arm32 *state, ulong exidx,
402d0df954bSJoseph Chen 			size_t exidx_sz, bool kernel_stack, ulong stack,
403d0df954bSJoseph Chen 			size_t stack_size)
404d0df954bSJoseph Chen {
405d0df954bSJoseph Chen 	struct unwind_idx *index;
406d0df954bSJoseph Chen 	bool finished;
407d0df954bSJoseph Chen 
408d0df954bSJoseph Chen 	if (!exidx_sz)
409d0df954bSJoseph Chen 		return false;
410d0df954bSJoseph Chen 
411d0df954bSJoseph Chen 	/* Reset the mask of updated registers */
412d0df954bSJoseph Chen 	state->update_mask = 0;
413d0df954bSJoseph Chen 
414d0df954bSJoseph Chen 	/* The pc value is correct and will be overwritten, save it */
415d0df954bSJoseph Chen 	state->start_pc = state->registers[PC];
416d0df954bSJoseph Chen 
417d0df954bSJoseph Chen 	/* Find the item to run */
418d0df954bSJoseph Chen 	index = find_index(state->start_pc, exidx, exidx_sz);
419d0df954bSJoseph Chen 
420d0df954bSJoseph Chen 	finished = false;
421d0df954bSJoseph Chen 	if (index->insn != EXIDX_CANTUNWIND) {
422d0df954bSJoseph Chen 		if (index->insn & (1U << 31)) {
423d0df954bSJoseph Chen 			/* The data is within the instruction */
424d0df954bSJoseph Chen 			state->insn = (ulong)&index->insn;
425d0df954bSJoseph Chen 		} else {
426d0df954bSJoseph Chen 			/* A prel31 offset to the unwind table */
427d0df954bSJoseph Chen 			state->insn = (ulong)&index->insn +
428d0df954bSJoseph Chen 				      expand_prel31(index->insn);
429d0df954bSJoseph Chen 		}
430d0df954bSJoseph Chen 
431d0df954bSJoseph Chen 		/* Run the unwind function */
432d0df954bSJoseph Chen 		finished = unwind_tab(state, kernel_stack, stack, stack_size);
433d0df954bSJoseph Chen 	}
434d0df954bSJoseph Chen 
435d0df954bSJoseph Chen 	/* This is the top of the stack, finish */
436d0df954bSJoseph Chen 	if (index->insn == EXIDX_CANTUNWIND)
437d0df954bSJoseph Chen 		finished = true;
438d0df954bSJoseph Chen 
439d0df954bSJoseph Chen 	return !finished;
440d0df954bSJoseph Chen }
441d0df954bSJoseph Chen 
offset_prel31(uint32_t addr,int32_t offset)442d0df954bSJoseph Chen static uint32_t offset_prel31(uint32_t addr, int32_t offset)
443d0df954bSJoseph Chen {
444d0df954bSJoseph Chen 	return (addr + offset) & 0x7FFFFFFFUL;
445d0df954bSJoseph Chen }
446d0df954bSJoseph Chen 
relocate_exidx(void * exidx,size_t exidx_sz,int32_t offset)447d0df954bSJoseph Chen int relocate_exidx(void *exidx, size_t exidx_sz, int32_t offset)
448d0df954bSJoseph Chen {
449d0df954bSJoseph Chen 	size_t num_items = exidx_sz / sizeof(struct unwind_idx);
450d0df954bSJoseph Chen 	struct unwind_idx *start = (struct unwind_idx *)exidx;
451d0df954bSJoseph Chen 	size_t n;
452d0df954bSJoseph Chen 
453d0df954bSJoseph Chen 	for (n = 0; n < num_items; n++) {
454d0df954bSJoseph Chen 		struct unwind_idx *item = &start[n];
455d0df954bSJoseph Chen 
456d0df954bSJoseph Chen 		if (item->offset & BIT(31))
457d0df954bSJoseph Chen 			return -EINVAL;
458d0df954bSJoseph Chen 
459d0df954bSJoseph Chen 		/* Offset to the start of the function has to be adjusted */
460d0df954bSJoseph Chen 		item->offset = offset_prel31(item->offset, offset);
461d0df954bSJoseph Chen 
462d0df954bSJoseph Chen 		if (item->insn == EXIDX_CANTUNWIND)
463d0df954bSJoseph Chen 			continue;
464d0df954bSJoseph Chen 		if (item->insn & BIT(31)) {
465d0df954bSJoseph Chen 			/* insn is a table entry itself */
466d0df954bSJoseph Chen 			continue;
467d0df954bSJoseph Chen 		}
468d0df954bSJoseph Chen 		/*
469d0df954bSJoseph Chen 		 * insn is an offset to an entry in .ARM.extab so it has to be
470d0df954bSJoseph Chen 		 * adjusted
471d0df954bSJoseph Chen 		 */
472d0df954bSJoseph Chen 		item->insn = offset_prel31(item->insn, offset);
473d0df954bSJoseph Chen 	}
474d0df954bSJoseph Chen 	return 0;
475d0df954bSJoseph Chen }
476d0df954bSJoseph Chen 
print_stack_arm32(struct unwind_state_arm32 * state,ulong exidx,size_t exidx_sz,bool kernel_stack,ulong stack,size_t stack_size)477d0df954bSJoseph Chen void print_stack_arm32(struct unwind_state_arm32 *state,
478d0df954bSJoseph Chen 		       ulong exidx, size_t exidx_sz, bool kernel_stack,
479d0df954bSJoseph Chen 		       ulong stack, size_t stack_size)
480d0df954bSJoseph Chen {
481d0df954bSJoseph Chen 	ulong pc, lr;
482*74ab8aa2SJoseph Chen #if defined(CONFIG_TPL_BUILD)
483*74ab8aa2SJoseph Chen 	char *build = "tpl";
484*74ab8aa2SJoseph Chen #elif defined(CONFIG_SPL_BUILD)
485*74ab8aa2SJoseph Chen 	char *build = "spl";
486*74ab8aa2SJoseph Chen #else
487*74ab8aa2SJoseph Chen 	char *build = "";
488*74ab8aa2SJoseph Chen #endif
489d0df954bSJoseph Chen 
490d0df954bSJoseph Chen 	if (gd->flags & GD_FLG_RELOC) {
491d0df954bSJoseph Chen 		pc = (ulong)state->registers[PC] - gd->reloc_off;
492d0df954bSJoseph Chen 		lr = (ulong)state->registers[LR] - gd->reloc_off;
493d0df954bSJoseph Chen 	} else {
494d0df954bSJoseph Chen 		pc = (ulong)state->registers[PC];
495d0df954bSJoseph Chen 		lr = (ulong)state->registers[LR];
496d0df954bSJoseph Chen 	}
497d0df954bSJoseph Chen 
498d0df954bSJoseph Chen 	printf("\nCall trace:\n");
499d0df954bSJoseph Chen 	printf("  PC:	[< %08lx >]\n", pc);
500d0df954bSJoseph Chen 	printf("  LR:	[< %08lx >]\n", lr);
501d0df954bSJoseph Chen 
502d0df954bSJoseph Chen 	printf("\nStack:\n");
503d0df954bSJoseph Chen 	do {
504d0df954bSJoseph Chen 		if (gd->flags & GD_FLG_RELOC)
505d0df954bSJoseph Chen 			pc = (ulong)state->registers[PC] - gd->reloc_off;
506d0df954bSJoseph Chen 		else
507d0df954bSJoseph Chen 			pc = (ulong)state->registers[PC];
508d0df954bSJoseph Chen 
509d0df954bSJoseph Chen 		printf("	[< %08lx >]\n", pc);
510d0df954bSJoseph Chen 	} while (unwind_stack_arm32(state, exidx, exidx_sz,
511d0df954bSJoseph Chen 				    kernel_stack, stack, stack_size));
512d0df954bSJoseph Chen 
513*74ab8aa2SJoseph Chen 	printf("\nCopy info from \"Call trace...\" to a file(eg. dump.txt), and run\n"
514*74ab8aa2SJoseph Chen 	       "command in your U-Boot project: "
515*74ab8aa2SJoseph Chen 	       "./scripts/stacktrace.sh dump.txt %s\n\n", build);
516d0df954bSJoseph Chen }
517d0df954bSJoseph Chen 
dump_core_stack(struct pt_regs * regs)518d0df954bSJoseph Chen void dump_core_stack(struct pt_regs *regs)
519d0df954bSJoseph Chen {
520d0df954bSJoseph Chen 	struct unwind_state_arm32 state;
521d0df954bSJoseph Chen 	ulong exidx = (ulong)__exidx_start;
522d0df954bSJoseph Chen 	size_t exidx_sz = (ulong)__exidx_end - (ulong)__exidx_start;
523d0df954bSJoseph Chen 	ulong stack = gd->start_addr_sp;
524d0df954bSJoseph Chen 	size_t stack_size = CONFIG_SYS_STACK_SIZE;
525d0df954bSJoseph Chen 	int i;
526d0df954bSJoseph Chen 
527d0df954bSJoseph Chen 	/* Don't use memset(), which updates LR ! */
528d0df954bSJoseph Chen 	for (i = 0; i < 16; i++)
529d0df954bSJoseph Chen 		state.registers[i] = 0;
530d0df954bSJoseph Chen 	state.update_mask = 0;
531d0df954bSJoseph Chen 	state.start_pc = 0;
532d0df954bSJoseph Chen 	state.entries = 0;
533d0df954bSJoseph Chen 	state.insn = 0;
534d0df954bSJoseph Chen 	state.byte = 0;
535d0df954bSJoseph Chen 
536d0df954bSJoseph Chen 	/* r7: Thumb-style frame pointer */
537d0df954bSJoseph Chen 	state.registers[7] = regs->ARM_r7;
538d0df954bSJoseph Chen 	/* r11: ARM-style frame pointer */
539d0df954bSJoseph Chen 	state.registers[FP] = regs->ARM_ip;
540d0df954bSJoseph Chen 	state.registers[SP] = regs->ARM_sp;
541d0df954bSJoseph Chen 	state.registers[LR] = regs->ARM_lr;
542d0df954bSJoseph Chen 	state.registers[PC] = regs->ARM_pc;
543d0df954bSJoseph Chen 
544d0df954bSJoseph Chen 	print_stack_arm32(&state, exidx, exidx_sz,
545d0df954bSJoseph Chen 			  true, stack, stack_size);
546d0df954bSJoseph Chen }
547d0df954bSJoseph Chen 
dump_stack(void)548d0df954bSJoseph Chen void dump_stack(void)
549d0df954bSJoseph Chen {
550d0df954bSJoseph Chen 	struct pt_regs regs;
551d0df954bSJoseph Chen 
552d0df954bSJoseph Chen 	regs.ARM_r7 = read_r7();
553d0df954bSJoseph Chen 	regs.ARM_ip = read_fp();
554d0df954bSJoseph Chen 	regs.ARM_sp = read_sp();
555d0df954bSJoseph Chen 	regs.ARM_lr = read_lr();
556d0df954bSJoseph Chen 	regs.ARM_pc = (uint32_t)dump_stack;
557d0df954bSJoseph Chen 
558d0df954bSJoseph Chen 	dump_core_stack(&regs);
559d0df954bSJoseph Chen }
560