1 // SPDX-License-Identifier: BSD-2-Clause 2 /*- 3 * Copyright (c) 2015 Linaro Limited 4 * Copyright (c) 2015 The FreeBSD Foundation 5 * All rights reserved. 6 * 7 * This software was developed by Semihalf under 8 * the sponsorship of the FreeBSD Foundation. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <arm.h> 33 #include <kernel/thread.h> 34 #include <kernel/unwind.h> 35 #include <kernel/tee_misc.h> 36 #include <kernel/tee_ta_manager.h> 37 #include <string.h> 38 #include <tee/tee_svc.h> 39 #include <trace.h> 40 #include <user_ta_header.h> 41 #include <util.h> 42 43 #include "unwind_private.h" 44 45 static void copy_in_reg(uint64_t *reg, vaddr_t addr) 46 { 47 memcpy(reg, (void *)addr, sizeof(*reg)); 48 } 49 50 #ifdef CFG_SYSCALL_FTRACE 51 static void ftrace_core_map_lr(uint64_t *lr) 52 { 53 struct ftrace_buf *fbuf = NULL; 54 struct tee_ta_session *s = NULL; 55 56 if (tee_ta_get_current_session(&s) != TEE_SUCCESS) 57 return; 58 59 if (!s->fbuf) 60 return; 61 62 fbuf = s->fbuf; 63 64 /* 65 * Function tracer inserts return hook (addr: &__ftrace_return) 66 * via modifying lr values in the stack frames. And during aborts, 67 * stack trace picks these modified lr values which needs to be 68 * replaced with original lr value. So here we use the ftrace return 69 * stack to retrieve original lr value but we need to first check if 70 * it has actually been modified or not in case TA is profiled 71 * partially. 72 */ 73 if ((*lr == (uint64_t)&__ftrace_return) && 74 fbuf->lr_idx < fbuf->ret_idx) { 75 fbuf->lr_idx++; 76 *lr = fbuf->ret_stack[fbuf->ret_idx - fbuf->lr_idx]; 77 } 78 } 79 #else 80 static void ftrace_core_map_lr(uint64_t *lr __unused) 81 { 82 } 83 #endif 84 85 bool unwind_stack_arm64(struct unwind_state_arm64 *frame, 86 vaddr_t stack, size_t stack_size) 87 { 88 vaddr_t fp = frame->fp; 89 90 if (!core_is_buffer_inside(fp, sizeof(uint64_t) * 3, 91 stack, stack_size)) 92 return false; 93 94 frame->sp = fp + 0x10; 95 /* FP to previous frame (X29) */ 96 copy_in_reg(&frame->fp, fp); 97 /* LR (X30) */ 98 copy_in_reg(&frame->pc, fp + 8); 99 100 ftrace_core_map_lr(&frame->pc); 101 102 frame->pc -= 4; 103 104 return true; 105 } 106 107 #if (TRACE_LEVEL > 0) 108 109 void print_stack_arm64(int level, struct unwind_state_arm64 *state, 110 vaddr_t stack, size_t stack_size) 111 { 112 trace_printf_helper_raw(level, true, "Call stack:"); 113 114 do { 115 trace_printf_helper_raw(level, true, " 0x%016" PRIx64, 116 state->pc); 117 } while (unwind_stack_arm64(state, stack, stack_size)); 118 } 119 120 void print_kernel_stack(int level) 121 { 122 struct unwind_state_arm64 state; 123 uaddr_t stack = thread_stack_start(); 124 size_t stack_size = thread_stack_size(); 125 126 memset(&state, 0, sizeof(state)); 127 state.pc = read_pc(); 128 state.fp = read_fp(); 129 130 print_stack_arm64(level, &state, stack, stack_size); 131 } 132 133 #endif 134 135 vaddr_t *unw_get_kernel_stack(void) 136 { 137 size_t n = 0; 138 size_t size = 0; 139 vaddr_t *tmp = NULL; 140 vaddr_t *addr = NULL; 141 struct unwind_state_arm64 state = { 0 }; 142 uaddr_t stack = thread_stack_start(); 143 size_t stack_size = thread_stack_size(); 144 145 state.pc = read_pc(); 146 state.fp = read_fp(); 147 148 while (unwind_stack_arm64(&state, stack, stack_size)) { 149 tmp = unw_grow(addr, &size, (n + 1) * sizeof(vaddr_t)); 150 if (!tmp) 151 goto err; 152 addr = tmp; 153 addr[n] = state.pc; 154 n++; 155 } 156 157 if (addr) { 158 tmp = unw_grow(addr, &size, (n + 1) * sizeof(vaddr_t)); 159 if (!tmp) 160 goto err; 161 addr = tmp; 162 addr[n] = 0; 163 } 164 165 return addr; 166 err: 167 EMSG("Out of memory"); 168 free(addr); 169 return NULL; 170 } 171