1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (C) 2019, Linaro Limited 4 */ 5 6 /* 7 * APIs defined in this file are required to use __noprof attribute to 8 * avoid any circular dependency during profiling. So this requirement 9 * prohibits these APIs to use standard library APIs as those can be 10 * profiled too. 11 */ 12 13 #include <assert.h> 14 #include <user_ta_header.h> 15 #if defined(__KERNEL__) 16 #include <arm.h> 17 #include <kernel/panic.h> 18 #include <kernel/tee_ta_manager.h> 19 #include <kernel/thread.h> 20 #include <mm/core_mmu.h> 21 #else 22 #include <arm_user_sysreg.h> 23 #include <setjmp.h> 24 #include <utee_syscalls.h> 25 #endif 26 #include "ftrace.h" 27 28 #define DURATION_MAX_LEN 16 29 30 static const char hex_str[] = "0123456789abcdef"; 31 32 static __noprof struct ftrace_buf *get_fbuf(void) 33 { 34 #if defined(__KERNEL__) 35 short int ct = thread_get_id_may_fail(); 36 struct ts_session *s = NULL; 37 struct thread_specific_data *tsd = NULL; 38 39 if (ct == -1) 40 return NULL; 41 42 if (!(core_mmu_user_va_range_is_defined() && 43 core_mmu_user_mapping_is_active())) 44 return NULL; 45 46 tsd = thread_get_tsd(); 47 s = TAILQ_FIRST(&tsd->sess_stack); 48 49 if (!s || tsd->ctx != s->ctx) 50 return NULL; 51 52 if (s->fbuf && s->fbuf->syscall_trace_enabled && 53 !s->fbuf->syscall_trace_suspended) 54 return s->fbuf; 55 else 56 return NULL; 57 #else 58 return &__ftrace_buf_start; 59 #endif 60 } 61 62 #if defined(_CFG_FTRACE_BUF_WHEN_FULL_shift) 63 64 /* 65 * This API shifts/moves ftrace buffer to create space for new dump 66 * in case the buffer size falls short of actual dump. 67 */ 68 static bool __noprof fbuf_make_room(struct ftrace_buf *fbuf, size_t size) 69 { 70 char *dst = (char *)fbuf + fbuf->buf_off; 71 const char *src = (char *)fbuf + fbuf->buf_off + size; 72 size_t n = 0; 73 74 fbuf->curr_size -= size; 75 76 for (n = 0; n < fbuf->curr_size; n++) 77 dst[n] = src[n]; 78 79 return true; 80 } 81 82 #elif defined(_CFG_FTRACE_BUF_WHEN_FULL_wrap) 83 84 /* Makes room in the trace buffer by discarding the previously recorded data. */ 85 static bool __noprof fbuf_make_room(struct ftrace_buf *fbuf, 86 size_t size) 87 { 88 if (fbuf->buf_off + size > fbuf->max_size) 89 return false; 90 91 fbuf->curr_size = 0; 92 93 return true; 94 } 95 96 #elif defined(_CFG_FTRACE_BUF_WHEN_FULL_stop) 97 98 static bool __noprof fbuf_make_room(struct ftrace_buf *fbuf __unused, 99 size_t size __unused) 100 { 101 return false; 102 } 103 104 #else 105 #error CFG_FTRACE_BUF_WHEN_FULL value not supported 106 #endif 107 108 static size_t __noprof to_func_enter_fmt(char *buf, uint32_t ret_idx, 109 unsigned long pc) 110 { 111 char *str = buf; 112 uint32_t addr_size = 2 * sizeof(unsigned long); 113 uint32_t i = 0; 114 115 for (i = 0; i < (DURATION_MAX_LEN + ret_idx); i++) 116 if (i == (DURATION_MAX_LEN - 2)) 117 *str++ = '|'; 118 else 119 *str++ = ' '; 120 121 *str++ = '0'; 122 *str++ = 'x'; 123 124 for (i = 0; i < addr_size; i++) 125 *str++ = hex_str[(pc >> 4 * (addr_size - i - 1)) & 0xf]; 126 127 *str++ = '('; 128 *str++ = ')'; 129 *str++ = ' '; 130 *str++ = '{'; 131 *str++ = '\n'; 132 *str = '\0'; 133 134 return str - buf; 135 } 136 137 void __noprof ftrace_enter(unsigned long pc, unsigned long *lr) 138 { 139 struct ftrace_buf *fbuf = NULL; 140 size_t dump_size = 0; 141 bool full = false; 142 143 fbuf = get_fbuf(); 144 145 if (!fbuf || !fbuf->buf_off || !fbuf->max_size) 146 return; 147 148 dump_size = DURATION_MAX_LEN + fbuf->ret_idx + 149 (2 * sizeof(unsigned long)) + 8; 150 151 /* 152 * Check if we have enough space in ftrace buffer. If not then try to 153 * make room. 154 */ 155 full = (fbuf->curr_size + dump_size) > fbuf->max_size; 156 if (full) 157 full = !fbuf_make_room(fbuf, dump_size); 158 159 if (!full) 160 fbuf->curr_size += to_func_enter_fmt((char *)fbuf + 161 fbuf->buf_off + 162 fbuf->curr_size, 163 fbuf->ret_idx, 164 pc); 165 166 if (fbuf->ret_idx < FTRACE_RETFUNC_DEPTH) { 167 fbuf->ret_stack[fbuf->ret_idx] = *lr; 168 fbuf->begin_time[fbuf->ret_idx] = barrier_read_cntpct(); 169 fbuf->ret_idx++; 170 } else { 171 /* 172 * This scenario isn't expected as function call depth 173 * shouldn't be more than FTRACE_RETFUNC_DEPTH. 174 */ 175 #if defined(__KERNEL__) 176 panic(); 177 #else 178 _utee_panic(0); 179 #endif 180 } 181 182 *lr = (unsigned long)&__ftrace_return; 183 } 184 185 static void __noprof ftrace_duration(char *buf, uint64_t start, uint64_t end) 186 { 187 uint32_t max_us = CFG_FTRACE_US_MS; 188 uint32_t cntfrq = read_cntfrq(); 189 uint64_t ticks = end - start; 190 uint32_t ms = 0; 191 uint32_t us = 0; 192 uint32_t ns = 0; 193 uint32_t frac = 0; 194 uint32_t in = 0; 195 char unit = 'u'; 196 int i = 0; 197 198 ticks = ticks * 1000000000 / cntfrq; 199 us = ticks / 1000; 200 ns = ticks % 1000; 201 202 if (max_us && us >= max_us) { 203 /* Display value in milliseconds */ 204 unit = 'm'; 205 ms = us / 1000; 206 us = us % 1000; 207 frac = us; 208 in = ms; 209 } else { 210 /* Display value in microseconds */ 211 frac = ns; 212 in = us; 213 } 214 215 *buf-- = 's'; 216 *buf-- = unit; 217 *buf-- = ' '; 218 219 COMPILE_TIME_ASSERT(DURATION_MAX_LEN == 16); 220 if (in > 999999) { 221 /* Not enough space to print the value */ 222 for (i = 0; i < 10; i++) 223 *buf-- = '-'; 224 return; 225 } 226 227 for (i = 0; i < 3; i++) { 228 *buf-- = hex_str[frac % 10]; 229 frac /= 10; 230 } 231 232 *buf-- = '.'; 233 234 while (in) { 235 *buf-- = hex_str[in % 10]; 236 in /= 10; 237 } 238 } 239 240 unsigned long __noprof ftrace_return(void) 241 { 242 struct ftrace_buf *fbuf = NULL; 243 size_t dump_size = 0; 244 char *curr_buf = NULL; 245 char *dur_loc = NULL; 246 uint32_t i = 0; 247 248 fbuf = get_fbuf(); 249 250 /* Check for valid return index */ 251 if (fbuf && fbuf->ret_idx && fbuf->ret_idx <= FTRACE_RETFUNC_DEPTH) 252 fbuf->ret_idx--; 253 else 254 return 0; 255 256 curr_buf = (char *)fbuf + fbuf->buf_off + fbuf->curr_size; 257 258 /* 259 * Check for '{' symbol as it represents if it is an exit from current 260 * or nested function. If exit is from current function, than exit dump 261 * via ';' symbol else exit dump via '}' symbol. 262 */ 263 if (*(curr_buf - 2) == '{') { 264 *(curr_buf - 3) = ';'; 265 *(curr_buf - 2) = '\n'; 266 *(curr_buf - 1) = '\0'; 267 fbuf->curr_size -= 1; 268 269 dur_loc = curr_buf - (fbuf->ret_idx + 270 (2 * sizeof(unsigned long)) + 11); 271 ftrace_duration(dur_loc, fbuf->begin_time[fbuf->ret_idx], 272 barrier_read_cntpct()); 273 } else { 274 bool full = false; 275 276 dump_size = DURATION_MAX_LEN + fbuf->ret_idx + 3; 277 full = (fbuf->curr_size + dump_size) > fbuf->max_size; 278 if (full) 279 full = !fbuf_make_room(fbuf, dump_size); 280 281 if (!full) { 282 curr_buf = (char *)fbuf + fbuf->buf_off + 283 fbuf->curr_size; 284 285 for (i = 0; i < (DURATION_MAX_LEN + fbuf->ret_idx); i++) 286 if (i == (DURATION_MAX_LEN - 2)) 287 *curr_buf++ = '|'; 288 else 289 *curr_buf++ = ' '; 290 291 *curr_buf++ = '}'; 292 *curr_buf++ = '\n'; 293 *curr_buf = '\0'; 294 295 fbuf->curr_size += dump_size - 1; 296 297 dur_loc = curr_buf - fbuf->ret_idx - 6; 298 ftrace_duration(dur_loc, 299 fbuf->begin_time[fbuf->ret_idx], 300 barrier_read_cntpct()); 301 } 302 } 303 304 return fbuf->ret_stack[fbuf->ret_idx]; 305 } 306 307 #if !defined(__KERNEL__) 308 void __noprof ftrace_longjmp(unsigned int *ret_idx) 309 { 310 while (__ftrace_buf_start.ret_idx > *ret_idx) 311 ftrace_return(); 312 } 313 314 void __noprof ftrace_setjmp(unsigned int *ret_idx) 315 { 316 *ret_idx = __ftrace_buf_start.ret_idx; 317 } 318 #endif 319