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 
get_fbuf(void)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  */
fbuf_make_room(struct ftrace_buf * fbuf,size_t size)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. */
fbuf_make_room(struct ftrace_buf * fbuf,size_t size)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 
fbuf_make_room(struct ftrace_buf * fbuf __unused,size_t size __unused)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 
to_func_enter_fmt(char * buf,uint32_t ret_idx,unsigned long pc)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 
ftrace_enter(unsigned long pc,unsigned long * lr)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_counter_timer();
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 
ftrace_duration(char * buf,uint64_t start,uint64_t end)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 
ftrace_return(void)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_counter_timer());
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_counter_timer());
301 		}
302 	}
303 
304 	return fbuf->ret_stack[fbuf->ret_idx];
305 }
306 
307 #if !defined(__KERNEL__)
ftrace_longjmp(unsigned int * ret_idx)308 void __noprof ftrace_longjmp(unsigned int *ret_idx)
309 {
310 	while (__ftrace_buf_start.ret_idx > *ret_idx)
311 		ftrace_return();
312 }
313 
ftrace_setjmp(unsigned int * ret_idx)314 void __noprof ftrace_setjmp(unsigned int *ret_idx)
315 {
316 	*ret_idx = __ftrace_buf_start.ret_idx;
317 }
318 #endif
319