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 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 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 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 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 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 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 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 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 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 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 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 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