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(®s);
559d0df954bSJoseph Chen }
560