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 
get_fbuf(void)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  */
fbuf_make_room(struct ftrace_buf * fbuf,size_t size)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. */
fbuf_make_room(struct ftrace_buf * fbuf,size_t size)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 
fbuf_make_room(struct ftrace_buf * fbuf __unused,size_t size __unused)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 
to_func_enter_fmt(char * buf,uint32_t ret_idx,unsigned long pc)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 
ftrace_enter(unsigned long pc,unsigned long * lr)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 
ftrace_duration(char * buf,uint64_t start,uint64_t end)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 
ftrace_return(void)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__)
ftrace_longjmp(unsigned int * ret_idx)319 void __noprof ftrace_longjmp(unsigned int *ret_idx)
320 {
321 	while (__ftrace_buf_start.ret_idx > *ret_idx)
322 		ftrace_return();
323 }
324 
ftrace_setjmp(unsigned int * ret_idx)325 void __noprof ftrace_setjmp(unsigned int *ret_idx)
326 {
327 	*ret_idx = __ftrace_buf_start.ret_idx;
328 }
329 #endif
330