| // SPDX-License-Identifier: BSD-2-Clause |
| /*- |
| * Copyright (c) 2015 Linaro Limited |
| * Copyright (c) 2015 The FreeBSD Foundation |
| * All rights reserved. |
| * |
| * This software was developed by Semihalf under |
| * the sponsorship of the FreeBSD Foundation. |
| * |
| * 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 AUTHOR 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 AUTHOR 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. |
| */ |
| |
| #include <arm.h> |
| #include <kernel/linker.h> |
| #include <kernel/tee_misc.h> |
| #include <kernel/tee_ta_manager.h> |
| #include <kernel/thread.h> |
| #include <kernel/unwind.h> |
| #include <string.h> |
| #include <tee/tee_svc.h> |
| #include <trace.h> |
| #include <user_ta_header.h> |
| #include <util.h> |
| |
| #include "unwind_private.h" |
| |
| static void copy_in_reg(uint64_t *reg, vaddr_t addr) |
| { |
| memcpy(reg, (void *)addr, sizeof(*reg)); |
| } |
| |
| #ifdef CFG_SYSCALL_FTRACE |
| static void ftrace_core_map_lr(uint64_t *lr) |
| { |
| struct ftrace_buf *fbuf = NULL; |
| struct tee_ta_session *s = NULL; |
| |
| if (tee_ta_get_current_session(&s) != TEE_SUCCESS) |
| return; |
| |
| if (!s->fbuf) |
| return; |
| |
| fbuf = s->fbuf; |
| |
| /* |
| * Function tracer inserts return hook (addr: &__ftrace_return) |
| * via modifying lr values in the stack frames. And during aborts, |
| * stack trace picks these modified lr values which needs to be |
| * replaced with original lr value. So here we use the ftrace return |
| * stack to retrieve original lr value but we need to first check if |
| * it has actually been modified or not in case TA is profiled |
| * partially. |
| */ |
| if ((*lr == (uint64_t)&__ftrace_return) && |
| fbuf->lr_idx < fbuf->ret_idx) { |
| fbuf->lr_idx++; |
| *lr = fbuf->ret_stack[fbuf->ret_idx - fbuf->lr_idx]; |
| } |
| } |
| #else |
| static void ftrace_core_map_lr(uint64_t *lr __unused) |
| { |
| } |
| #endif |
| |
| bool unwind_stack_arm64(struct unwind_state_arm64 *frame, |
| vaddr_t stack, size_t stack_size) |
| { |
| vaddr_t fp = frame->fp; |
| |
| if (!core_is_buffer_inside(fp, sizeof(uint64_t) * 3, |
| stack, stack_size)) |
| return false; |
| |
| frame->sp = fp + 0x10; |
| /* FP to previous frame (X29) */ |
| copy_in_reg(&frame->fp, fp); |
| /* LR (X30) */ |
| copy_in_reg(&frame->pc, fp + 8); |
| |
| ftrace_core_map_lr(&frame->pc); |
| |
| frame->pc -= 4; |
| |
| return true; |
| } |
| |
| #if (TRACE_LEVEL > 0) |
| |
| void print_stack_arm64(int level, struct unwind_state_arm64 *state, |
| vaddr_t stack, size_t stack_size) |
| { |
| trace_printf_helper_raw(level, true, "TEE load address @ %#"PRIxVA, |
| VCORE_START_VA); |
| trace_printf_helper_raw(level, true, "Call stack:"); |
| |
| do { |
| trace_printf_helper_raw(level, true, " 0x%016" PRIx64, |
| state->pc); |
| } while (unwind_stack_arm64(state, stack, stack_size)); |
| } |
| |
| void print_kernel_stack(int level) |
| { |
| struct unwind_state_arm64 state; |
| uaddr_t stack = thread_stack_start(); |
| size_t stack_size = thread_stack_size(); |
| |
| memset(&state, 0, sizeof(state)); |
| state.pc = read_pc(); |
| state.fp = read_fp(); |
| |
| print_stack_arm64(level, &state, stack, stack_size); |
| } |
| |
| #endif |
| |
| vaddr_t *unw_get_kernel_stack(void) |
| { |
| size_t n = 0; |
| size_t size = 0; |
| vaddr_t *tmp = NULL; |
| vaddr_t *addr = NULL; |
| struct unwind_state_arm64 state = { 0 }; |
| uaddr_t stack = thread_stack_start(); |
| size_t stack_size = thread_stack_size(); |
| |
| state.pc = read_pc(); |
| state.fp = read_fp(); |
| |
| while (unwind_stack_arm64(&state, stack, stack_size)) { |
| tmp = unw_grow(addr, &size, (n + 1) * sizeof(vaddr_t)); |
| if (!tmp) |
| goto err; |
| addr = tmp; |
| addr[n] = state.pc; |
| n++; |
| } |
| |
| if (addr) { |
| tmp = unw_grow(addr, &size, (n + 1) * sizeof(vaddr_t)); |
| if (!tmp) |
| goto err; |
| addr = tmp; |
| addr[n] = 0; |
| } |
| |
| return addr; |
| err: |
| EMSG("Out of memory"); |
| free(addr); |
| return NULL; |
| } |