diff --git a/core/arch/arm/arm.mk b/core/arch/arm/arm.mk index cb69978be55..13361d8a9cd 100644 --- a/core/arch/arm/arm.mk +++ b/core/arch/arm/arm.mk @@ -97,7 +97,7 @@ arch-bits-core := 32 core-platform-cppflags += $(arm32-platform-cppflags) core-platform-cflags += $(arm32-platform-cflags) core-platform-cflags += $(arm32-platform-cflags-no-hard-float) -ifeq ($(CFG_CORE_UNWIND),y) +ifeq ($(CFG_UNWIND),y) core-platform-cflags += -funwind-tables endif core-platform-cflags += $(arm32-platform-cflags-generic) @@ -120,6 +120,9 @@ ta_arm32-platform-cflags += $(arm32-platform-cflags-hard-float) else ta_arm32-platform-cflags += $(arm32-platform-cflags-no-hard-float) endif +ifeq ($(CFG_UNWIND),y) +ta_arm32-platform-cflags += -funwind-tables +endif ta_arm32-platform-aflags += $(platform-aflags-debug-info) ta_arm32-platform-aflags += $(arm32-platform-aflags) diff --git a/core/arch/arm/include/kernel/abort.h b/core/arch/arm/include/kernel/abort.h index 0480f43092c..e31ab513488 100644 --- a/core/arch/arm/include/kernel/abort.h +++ b/core/arch/arm/include/kernel/abort.h @@ -45,7 +45,9 @@ struct abort_info { struct thread_abort_regs *regs; }; +/* Print abort info to the console */ void abort_print(struct abort_info *ai); +/* Print abort info + stack dump to the console */ void abort_print_error(struct abort_info *ai); void abort_handler(uint32_t abort_type, struct thread_abort_regs *regs); diff --git a/core/arch/arm/include/kernel/thread.h b/core/arch/arm/include/kernel/thread.h index 5e340d3d652..51196dc6f72 100644 --- a/core/arch/arm/include/kernel/thread.h +++ b/core/arch/arm/include/kernel/thread.h @@ -489,7 +489,15 @@ void thread_unwind_user_mode(uint32_t ret, uint32_t exit_status0, vaddr_t thread_get_saved_thread_sp(void); #endif /*ARM64*/ -bool thread_addr_is_in_stack(vaddr_t va); +/* + * Returns the start address (bottom) of the stack for the current thread, + * zero if there is no current thread. + */ +vaddr_t thread_stack_start(void); + + +/* Returns the stack size for the current thread */ +size_t thread_stack_size(void); /* * Adds a mutex to the list of held mutexes for current thread diff --git a/core/arch/arm/include/kernel/unwind.h b/core/arch/arm/include/kernel/unwind.h index f2c79b5ba2f..cab538cc013 100644 --- a/core/arch/arm/include/kernel/unwind.h +++ b/core/arch/arm/include/kernel/unwind.h @@ -37,9 +37,8 @@ #include #include -#ifdef ARM32 -/* The state of the unwind process */ -struct unwind_state { +/* The state of the unwind process (32-bit mode) */ +struct unwind_state_arm32 { uint32_t registers[16]; uint32_t start_pc; uint32_t *insn; @@ -47,29 +46,42 @@ struct unwind_state { unsigned byte; uint16_t update_mask; }; -#endif /*ARM32*/ + +/* + * Unwind a 32-bit user or kernel stack. + * @exidx, @exidx_sz: address and size of the binary search index table + * (.ARM.exidx section). + */ +bool unwind_stack_arm32(struct unwind_state_arm32 *state, uaddr_t exidx, + size_t exidx_sz); #ifdef ARM64 -struct unwind_state { +/* The state of the unwind process (64-bit mode) */ +struct unwind_state_arm64 { uint64_t fp; uint64_t sp; uint64_t pc; }; -#endif /*ARM64*/ -bool unwind_stack(struct unwind_state *state); +/* + * Unwind a 64-bit user or kernel stack. + * @stack, @stack_size: the bottom of the stack and its size, respectively. + */ +bool unwind_stack_arm64(struct unwind_state_arm64 *state, uaddr_t stack, + size_t stack_size); +#endif /*ARM64*/ -#if defined(CFG_CORE_UNWIND) && (TRACE_LEVEL > 0) -void print_stack(int level); +#if defined(CFG_UNWIND) && (TRACE_LEVEL > 0) +void print_kernel_stack(int level); #else -static inline void print_stack(int level __unused) +static inline void print_kernel_stack(int level __unused) { } #endif #endif /*ASM*/ -#ifdef CFG_CORE_UNWIND +#ifdef CFG_UNWIND #define UNWIND(...) __VA_ARGS__ #else #define UNWIND(...) diff --git a/core/arch/arm/include/kernel/user_ta.h b/core/arch/arm/include/kernel/user_ta.h index 3b980fe86a0..d283a032b53 100644 --- a/core/arch/arm/include/kernel/user_ta.h +++ b/core/arch/arm/include/kernel/user_ta.h @@ -41,6 +41,8 @@ TAILQ_HEAD(tee_storage_enum_head, tee_storage_enum); struct user_ta_ctx { uaddr_t entry_func; + uaddr_t exidx_start; /* 32-bit TA: exception handling index table */ + size_t exidx_size; bool is_32bit; /* true if 32-bit ta, false if 64-bit ta */ /* list of sessions opened by this TA */ struct tee_ta_session_head open_sessions; diff --git a/core/arch/arm/kernel/abort.c b/core/arch/arm/kernel/abort.c index 3d2952122b6..0efd223ca2d 100644 --- a/core/arch/arm/kernel/abort.c +++ b/core/arch/arm/kernel/abort.c @@ -32,11 +32,14 @@ #include #include #include +#include #include #include #include #include +#include "thread_private.h" + enum fault_type { FAULT_TYPE_USER_TA_PANIC, FAULT_TYPE_USER_TA_VFP, @@ -44,11 +47,51 @@ enum fault_type { FAULT_TYPE_IGNORE, }; -#ifdef CFG_CORE_UNWIND +#ifdef CFG_UNWIND + +static void get_current_ta_exidx(uaddr_t *exidx, size_t *exidx_sz) +{ + struct tee_ta_session *s; + struct user_ta_ctx *utc; + + if (tee_ta_get_current_session(&s) != TEE_SUCCESS) + panic(); + + utc = to_user_ta_ctx(s->ctx); + + /* Only 32-bit TAs use .ARM.exidx/.ARM.extab exception handling */ + assert(utc->is_32bit); + + *exidx = utc->exidx_start; /* NULL if TA has no unwind tables */ + if (*exidx) + *exidx += utc->load_addr; + *exidx_sz = utc->exidx_size; +} + #ifdef ARM32 -static void __print_stack_unwind(struct abort_info *ai) + +/* + * These are set in the linker script. Their addresses will be the start or end + * of the exception binary search index table (.ARM.exidx section) + */ +extern uint8_t __exidx_start[]; +extern uint8_t __exidx_end[]; + +/* + * Kernel or user mode unwind (32-bit execution state). + */ +static void __print_stack_unwind_arm32(struct abort_info *ai) { - struct unwind_state state; + struct unwind_state_arm32 state; + uaddr_t exidx; + size_t exidx_sz; + + if (abort_is_user_exception(ai)) { + get_current_ta_exidx(&exidx, &exidx_sz); + } else { + exidx = (vaddr_t)__exidx_start; + exidx_sz = (vaddr_t)__exidx_end - (vaddr_t)__exidx_start; + } memset(&state, 0, sizeof(state)); state.registers[0] = ai->regs->r0; @@ -63,41 +106,102 @@ static void __print_stack_unwind(struct abort_info *ai) state.registers[9] = ai->regs->r9; state.registers[10] = ai->regs->r10; state.registers[11] = ai->regs->r11; + state.registers[13] = read_mode_sp(ai->regs->spsr & CPSR_MODE_MASK); state.registers[14] = read_mode_lr(ai->regs->spsr & CPSR_MODE_MASK); state.registers[15] = ai->pc; + EMSG_RAW("Call stack:"); do { EMSG_RAW(" pc 0x%08x", state.registers[15]); - } while (unwind_stack(&state)); + } while (exidx && unwind_stack_arm32(&state, exidx, exidx_sz)); } -#endif /*ARM32*/ +#else /* ARM32 */ +static void __print_stack_unwind_arm32(struct abort_info *ai __unused) +{ + struct unwind_state_arm32 state; + uaddr_t exidx; + size_t exidx_sz; + + /* 64-bit kernel, hence 32-bit unwind must be for user mode */ + assert(abort_is_user_exception(ai)); + + get_current_ta_exidx(&exidx, &exidx_sz); + + memset(&state, 0, sizeof(state)); + state.registers[0] = ai->regs->x0; + state.registers[1] = ai->regs->x1; + state.registers[2] = ai->regs->x2; + state.registers[3] = ai->regs->x3; + state.registers[4] = ai->regs->x4; + state.registers[5] = ai->regs->x5; + state.registers[6] = ai->regs->x6; + state.registers[7] = ai->regs->x7; + state.registers[8] = ai->regs->x8; + state.registers[9] = ai->regs->x9; + state.registers[10] = ai->regs->x10; + state.registers[11] = ai->regs->x11; + + state.registers[13] = ai->regs->x13; + state.registers[14] = ai->regs->x14; + state.registers[15] = ai->pc; + + EMSG_RAW("Call stack:"); + do { + EMSG_RAW(" pc 0x%08x", state.registers[15]); + } while (exidx && unwind_stack_arm32(&state, exidx, exidx_sz)); +} +#endif /* ARM32 */ #ifdef ARM64 -static void __print_stack_unwind(struct abort_info *ai) +/* Kernel or user mode unwind (64-bit execution state) */ +static void __print_stack_unwind_arm64(struct abort_info *ai) { - struct unwind_state state; + struct unwind_state_arm64 state; + uaddr_t stack; + size_t stack_size; + + if (abort_is_user_exception(ai)) { + struct tee_ta_session *s; + struct user_ta_ctx *utc; + + if (tee_ta_get_current_session(&s) != TEE_SUCCESS) + panic(); + + utc = to_user_ta_ctx(s->ctx); + /* User stack */ + stack = (uaddr_t)utc->mmu->regions[0].va; + stack_size = utc->mobj_stack->size; + } else { + /* Kernel stack */ + stack = thread_stack_start(); + stack_size = thread_stack_size(); + } memset(&state, 0, sizeof(state)); state.pc = ai->regs->elr; state.fp = ai->regs->x29; + EMSG_RAW("Call stack:"); do { EMSG_RAW("pc 0x%016" PRIx64, state.pc); - } while (unwind_stack(&state)); + } while (stack && unwind_stack_arm64(&state, stack, stack_size)); } -#endif /*ARM64*/ +#else +static void __print_stack_unwind_arm64(struct abort_info *ai __unused) +{ -static void print_stack_unwind(struct abort_info *ai) +} +#endif /*ARM64*/ +#else /* CFG_UNWIND */ +static void __print_stack_unwind_arm32(struct abort_info *ai __unused) { - EMSG_RAW("Call stack:"); - __print_stack_unwind(ai); } -#else /*CFG_CORE_UNWIND*/ -static void print_stack_unwind(struct abort_info *ai __unused) + +static void __print_stack_unwind_arm64(struct abort_info *ai __unused) { } -#endif /*CFG_CORE_UNWIND*/ +#endif /* CFG_UNWIND */ static __maybe_unused const char *abort_type_to_str(uint32_t abort_type) { @@ -129,7 +233,7 @@ static __maybe_unused const char *fault_to_str(uint32_t abort_type, } } -static __maybe_unused void print_detailed_abort( +static __maybe_unused void __print_abort_info( struct abort_info *ai __maybe_unused, const char *ctx __maybe_unused) { @@ -196,46 +300,66 @@ static __maybe_unused void print_detailed_abort( #endif /*ARM64*/ } -static void print_user_abort(struct abort_info *ai __maybe_unused) +#if defined(ARM32) +static const bool kernel_is32bit = true; +#elif defined(ARM64) +static const bool kernel_is32bit; +#endif + +/* + * Print abort info and (optionally) stack dump to the console + * @ai user-mode or kernel-mode abort info. If user mode, the current session + * must be the one of the TA that caused the abort. + * @stack_dump true to show a stack trace + */ +static void __abort_print(struct abort_info *ai, bool stack_dump) { -#ifdef CFG_TEE_CORE_TA_TRACE - print_detailed_abort(ai, "user TA"); - tee_ta_dump_current(); + bool is_32bit; + bool paged_ta = false; + + if (abort_is_user_exception(ai)) { + struct tee_ta_session *s; + struct user_ta_ctx *utc; + + if (tee_ta_get_current_session(&s) != TEE_SUCCESS) + panic(); + + utc = to_user_ta_ctx(s->ctx); + is_32bit = utc->is_32bit; +#ifdef CFG_PAGED_USER_TA + /* + * We don't want to unwind paged TAs, because we currently + * don't handle page faults that could occur when accessing the + * TA memory (unwind tables for instance). + */ + paged_ta = true; #endif + + __print_abort_info(ai, "user TA"); + tee_ta_dump_current(); + } else { + is_32bit = kernel_is32bit; + + __print_abort_info(ai, "core"); + } + + if (!stack_dump || paged_ta) + return; + + if (is_32bit) + __print_stack_unwind_arm32(ai); + else + __print_stack_unwind_arm64(ai); } -void abort_print(struct abort_info *ai __maybe_unused) +void abort_print(struct abort_info *ai) { -#if (TRACE_LEVEL >= TRACE_INFO) - print_detailed_abort(ai, "core"); -#endif /*TRACE_LEVEL >= TRACE_DEBUG*/ + __abort_print(ai, false); } void abort_print_error(struct abort_info *ai) { -#if (TRACE_LEVEL >= TRACE_INFO) - /* full verbose log at DEBUG level */ - print_detailed_abort(ai, "core"); -#else -#ifdef ARM32 - EMSG("%s-abort at 0x%" PRIxVA "\n" - "FSR 0x%x PC 0x%x TTBR0 0x%X CONTEXIDR 0x%X\n" - "CPUID 0x%x CPSR 0x%x (read from SPSR)", - abort_type_to_str(ai->abort_type), - ai->va, ai->fault_descr, ai->pc, read_ttbr0(), read_contextidr(), - read_mpidr(), read_spsr()); -#endif /*ARM32*/ -#ifdef ARM64 - EMSG("%s-abort at 0x%" PRIxVA "\n" - "ESR 0x%x PC 0x%x TTBR0 0x%" PRIx64 " CONTEXIDR 0x%X\n" - "CPUID 0x%" PRIx64 " CPSR 0x%x (read from SPSR)", - abort_type_to_str(ai->abort_type), - ai->va, ai->fault_descr, ai->pc, read_ttbr0_el1(), - read_contextidr_el1(), - read_mpidr_el1(), (uint32_t)ai->regs->spsr); -#endif /*ARM64*/ -#endif /*TRACE_LEVEL >= TRACE_DEBUG*/ - print_stack_unwind(ai); + __abort_print(ai, true); } #ifdef ARM32 @@ -553,7 +677,7 @@ void abort_handler(uint32_t abort_type, struct thread_abort_regs *regs) break; case FAULT_TYPE_USER_TA_PANIC: DMSG("[abort] abort in User mode (TA will panic)"); - print_user_abort(&ai); + abort_print_error(&ai); vfp_disable(); handle_user_ta_panic(&ai); break; @@ -568,11 +692,9 @@ void abort_handler(uint32_t abort_type, struct thread_abort_regs *regs) handled = tee_pager_handle_fault(&ai); thread_kernel_restore_vfp(); if (!handled) { - if (!abort_is_user_exception(&ai)) { - abort_print_error(&ai); + abort_print_error(&ai); + if (!abort_is_user_exception(&ai)) panic("unhandled pageable abort"); - } - print_user_abort(&ai); DMSG("[abort] abort in User mode (TA will panic)"); vfp_disable(); handle_user_ta_panic(&ai); diff --git a/core/arch/arm/kernel/elf_common.h b/core/arch/arm/kernel/elf_common.h index 497a9023f6e..a9cb6afa889 100644 --- a/core/arch/arm/kernel/elf_common.h +++ b/core/arch/arm/kernel/elf_common.h @@ -355,6 +355,7 @@ typedef struct { #define PT_HISUNW 0x6fffffff #define PT_HIOS 0x6fffffff /* Last OS-specific. */ #define PT_LOPROC 0x70000000 /* First processor-specific type. */ +#define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */ #define PT_HIPROC 0x7fffffff /* Last processor-specific type. */ /* Values for p_flags. */ diff --git a/core/arch/arm/kernel/elf_load.c b/core/arch/arm/kernel/elf_load.c index cc7e9d99bf5..fbfa7df17fa 100644 --- a/core/arch/arm/kernel/elf_load.c +++ b/core/arch/arm/kernel/elf_load.c @@ -397,25 +397,26 @@ TEE_Result elf_load_head(struct elf_load_state *state, size_t head_size, } TEE_Result elf_load_get_next_segment(struct elf_load_state *state, size_t *idx, - vaddr_t *vaddr, size_t *size, uint32_t *flags) + vaddr_t *vaddr, size_t *size, uint32_t *flags, + uint32_t *type) { struct elf_ehdr ehdr; copy_ehdr(&ehdr, state); - while (*idx < ehdr.e_phnum) { + if (*idx < ehdr.e_phnum) { struct elf_phdr phdr; copy_phdr(&phdr, state, *idx); (*idx)++; - if (phdr.p_type == PT_LOAD) { - if (vaddr) - *vaddr = phdr.p_vaddr; - if (size) - *size = phdr.p_memsz; - if (flags) - *flags = phdr.p_flags; - return TEE_SUCCESS; - } + if (vaddr) + *vaddr = phdr.p_vaddr; + if (size) + *size = phdr.p_memsz; + if (flags) + *flags = phdr.p_flags; + if (type) + *type = phdr.p_type; + return TEE_SUCCESS; } return TEE_ERROR_ITEM_NOT_FOUND; } diff --git a/core/arch/arm/kernel/elf_load.h b/core/arch/arm/kernel/elf_load.h index 95743f24e95..2d90d60f834 100644 --- a/core/arch/arm/kernel/elf_load.h +++ b/core/arch/arm/kernel/elf_load.h @@ -71,7 +71,8 @@ TEE_Result elf_load_head(struct elf_load_state *state, size_t head_size, void **head, size_t *vasize, bool *is_32bit); TEE_Result elf_load_body(struct elf_load_state *state, vaddr_t vabase); TEE_Result elf_load_get_next_segment(struct elf_load_state *state, size_t *idx, - vaddr_t *vaddr, size_t *size, uint32_t *flags); + vaddr_t *vaddr, size_t *size, uint32_t *flags, + uint32_t *type); void elf_load_final(struct elf_load_state *state); #endif /*ELF_LOAD_H*/ diff --git a/core/arch/arm/kernel/sub.mk b/core/arch/arm/kernel/sub.mk index fc148a3a00b..d96d6b22816 100644 --- a/core/arch/arm/kernel/sub.mk +++ b/core/arch/arm/kernel/sub.mk @@ -43,7 +43,7 @@ srcs-$(CFG_ARM32_core) += generic_entry_a32.S srcs-$(CFG_ARM64_core) += generic_entry_a64.S endif -ifeq ($(CFG_CORE_UNWIND),y) -srcs-$(CFG_ARM32_core) += unwind_arm32.c +ifeq ($(CFG_UNWIND),y) +srcs-y += unwind_arm32.c srcs-$(CFG_ARM64_core) += unwind_arm64.c endif diff --git a/core/arch/arm/kernel/thread.c b/core/arch/arm/kernel/thread.c index 6c1451952ea..d9a81db32f5 100644 --- a/core/arch/arm/kernel/thread.c +++ b/core/arch/arm/kernel/thread.c @@ -619,17 +619,21 @@ vaddr_t thread_get_saved_thread_sp(void) } #endif /*ARM64*/ -bool thread_addr_is_in_stack(vaddr_t va) +vaddr_t thread_stack_start(void) { struct thread_ctx *thr; int ct = thread_get_id_may_fail(); if (ct == -1) - return false; + return 0; thr = threads + ct; - return va < thr->stack_va_end && - va >= (thr->stack_va_end - STACK_THREAD_SIZE); + return thr->stack_va_end - STACK_THREAD_SIZE; +} + +size_t thread_stack_size(void) +{ + return STACK_THREAD_SIZE; } void thread_state_free(void) diff --git a/core/arch/arm/kernel/thread_a64.S b/core/arch/arm/kernel/thread_a64.S index bd27e4e5b0d..5c52a9511ff 100644 --- a/core/arch/arm/kernel/thread_a64.S +++ b/core/arch/arm/kernel/thread_a64.S @@ -305,6 +305,8 @@ FUNC __thread_enter_user_mode , : msr sp_el0, x4 /* Used when running TA in Aarch64 */ /* Set user function */ msr elr_el1, x5 + /* Set frame pointer (user stack can't be unwound past this point) */ + mov x29, #0 /* Jump into user mode */ eret diff --git a/core/arch/arm/kernel/unwind_arm32.c b/core/arch/arm/kernel/unwind_arm32.c index 7efe94ba081..527159c9b45 100644 --- a/core/arch/arm/kernel/unwind_arm32.c +++ b/core/arch/arm/kernel/unwind_arm32.c @@ -95,13 +95,6 @@ struct unwind_idx { uint32_t insn; }; -/* - * These are set in the linker script. Their addresses will be - * either the start or end of the exception table or index. - */ -extern struct unwind_idx __exidx_start; -extern struct unwind_idx __exidx_end; - /* Expand a 31-bit signed value to a 32-bit signed value */ static int32_t expand_prel31(uint32_t prel31) { @@ -113,18 +106,19 @@ static int32_t expand_prel31(uint32_t prel31) * Perform a binary search of the index table to find the function * with the largest address that doesn't exceed addr. */ -static struct unwind_idx *find_index(uint32_t addr) +static struct unwind_idx *find_index(uint32_t addr, vaddr_t exidx, + size_t exidx_sz) { vaddr_t idx_start, idx_end; unsigned int min, mid, max; struct unwind_idx *start; struct unwind_idx *item; int32_t prel31_addr; - uint32_t func_addr; + vaddr_t func_addr; - start = &__exidx_start; - idx_start = (vaddr_t)&__exidx_start; - idx_end = (vaddr_t)&__exidx_end; + start = (struct unwind_idx *)exidx; + idx_start = exidx; + idx_end = exidx + exidx_sz; min = 0; max = (idx_end - idx_start) / sizeof(struct unwind_idx); @@ -135,7 +129,7 @@ static struct unwind_idx *find_index(uint32_t addr) item = &start[mid]; prel31_addr = expand_prel31(item->offset); - func_addr = (uint32_t)&item->offset + prel31_addr; + func_addr = (vaddr_t)&item->offset + prel31_addr; if (func_addr <= addr) { min = mid; @@ -148,7 +142,7 @@ static struct unwind_idx *find_index(uint32_t addr) } /* Reads the next byte from the instruction list */ -static uint8_t unwind_exec_read_byte(struct unwind_state *state) +static uint8_t unwind_exec_read_byte(struct unwind_state_arm32 *state) { uint8_t insn; @@ -167,10 +161,10 @@ static uint8_t unwind_exec_read_byte(struct unwind_state *state) } /* Executes the next instruction on the list */ -static bool unwind_exec_insn(struct unwind_state *state) +static bool unwind_exec_insn(struct unwind_state_arm32 *state) { unsigned int insn; - uint32_t *vsp = (uint32_t *)state->registers[SP]; + uint32_t *vsp = (uint32_t *)(uintptr_t)state->registers[SP]; int update_vsp = 0; /* This should never happen */ @@ -276,14 +270,14 @@ static bool unwind_exec_insn(struct unwind_state *state) } if (update_vsp) { - state->registers[SP] = (uint32_t)vsp; + state->registers[SP] = (uint32_t)(uintptr_t)vsp; } return true; } /* Performs the unwind of a function */ -static bool unwind_tab(struct unwind_state *state) +static bool unwind_tab(struct unwind_state_arm32 *state) { uint32_t entry; @@ -325,7 +319,8 @@ static bool unwind_tab(struct unwind_state *state) return false; } -bool unwind_stack(struct unwind_state *state) +bool unwind_stack_arm32(struct unwind_state_arm32 *state, uaddr_t exidx, + size_t exidx_sz) { struct unwind_idx *index; bool finished; @@ -337,7 +332,7 @@ bool unwind_stack(struct unwind_state *state) state->start_pc = state->registers[PC]; /* Find the item to run */ - index = find_index(state->start_pc); + index = find_index(state->start_pc, exidx, exidx_sz); finished = false; if (index->insn != EXIDX_CANTUNWIND) { @@ -361,11 +356,11 @@ bool unwind_stack(struct unwind_state *state) return !finished; } -#if defined(CFG_CORE_UNWIND) && (TRACE_LEVEL > 0) +#if defined(CFG_UNWIND) && defined(ARM32) && (TRACE_LEVEL > 0) -void print_stack(int level) +void print_kernel_stack(int level) { - struct unwind_state state; + struct unwind_state_arm32 state; memset(state.registers, 0, sizeof(state.registers)); /* r7: Thumb-style frame pointer */ @@ -374,7 +369,7 @@ void print_stack(int level) state.registers[FP] = read_fp(); state.registers[SP] = read_sp(); state.registers[LR] = read_lr(); - state.registers[PC] = (uint32_t)print_stack; + state.registers[PC] = (uint32_t)print_kernel_stack; do { switch (level) { @@ -393,25 +388,7 @@ void print_stack(int level) default: break; } - } while (unwind_stack(&state)); -} - -#endif /* defined(CFG_CORE_UNWIND) && (TRACE_LEVEL > 0) */ - -/* - * These functions are referenced but never used - */ -void __aeabi_unwind_cpp_pr0(void); -void __aeabi_unwind_cpp_pr0(void) -{ + } while (unwind_stack_arm32(&state, 0, 0)); } -void __aeabi_unwind_cpp_pr1(void); -void __aeabi_unwind_cpp_pr1(void) -{ -} - -void __aeabi_unwind_cpp_pr2(void); -void __aeabi_unwind_cpp_pr2(void) -{ -} +#endif diff --git a/core/arch/arm/kernel/unwind_arm64.c b/core/arch/arm/kernel/unwind_arm64.c index 10b70ef4929..18d9f37b535 100644 --- a/core/arch/arm/kernel/unwind_arm64.c +++ b/core/arch/arm/kernel/unwind_arm64.c @@ -34,12 +34,13 @@ #include #include -bool unwind_stack(struct unwind_state *frame) +bool unwind_stack_arm64(struct unwind_state_arm64 *frame, uaddr_t stack, + size_t stack_size) { uint64_t fp; fp = frame->fp; - if (!thread_addr_is_in_stack(fp)) + if (fp < stack || fp >= stack + stack_size) return false; frame->sp = fp + 0x10; @@ -51,11 +52,11 @@ bool unwind_stack(struct unwind_state *frame) return true; } -#if defined(CFG_CORE_UNWIND) && (TRACE_LEVEL > 0) +#if defined(CFG_UNWIND) && (TRACE_LEVEL > 0) -void print_stack(int level) +void print_kernel_stack(int level) { - struct unwind_state state; + struct unwind_state_arm64 state; memset(&state, 0, sizeof(state)); state.pc = read_pc(); @@ -78,7 +79,7 @@ void print_stack(int level) default: break; } - } while (unwind_stack(&state)); + } while (unwind_stack_arm64(&state, 0, 0)); } -#endif /* defined(CFG_CORE_UNWIND) && (TRACE_LEVEL > 0) */ +#endif diff --git a/core/arch/arm/kernel/user_ta.c b/core/arch/arm/kernel/user_ta.c index 416bb924e33..cf8efebf175 100644 --- a/core/arch/arm/kernel/user_ta.c +++ b/core/arch/arm/kernel/user_ta.c @@ -150,19 +150,25 @@ static TEE_Result load_elf_segments(struct user_ta_ctx *utc, vaddr_t offs; size_t size; uint32_t flags; + uint32_t type; res = elf_load_get_next_segment(elf_state, &idx, &offs, &size, - &flags); + &flags, &type); if (res == TEE_ERROR_ITEM_NOT_FOUND) break; if (res != TEE_SUCCESS) return res; - mattr = elf_flags_to_mattr(flags, init_attrs); - res = tee_mmu_map_add_segment(utc, utc->mobj_code, offs, size, - mattr); - if (res != TEE_SUCCESS) - return res; + if (type == PT_LOAD) { + mattr = elf_flags_to_mattr(flags, init_attrs); + res = tee_mmu_map_add_segment(utc, utc->mobj_code, + offs, size, mattr); + if (res != TEE_SUCCESS) + return res; + } else if (type == PT_ARM_EXIDX) { + utc->exidx_start = offs; + utc->exidx_size = size; + } } if (init_attrs) diff --git a/lib/libutee/arch/arm/user_ta_entry.c b/lib/libutee/arch/arm/user_ta_entry.c index 08842c79c8e..442230df061 100644 --- a/lib/libutee/arch/arm/user_ta_entry.c +++ b/lib/libutee/arch/arm/user_ta_entry.c @@ -216,6 +216,14 @@ void __noreturn __utee_entry(unsigned long func, unsigned long session_id, { TEE_Result res; +#if defined(ARM32) && defined(CFG_UNWIND) + /* + * This function is the bottom of the user call stack: mark it as such + * so that the unwinding code won't try to go further down. + */ + asm(".cantunwind"); +#endif + switch (func) { case UTEE_ENTRY_FUNC_OPEN_SESSION: res = entry_open_session(session_id, up); diff --git a/lib/libutils/ext/arch/arm/aeabi_unwind.c b/lib/libutils/ext/arch/arm/aeabi_unwind.c new file mode 100644 index 00000000000..ff41febb7e6 --- /dev/null +++ b/lib/libutils/ext/arch/arm/aeabi_unwind.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017, Linaro Limited + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +void __aeabi_unwind_cpp_pr0(void); +void __aeabi_unwind_cpp_pr0(void) +{ +} + +void __aeabi_unwind_cpp_pr1(void); +void __aeabi_unwind_cpp_pr1(void) +{ +} + +void __aeabi_unwind_cpp_pr2(void); +void __aeabi_unwind_cpp_pr2(void) +{ +} + diff --git a/lib/libutils/ext/arch/arm/sub.mk b/lib/libutils/ext/arch/arm/sub.mk index d141a4dc3fa..b10db0c1f8b 100644 --- a/lib/libutils/ext/arch/arm/sub.mk +++ b/lib/libutils/ext/arch/arm/sub.mk @@ -1,2 +1,3 @@ +srcs-$(CFG_ARM32_$(sm)) += aeabi_unwind.c srcs-$(CFG_ARM32_$(sm)) += atomic_a32.S srcs-$(CFG_ARM64_$(sm)) += atomic_a64.S diff --git a/lib/libutils/ext/include/trace.h b/lib/libutils/ext/include/trace.h index 8e0abb5700c..1a1fdbd5940 100644 --- a/lib/libutils/ext/include/trace.h +++ b/lib/libutils/ext/include/trace.h @@ -167,36 +167,36 @@ void dhex_dump(const char *function, int line, int level, #endif /* TRACE_LEVEL */ -#if defined(__KERNEL__) && defined(CFG_CORE_UNWIND) +#if defined(__KERNEL__) && defined(CFG_UNWIND) #include #define _PRINT_STACK #endif #if defined(_PRINT_STACK) && (TRACE_LEVEL >= TRACE_ERROR) -#define EPRINT_STACK() print_stack(TRACE_ERROR) +#define EPRINT_STACK() print_kernel_stack(TRACE_ERROR) #else #define EPRINT_STACK() (void)0 #endif #if defined(_PRINT_STACK) && (TRACE_LEVEL >= TRACE_INFO) -#define IPRINT_STACK() print_stack(TRACE_INFO) +#define IPRINT_STACK() print_kernel_stack(TRACE_INFO) #else #define IPRINT_STACK() (void)0 #endif #if defined(_PRINT_STACK) && (TRACE_LEVEL >= TRACE_DEBUG) -#define DPRINT_STACK() print_stack(TRACE_DEBUG) +#define DPRINT_STACK() print_kernel_stack(TRACE_DEBUG) #else #define DPRINT_STACK() (void)0 #endif #if defined(_PRINT_STACK) && (TRACE_LEVEL >= TRACE_FLOW) -#define FPRINT_STACK() print_stack(TRACE_FLOW) +#define FPRINT_STACK() print_kernel_stack(TRACE_FLOW) #else #define FPRINT_STACK() (void)0 #endif -#if defined(__KERNEL__) && defined(CFG_CORE_UNWIND) +#if defined(__KERNEL__) && defined(CFG_UNWIND) #undef _PRINT_STACK #endif diff --git a/mk/config.mk b/mk/config.mk index d2afba663b4..ca04603195d 100644 --- a/mk/config.mk +++ b/mk/config.mk @@ -155,10 +155,15 @@ CFG_LIBUTILS_WITH_ISOC ?= y # With CFG_TA_FLOAT_SUPPORT enabled TA code is free use floating point types CFG_TA_FLOAT_SUPPORT ?= y -# Enable stack unwinding for aborts from kernel mode if CFG_TEE_CORE_DEBUG -# is enabled +# Stack unwinding: print a stack dump to the console on abort +# If CFG_UNWIND is enabled, both the kernel and user mode call stacks can be +# unwound (not paged TAs, however). +# Note that 32-bit ARM code needs unwind tables for this to work, so enabling +# this option will increase the size of the 32-bit TEE binary by a few KB. +# Similarly, TAs have to be compiled with -funwind-tables (default when the +# option is set) otherwise they can't be unwound. ifeq ($(CFG_TEE_CORE_DEBUG),y) -CFG_CORE_UNWIND ?= y +CFG_UNWIND ?= y endif # Enable support for dynamically loaded user TAs