1 /*
2  * Copyright (c) 2016, Linaro Limited
3  * SPDX-License-Identifier: BSD-2-Clause
4  */
5 
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <inttypes.h>
9 #include <limits.h>
10 #include <libgen.h>
11 #include <pthread.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include <stdbool.h>
15 #include <stdlib.h>
16 #include <sys/types.h>
17 #include <sys/mman.h>
18 #include <sys/stat.h>
19 #include <sys/wait.h>
20 #include <unistd.h>
21 #include <yaml.h>
22 
23 #include "benchmark_aux.h"
24 #include "common.h"
25 
26 #define MAX_SCALAR	20
27 static struct tee_ts_global *bench_ts_global;
28 
29 static const TEEC_UUID pta_benchmark_uuid = PTA_BENCHMARK_UUID;
30 static TEEC_Context ctx;
31 static TEEC_Session sess;
32 
33 static sig_atomic_t is_running;
34 static yaml_emitter_t emitter;
35 
36 struct consumer_param {
37 	pid_t child_pid;
38 	char *ts_filepath;
39 };
sigint_handler(int data)40 void sigint_handler(int data)
41 {
42 	(void)data;
43 
44 	is_running = 0;
45 }
46 
open_bench_pta(void)47 static void open_bench_pta(void)
48 {
49 	TEEC_Result res;
50 	uint32_t err_origin;
51 
52 	res = TEEC_InitializeContext(NULL, &ctx);
53 	tee_check_res(res, "TEEC_InitializeContext");
54 
55 	res = TEEC_OpenSession(&ctx, &sess, &pta_benchmark_uuid,
56 			TEEC_LOGIN_PUBLIC, NULL, NULL, &err_origin);
57 	tee_check_res(res, "TEEC_OpenSession");
58 }
59 
close_bench_pta(void)60 static void close_bench_pta(void)
61 {
62 	/* release benchmark timestamp shm */
63 	TEEC_CloseSession(&sess);
64 	TEEC_FinalizeContext(&ctx);
65 }
66 
alloc_bench_buf(uint32_t cores)67 static void alloc_bench_buf(uint32_t cores)
68 {
69 	TEEC_Result res;
70 	TEEC_Operation op = { 0 };
71 	uint32_t ret_orig;
72 	intptr_t paddr_ts_buf = 0;
73 	size_t size;
74 
75 	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT,
76 			TEEC_VALUE_INPUT, TEEC_NONE, TEEC_NONE);
77 
78 	op.params[1].value.a = cores;
79 
80 	res = TEEC_InvokeCommand(&sess, BENCHMARK_CMD_REGISTER_MEMREF,
81 					&op, &ret_orig);
82 	tee_check_res(res, "TEEC_InvokeCommand");
83 
84 	paddr_ts_buf = op.params[0].value.a;
85 	size = op.params[0].value.b;
86 
87 	INFO("ts buffer paddr = %" PRIiPTR ", size = %zu\n", paddr_ts_buf, size);
88 	if (paddr_ts_buf) {
89 
90 		bench_ts_global = mmap_paddr(paddr_ts_buf, size);
91 		if (!bench_ts_global)
92 			ERROR_EXIT("Failed to allocate timestamp buffer");
93 	} else {
94 		ERROR_EXIT("Failed to allocate timestamp buffer");
95 	}
96 }
97 
free_bench_buf(void)98 static void free_bench_buf(void)
99 {
100 	TEEC_Result res;
101 	TEEC_Operation op = { 0 };
102 	uint32_t ret_orig;
103 
104 	DBG("Freeing benchmark buffer.");
105 	op.paramTypes = TEEC_PARAM_TYPES(TEEC_NONE,
106 					TEEC_NONE, TEEC_NONE, TEEC_NONE);
107 
108 	res = TEEC_InvokeCommand(&sess, BENCHMARK_CMD_UNREGISTER,
109 					&op, &ret_orig);
110 	tee_check_res(res, "TEEC_InvokeCommand");
111 }
112 
usage(char * progname)113 static void usage(char *progname)
114 {
115 	fprintf(stderr, "Call latency benchmark tool for OP-TEE\n\n");
116 	fprintf(stderr, "Usage:\n");
117 	fprintf(stderr, "  %s -h\n", progname);
118 	fprintf(stderr, "  %s host_app [host_app_args]\n", progname);
119 	fprintf(stderr, "Options:\n");
120 	fprintf(stderr, "  -h              Print this help and exit\n");
121 	fprintf(stderr, "  host_app        Path to host app to benchmark\n");
122 	fprintf(stderr, "  host_app_args   Original host app args\n");
123 }
124 
timestamp_pop(struct tee_ts_cpu_buf * cpu_buf,struct tee_time_st * ts)125 static int timestamp_pop(struct tee_ts_cpu_buf *cpu_buf,
126 			 struct tee_time_st *ts)
127 {
128 	uint64_t ts_tail;
129 
130 	if (!cpu_buf && !ts)
131 		return RING_BADPARM;
132 
133 	if (cpu_buf->tail >= cpu_buf->head)
134 		return RING_NODATA;
135 
136 	ts_tail = cpu_buf->tail++;
137 	*ts = cpu_buf->stamps[ts_tail & TEE_BENCH_MAX_MASK];
138 
139 	return 0;
140 }
141 
init_emitter(FILE * ts_file)142 static bool init_emitter(FILE *ts_file)
143 {
144 	yaml_event_t event;
145 
146 	if (!yaml_emitter_initialize(&emitter))
147 		ERROR_RETURN_FALSE("Can't initialize YAML emitter");
148 
149 	yaml_emitter_set_canonical(&emitter, 0);
150 	yaml_emitter_set_unicode(&emitter, 1);
151 	yaml_emitter_set_output_file(&emitter, ts_file);
152 
153 	/* Stream start */
154 	if (!yaml_stream_start_event_initialize(&event, YAML_UTF8_ENCODING))
155 		ERROR_GOTO(emitter_delete,
156 			"Failed to initialize YAML stream start event");
157 	if (!yaml_emitter_emit(&emitter, &event))
158 		ERROR_GOTO(emitter_delete,
159 			"Failed to emit YAML stream start event");
160 
161 	/* Document start */
162 	if (!yaml_document_start_event_initialize(&event,
163 			NULL, NULL, NULL, YAML_IMPLICIT))
164 		ERROR_GOTO(emitter_delete,
165 			"Failed to initialize YAML document start event");
166 	if (!yaml_emitter_emit(&emitter, &event))
167 		ERROR_GOTO(emitter_delete,
168 			"Failed to emit YAML doc start event");
169 
170 	/* Mapping start */
171 	if (!yaml_mapping_start_event_initialize(&event,
172 				NULL, NULL , YAML_IMPLICIT,
173 				YAML_ANY_MAPPING_STYLE))
174 		ERROR_GOTO(emitter_delete,
175 			"Failed to initialize YAML mapping start event");
176 	if (!yaml_emitter_emit(&emitter, &event))
177 		ERROR_GOTO(emitter_delete,
178 			"Failed to emit YAML sequence mapping event");
179 	/* Key timestamps */
180 	yaml_scalar_event_initialize(&event, NULL, NULL,
181 		(yaml_char_t *)"timestamps", -1, 1, 1, YAML_PLAIN_SCALAR_STYLE);
182 	if (!yaml_emitter_emit(&emitter, &event))
183 		ERROR_RETURN_FALSE("Failed to emit YAML scalar");
184 
185 	/* Sequence start */
186 	if (!yaml_sequence_start_event_initialize(&event,
187 				NULL, NULL , YAML_IMPLICIT,
188 				YAML_ANY_SEQUENCE_STYLE))
189 		ERROR_GOTO(emitter_delete,
190 			"Failed to initialize YAML sequence start event");
191 	if (!yaml_emitter_emit(&emitter, &event))
192 		ERROR_GOTO(emitter_delete,
193 			"Failed to emit YAML sequence start event");
194 
195 	return true;
196 emitter_delete:
197 	yaml_emitter_delete(&emitter);
198 	return false;
199 }
200 
deinit_emitter()201 static void deinit_emitter()
202 {
203 	yaml_event_t event;
204 
205 	/* Sequence cmd */
206 	if (!yaml_sequence_end_event_initialize(&event))
207 		ERROR_GOTO(emitter_delete,
208 			"Failed to initialize YAML sequence end event");
209 	if (!yaml_emitter_emit(&emitter, &event))
210 		ERROR_GOTO(emitter_delete,
211 			"Failed to emit YAML sequence end event");
212 
213 	/* Mapping end */
214 	if (!yaml_mapping_end_event_initialize(&event))
215 		ERROR_GOTO(emitter_delete,
216 			"Failed to initialize YAML mapping end event");
217 	if (!yaml_emitter_emit(&emitter, &event))
218 		ERROR_GOTO(emitter_delete,
219 			"Failed to emit YAML mapping end event");
220 
221 	/* Document end */
222 	if (!yaml_document_end_event_initialize(&event, 0))
223 		ERROR_GOTO(emitter_delete,
224 			"Failed to initialize YAML document end event");
225 	if (!yaml_emitter_emit(&emitter, &event))
226 		ERROR_GOTO(emitter_delete, "Failed to emit YAML doc end event");
227 
228 	/* Stream end */
229 	if (!yaml_stream_end_event_initialize(&event))
230 		ERROR_GOTO(emitter_delete,
231 			"Failed to initialise YAML stream end event");
232 	if (!yaml_emitter_emit(&emitter, &event))
233 		ERROR_GOTO(emitter_delete,
234 			"Failed to emit YAML stream end event");
235 
236 emitter_delete:
237 	yaml_emitter_delete(&emitter);
238 }
239 
fill_map(char * var,char * value)240 static bool fill_map(char *var, char *value)
241 {
242 	yaml_event_t event;
243 
244 	yaml_scalar_event_initialize(&event, NULL, NULL,
245 		(yaml_char_t *)var, -1, 1, 1, YAML_PLAIN_SCALAR_STYLE);
246 	if (!yaml_emitter_emit(&emitter, &event))
247 		ERROR_RETURN_FALSE("Failed to emit YAML scalar");
248 
249 	yaml_scalar_event_initialize(&event, NULL, NULL,
250 		(yaml_char_t *)value, -1, 1, 1, YAML_PLAIN_SCALAR_STYLE);
251 	if (!yaml_emitter_emit(&emitter, &event))
252 		ERROR_RETURN_FALSE("Failed to emit YAML scalar");
253 
254 	return true;
255 }
256 
fill_timestamp(uint32_t core,uint64_t count,uint64_t addr,const char * subsystem)257 static bool fill_timestamp(uint32_t core, uint64_t count, uint64_t addr,
258 							const char *subsystem)
259 {
260 	yaml_event_t event;
261 	char data[MAX_SCALAR];
262 
263 	/* Mapping start */
264 	if (!yaml_mapping_start_event_initialize(&event,
265 				NULL, NULL , YAML_IMPLICIT,
266 				YAML_ANY_MAPPING_STYLE))
267 		ERROR_RETURN_FALSE(
268 			"Failed to initialize YAML mapping start event");
269 	if (!yaml_emitter_emit(&emitter, &event))
270 		ERROR_RETURN_FALSE("Failed to emit YAML mapping start event");
271 
272 	snprintf(data, MAX_SCALAR, "%" PRIu32, core);
273 	fill_map("core", data);
274 
275 	snprintf(data, MAX_SCALAR, "%" PRIu64, count);
276 	fill_map("counter", data);
277 
278 	snprintf(data, MAX_SCALAR, "0x%" PRIx64, addr);
279 	fill_map("address", data);
280 
281 	snprintf(data, MAX_SCALAR, "%s", subsystem);
282 	fill_map("component", data);
283 
284 	/* Mapping end */
285 	if (!yaml_mapping_end_event_initialize(&event))
286 		ERROR_RETURN_FALSE(
287 			"Failed to initialize YAML mapping end event");
288 	if (!yaml_emitter_emit(&emitter, &event))
289 		ERROR_RETURN_FALSE("Failed to emit YAML mapping end event");
290 
291 	return true;
292 }
293 
294 /*
295  * Consume all timestamps from per-cpu ringbuffers and put everything into
296  * the yaml file.
297  */
ts_consumer(void * arg)298 static void *ts_consumer(void *arg)
299 {
300 	unsigned int i;
301 	int ret;
302 	bool ts_received = false;
303 	uint32_t cores;
304 	struct tee_time_st ts_data;
305 	FILE *ts_file;
306 	struct consumer_param *prm = (struct consumer_param *)arg;
307 	char *tsfile_path = prm->ts_filepath;
308 	pid_t child_pid = prm->child_pid;
309 	size_t teec_dyn_addr = 0;
310 
311 	if (!tsfile_path)
312 		ERROR_GOTO(exit, "Wrong timestamp file path");
313 
314 	cores = get_cores();
315 	if (!cores)
316 		ERROR_GOTO(exit, "Can't receive amount of avalable cores");
317 
318 	ts_file = fopen(tsfile_path, "w");
319 	if (!ts_file)
320 		ERROR_GOTO(exit, "Can't open timestamp file");
321 
322 	if (!init_emitter(ts_file))
323 		ERROR_GOTO(file_close,
324 			"Error occurred in emitter initialization");
325 
326 	while (is_running) {
327 		ts_received = false;
328 		for (i = 0; i < cores; i++) {
329 			ret = timestamp_pop(&bench_ts_global->cpu_buf[i],
330 						&ts_data);
331 			if (!ret) {
332 				ts_received = true;
333 				DBG("Timestamp: core = %u; tick = %lld; "
334 					"pc = 0x%" PRIx64 "; system = %s",
335 					i, ts_data.cnt, ts_data.addr,
336 					bench_str_src(ts_data.src));
337 				do {
338 					teec_dyn_addr = get_library_load_offset
339 						(child_pid,
340 						LIBTEEC_NAME);
341 
342 				} while (!teec_dyn_addr && is_running);
343 				if (ts_data.src == TEE_BENCH_CLIENT) {
344 					DBG("ts_addr = %llx, teec_addr = %x",
345 						ts_data.addr, teec_dyn_addr);
346 					ts_data.addr -= teec_dyn_addr;
347 				}
348 				if (!fill_timestamp(i, ts_data.cnt,
349 					ts_data.addr,
350 					bench_str_src(ts_data.src)))
351 					ERROR_GOTO(deinit_yaml,
352 					"Adding timestamp failed");
353 
354 			}
355 		}
356 
357 		if (!ts_received) {
358 			if (is_running) {
359 				DBG("yielding...");
360 				sched_yield();
361 			} else {
362 				ERROR_GOTO(deinit_yaml,
363 					"No new data in the per-cpu ringbuffers"
364 					);
365 			}
366 		}
367 	}
368 
369 deinit_yaml:
370 	deinit_emitter();
371 file_close:
372 	fclose(ts_file);
373 exit:
374 	return NULL;
375 }
376 
main(int argc,char * argv[])377 int main(int argc, char *argv[])
378 {
379 	int i;
380 	int status;
381 	pid_t pid;
382 	char testapp_path[PATH_MAX];
383 	char **testapp_argv;
384 	char *res;
385 	char *tsfile_path;
386 	uint32_t cores;
387 	pthread_t consumer_thread;
388 	struct consumer_param prm;
389 
390 	if (argc == 1) {
391 		usage(argv[0]);
392 		return 0;
393 	}
394 
395 	/* Parse command line */
396 	for (i = 1; i < argc; i++) {
397 		if (!strcmp(argv[i], "-h")) {
398 			usage(argv[0]);
399 			return 0;
400 		}
401 	}
402 
403 	signal(SIGINT, sigint_handler);
404 
405 	INFO("1. Opening Benchmark Static TA...");
406 	open_bench_pta();
407 
408 	cores = get_cores();
409 	if (!cores)
410 		ERROR_EXIT("Receiving amount of active cores failed");
411 
412 	INFO("2. Allocating per-core buffers, cores detected = %d",
413 					cores);
414 	alloc_bench_buf(cores);
415 
416 	res = realpath(argv[1], testapp_path);
417 	if (!res)
418 		tee_errx("Failed to get realpath", EXIT_FAILURE);
419 
420 	alloc_argv(argc, argv, &testapp_argv);
421 
422 	INFO("3. Starting origin host app %s ...", testapp_path);
423 
424 	/* fork/exec here */
425 	pid = fork();
426 
427 	if (pid == -1) {
428 		DBG("fork() failed");
429 		ERROR_EXIT("Starting origin host application failed.");
430 	} else if (pid > 0) {
431 		is_running = 1;
432 		tsfile_path = malloc(strlen(testapp_path) +
433 					strlen(TSFILE_NAME_SUFFIX) + 1);
434 		if (!tsfile_path)
435 			ERROR_EXIT("Memory allocation failed the file path.");
436 
437 		tsfile_path[0] = '\0';
438 		strcat(tsfile_path, testapp_path);
439 		strcat(tsfile_path, TSFILE_NAME_SUFFIX);
440 
441 		INFO("Dumping timestamps to %s ...", tsfile_path);
442 		print_line();
443 
444 		prm.child_pid = pid;
445 		prm.ts_filepath = tsfile_path;
446 
447 		if (pthread_create(&consumer_thread, NULL,
448 				ts_consumer, &prm)) {
449 			DBG( "Error creating ts consumer thread");
450 			ERROR_EXIT("Can't start process of reading timestamps");
451 		}
452 		/* wait for child app exits */
453 		waitpid(pid, &status, 0);
454 		DBG("Origin host application finished executing");
455 
456 		is_running = 0;
457 
458 		/* wait for our consumer thread terminate */
459 		if (pthread_join(consumer_thread, NULL)) {
460 			DBG("Error joining thread");
461 			ERROR_EXIT("Couldn't start consuming timestamps");
462 		}
463 	}
464 	else {
465 		execvp(testapp_path, testapp_argv);
466 		DBG("execve() failed");
467 		ERROR_EXIT("Starting origin host application failed");
468 	}
469 
470 	INFO("4. Done benchmark");
471 
472 	dealloc_argv(argc-1, testapp_argv);
473 	free_bench_buf();
474 	close_bench_pta();
475 	return 0;
476 }
477