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