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