From 31a2964210f0d04eb6e8d940c445a3d0c9c8705f Mon Sep 17 00:00:00 2001 From: Jerome Forissier Date: Thu, 11 May 2017 15:28:31 +0200 Subject: [PATCH] core: dump call stack of user TAs on abort Update the abort handling code in the TEE core to support unwinding the user mode stack in addition to the kernel stack. unwind_arm32.c is modified slightly so that it can be built for AArch64. This allows a 64-bit TEE core to dump both 32- and 64-bit TAs. Paged TAs (CFG_PAGED_USER_TA=y) cannot currently be unwound, because the code is not ready to handle the page faults that might occur as the unwinding tables are accessed. CFG_CORE_UNWIND is renamed to CFG_UNWIND since it enables both the kernel and user TA stack dumps. It is still set automatically when CFG_TEE_CORE_DEBUG=y. 32-bit user TAs have to be compiled with `-funwind-tables`, otherwise the call stack can't be unwound and the abort reports will not show a call stack .The TA dev kit takes care of adding this flag automatically when CFG_UNWIND=y. Signed-off-by: Jerome Forissier Tested-by: Jerome Forissier (HiKey) Reviewed-by: Jens Wiklander --- core/arch/arm/arm.mk | 5 +- core/arch/arm/include/kernel/abort.h | 2 + core/arch/arm/include/kernel/thread.h | 10 +- core/arch/arm/include/kernel/unwind.h | 34 ++-- core/arch/arm/include/kernel/user_ta.h | 2 + core/arch/arm/kernel/abort.c | 226 +++++++++++++++++------ core/arch/arm/kernel/elf_common.h | 1 + core/arch/arm/kernel/elf_load.c | 23 +-- core/arch/arm/kernel/elf_load.h | 3 +- core/arch/arm/kernel/sub.mk | 4 +- core/arch/arm/kernel/thread.c | 12 +- core/arch/arm/kernel/thread_a64.S | 2 + core/arch/arm/kernel/unwind_arm32.c | 65 +++---- core/arch/arm/kernel/unwind_arm64.c | 15 +- core/arch/arm/kernel/user_ta.c | 18 +- lib/libutee/arch/arm/user_ta_entry.c | 8 + lib/libutils/ext/arch/arm/aeabi_unwind.c | 42 +++++ lib/libutils/ext/arch/arm/sub.mk | 1 + lib/libutils/ext/include/trace.h | 12 +- mk/config.mk | 11 +- 20 files changed, 347 insertions(+), 149 deletions(-) create mode 100644 lib/libutils/ext/arch/arm/aeabi_unwind.c 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