1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2019, Linaro Limited
4  */
5 
6 #include <dirent.h>
7 #include <err.h>
8 #include <errno.h>
9 #include <fnmatch.h>
10 #include <inttypes.h>
11 #include <pta_stats.h>
12 #include <stdbool.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/types.h>
18 #include <tee_client_api.h>
19 #include <unistd.h>
20 #include "xtest_helpers.h"
21 #include "xtest_test.h"
22 #include "stats.h"
23 
usage(void)24 static int usage(void)
25 {
26 	fprintf(stderr, "Usage: %s --stats [OPTION]\n", xtest_progname);
27 	fprintf(stderr, "Displays statistics from OP-TEE\n");
28 	fprintf(stderr, "Options:\n");
29 	fprintf(stderr, " -h|--help      Print this help and exit\n");
30 	fprintf(stderr, " --pager        Print pager statistics\n");
31 	fprintf(stderr, " --alloc        Print allocation statistics\n");
32 	fprintf(stderr, " --memleak      Dump memory leak data on secure console\n");
33 	fprintf(stderr, " --ta           Print loaded TAs context\n");
34 	fprintf(stderr, " --time         Print REE and TEE time\n");
35 	fprintf(stderr, " --clocks       Dump clock tree on secure console\n");
36 	fprintf(stderr, " --regulators   Dump regulator tree on secure console\n");
37 
38 	return EXIT_FAILURE;
39 }
40 
open_sess(TEEC_Context * ctx,TEEC_Session * sess)41 static void open_sess(TEEC_Context *ctx, TEEC_Session *sess)
42 {
43 	TEEC_UUID uuid = STATS_UUID;
44 	TEEC_Result res = TEEC_ERROR_GENERIC;
45 	uint32_t eo = 0;
46 
47 	res = TEEC_InitializeContext(NULL, ctx);
48 	if (res)
49 		errx(EXIT_FAILURE, "TEEC_InitializeContext: %#"PRIx32, res);
50 
51 	res = TEEC_OpenSession(ctx, sess, &uuid, TEEC_LOGIN_PUBLIC, NULL,
52 			       NULL, &eo);
53 	if (res)
54 		errx(EXIT_FAILURE,
55 		     "TEEC_OpenSession: res %#"PRIx32" err_orig %#"PRIx32,
56 			res, eo);
57 }
58 
close_sess(TEEC_Context * ctx,TEEC_Session * sess)59 static int close_sess(TEEC_Context *ctx, TEEC_Session *sess)
60 {
61 	TEEC_CloseSession(sess);
62 	TEEC_FinalizeContext(ctx);
63 
64 	return EXIT_SUCCESS;
65 }
66 
stat_pager(int argc,char * argv[])67 static int stat_pager(int argc, char *argv[])
68 {
69 	TEEC_Context ctx = { };
70 	TEEC_Session sess = { };
71 	TEEC_Result res = TEEC_ERROR_GENERIC;
72 	uint32_t eo = 0;
73 	TEEC_Operation op = { };
74 
75 	UNUSED(argv);
76 	if (argc != 1)
77 		return usage();
78 
79 	open_sess(&ctx, &sess);
80 
81 	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_OUTPUT, TEEC_VALUE_OUTPUT,
82 					 TEEC_VALUE_OUTPUT, TEEC_NONE);
83 
84 	res = TEEC_InvokeCommand(&sess, STATS_CMD_PAGER_STATS, &op, &eo);
85 	if (res)
86 		errx(EXIT_FAILURE,
87 		     "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
88 		     res, eo);
89 
90 	printf("Pager statistics (Number of):\n");
91 	printf("Unlocked pages:     %"PRId32"\n", op.params[0].value.a);
92 	printf("Page pool size:     %"PRId32"\n", op.params[0].value.b);
93 	printf("R/O faults:         %"PRId32"\n", op.params[1].value.a);
94 	printf("R/W faults:         %"PRId32"\n", op.params[1].value.b);
95 	printf("Hidden faults:      %"PRId32"\n", op.params[2].value.a);
96 	printf("Zi pages released:  %"PRId32"\n", op.params[2].value.b);
97 
98 	return close_sess(&ctx, &sess);
99 }
100 
stat_alloc(int argc,char * argv[])101 static int stat_alloc(int argc, char *argv[])
102 {
103 	TEEC_Context ctx = { };
104 	TEEC_Session sess = { };
105 	TEEC_Result res = TEEC_ERROR_GENERIC;
106 	uint32_t eo = 0;
107 	TEEC_Operation op = { };
108 	struct pta_stats_alloc *stats = NULL;
109 	size_t stats_size_bytes = 0;
110 	size_t n = 0;
111 
112 	UNUSED(argv);
113 	if (argc != 1)
114 		return usage();
115 
116 	open_sess(&ctx, &sess);
117 
118 	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT,
119 					 TEEC_MEMREF_TEMP_OUTPUT,
120 					 TEEC_NONE, TEEC_NONE);
121 	res = TEEC_InvokeCommand(&sess, STATS_CMD_ALLOC_STATS, &op, &eo);
122 	if (res != TEEC_ERROR_SHORT_BUFFER)
123 		errx(EXIT_FAILURE,
124 		     "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
125 		     res, eo);
126 
127 	stats_size_bytes = op.params[1].tmpref.size;
128 	if (stats_size_bytes % sizeof(*stats))
129 		errx(EXIT_FAILURE,
130 		     "STATS_CMD_PAGER_STATS: %zu not a multiple of %zu",
131 		     stats_size_bytes, sizeof(*stats));
132 	stats = calloc(1, stats_size_bytes);
133 	if (!stats)
134 		err(EXIT_FAILURE, "calloc(1, %zu)", stats_size_bytes);
135 
136 	op.params[1].tmpref.buffer = stats;
137 	op.params[1].tmpref.size = stats_size_bytes;
138 	res = TEEC_InvokeCommand(&sess, STATS_CMD_ALLOC_STATS, &op, &eo);
139 	if (res)
140 		errx(EXIT_FAILURE,
141 		     "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
142 		     res, eo);
143 
144 	if (op.params[1].tmpref.size != stats_size_bytes)
145 		errx(EXIT_FAILURE,
146 		     "STATS_CMD_PAGER_STATS: expected size %zu, got %zu",
147 		     stats_size_bytes, op.params[1].tmpref.size);
148 
149 	for (n = 0; n < stats_size_bytes / sizeof(*stats); n++) {
150 		if (n)
151 			printf("\n");
152 		printf("Pool:                %*s\n",
153 		       (int)strnlen(stats[n].desc, sizeof(stats[n].desc)),
154 		       stats[n].desc);
155 		printf("Bytes allocated:                       %"PRId32"\n",
156 		       stats[n].allocated);
157 		printf("Max bytes allocated:                   %"PRId32"\n",
158 		       stats[n].max_allocated);
159 		printf("Size of pool:                          %"PRId32"\n",
160 		       stats[n].size);
161 		printf("Number of failed allocations:          %"PRId32"\n",
162 		       stats[n].num_alloc_fail);
163 		printf("Size of larges allocation failure:     %"PRId32"\n",
164 		       stats[n].biggest_alloc_fail);
165 		printf("Total bytes allocated at that failure: %"PRId32"\n",
166 		       stats[n].biggest_alloc_fail_used);
167 	}
168 
169 	free(stats);
170 
171 	return close_sess(&ctx, &sess);
172 }
173 
stat_memleak(int argc,char * argv[])174 static int stat_memleak(int argc, char *argv[])
175 {
176 	TEEC_Context ctx = { };
177 	TEEC_Session sess = { };
178 	TEEC_Result res = TEEC_ERROR_GENERIC;
179 	uint32_t eo = 0;
180 
181 	UNUSED(argv);
182 	if (argc != 1)
183 		return usage();
184 
185 	open_sess(&ctx, &sess);
186 
187 	res = TEEC_InvokeCommand(&sess, STATS_CMD_MEMLEAK_STATS, NULL, &eo);
188 	if (res)
189 		errx(EXIT_FAILURE,
190 		     "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
191 		     res, eo);
192 
193 	return close_sess(&ctx, &sess);
194 }
195 
stat_loaded_ta(int argc,char * argv[])196 static int stat_loaded_ta(int argc, char *argv[])
197 {
198 	TEEC_Context ctx = { };
199 	TEEC_Session sess = { };
200 	TEEC_Result res = TEEC_ERROR_GENERIC;
201 	uint32_t eo = 0;
202 	TEEC_Operation op = { };
203 	void *buff = NULL;
204 	struct pta_stats_ta *stats = NULL;
205 	size_t stats_size_bytes = 0;
206 	size_t n = 0;
207 	uint32_t retry_count = 10;
208 
209 	UNUSED(argv);
210 	if (argc != 1)
211 		return usage();
212 
213 	open_sess(&ctx, &sess);
214 retry:
215 	op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_OUTPUT,
216 					 TEEC_NONE, TEEC_NONE, TEEC_NONE);
217 	res = TEEC_InvokeCommand(&sess, STATS_CMD_TA_STATS, &op, &eo);
218 	if (res != TEEC_ERROR_SHORT_BUFFER)
219 		errx(EXIT_FAILURE,
220 		     "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
221 		     res, eo);
222 
223 	stats_size_bytes = op.params[0].tmpref.size;
224 	if (stats_size_bytes == 0) {
225 		printf("No loaded TA found");
226 		goto out;
227 	}
228 
229 	if (stats_size_bytes % sizeof(*stats))
230 		errx(EXIT_FAILURE,
231 		     "STATS_CMD_TA_STATS: %zu not a multiple of %zu",
232 		     stats_size_bytes, sizeof(*stats));
233 
234 	/* Always allocate two more in case TA loaded after size querying */
235 	stats_size_bytes += 2 * sizeof(*stats);
236 
237 	stats = realloc(buff, stats_size_bytes);
238 	if (!stats)
239 		errx(EXIT_FAILURE, "realloc(%zu) failed", stats_size_bytes);
240 	buff = stats;
241 
242 	op.params[0].tmpref.buffer = stats;
243 	op.params[0].tmpref.size = stats_size_bytes;
244 	res = TEEC_InvokeCommand(&sess, STATS_CMD_TA_STATS, &op, &eo);
245 	if (res == TEEC_ERROR_SHORT_BUFFER && retry_count > 0) {
246 		retry_count--;
247 		goto retry;
248 	}
249 
250 	if (res)
251 		errx(EXIT_FAILURE,
252 		     "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
253 		     res, eo);
254 
255 	for (n = 0; n < op.params[0].tmpref.size / sizeof(*stats); n++) {
256 		if (n)
257 			printf("\n");
258 		printf("ta(%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x)\n",
259 			stats[n].uuid.timeLow, stats[n].uuid.timeMid,
260 			stats[n].uuid.timeHiAndVersion,
261 			stats[n].uuid.clockSeqAndNode[0],
262 			stats[n].uuid.clockSeqAndNode[1],
263 			stats[n].uuid.clockSeqAndNode[2],
264 			stats[n].uuid.clockSeqAndNode[3],
265 			stats[n].uuid.clockSeqAndNode[4],
266 			stats[n].uuid.clockSeqAndNode[5],
267 			stats[n].uuid.clockSeqAndNode[6],
268 			stats[n].uuid.clockSeqAndNode[7]);
269 		printf("\tpanicked(%"PRId32") -- True if TA has panicked\n",
270 			stats[n].panicked);
271 		printf("\tsession number(%"PRId32")\n", stats[n].sess_num);
272 		printf("\tHeap Status:\n");
273 		printf("\t\tBytes allocated:                       %"PRId32"\n",
274 		       stats[n].heap.allocated);
275 		printf("\t\tMax bytes allocated:                   %"PRId32"\n",
276 		       stats[n].heap.max_allocated);
277 		printf("\t\tSize of pool:                          %"PRId32"\n",
278 		       stats[n].heap.size);
279 		printf("\t\tNumber of failed allocations:          %"PRId32"\n",
280 		       stats[n].heap.num_alloc_fail);
281 		printf("\t\tSize of larges allocation failure:     %"PRId32"\n",
282 		       stats[n].heap.biggest_alloc_fail);
283 		printf("\t\tTotal bytes allocated at that failure: %"PRId32"\n",
284 		       stats[n].heap.biggest_alloc_fail_used);
285 	}
286 
287 out:
288 	free(buff);
289 	return close_sess(&ctx, &sess);
290 }
291 
stat_system_time(int argc,char * argv[])292 static int stat_system_time(int argc, char *argv[])
293 {
294 	TEEC_Context ctx = { };
295 	TEEC_Session sess = { };
296 	TEEC_Result res = TEEC_ERROR_GENERIC;
297 	uint32_t eo = 0;
298 	TEEC_Operation op = { };
299 
300 	UNUSED(argv);
301 	if (argc != 1)
302 		return usage();
303 
304 	open_sess(&ctx, &sess);
305 	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_OUTPUT,
306 					 TEEC_VALUE_OUTPUT,
307 					 TEEC_NONE, TEEC_NONE);
308 	res = TEEC_InvokeCommand(&sess, STATS_CMD_GET_TIME, &op, &eo);
309 	if (res != TEEC_SUCCESS)
310 		errx(EXIT_FAILURE,
311 		     "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
312 		     res, eo);
313 
314 	printf("REE time: %"PRId32" seconds, %"PRId32" milliseconds\n",
315 			op.params[0].value.a, op.params[0].value.b);
316 	printf("TEE time: %"PRId32" seconds, %"PRId32" milliseconds\n",
317 			op.params[1].value.a, op.params[1].value.b);
318 
319 	return close_sess(&ctx, &sess);
320 }
321 
stat_driver_info(int argc,int driver_type)322 static int stat_driver_info(int argc, int driver_type)
323 {
324 	TEEC_Context ctx = { };
325 	TEEC_Session sess = { };
326 	TEEC_Result res = TEEC_ERROR_GENERIC;
327 	uint32_t eo = 0;
328 	TEEC_Operation op = { };
329 
330 	if (argc != 1)
331 		return usage();
332 
333 	open_sess(&ctx, &sess);
334 
335 	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT,
336 					 TEEC_NONE, TEEC_NONE, TEEC_NONE);
337 	op.params[0].value.a = driver_type;
338 
339 	res = TEEC_InvokeCommand(&sess, STATS_CMD_PRINT_DRIVER_INFO, &op, &eo);
340 	if (res != TEEC_SUCCESS)
341 		errx(EXIT_FAILURE,
342 		     "TEEC_InvokeCommand(): res %#"PRIx32" err_orig %#"PRIx32,
343 		     res, eo);
344 
345 	return close_sess(&ctx, &sess);
346 }
347 
stats_runner_cmd_parser(int argc,char * argv[])348 int stats_runner_cmd_parser(int argc, char *argv[])
349 {
350 	if (argc > 1) {
351 		if (!strcmp(argv[1], "--pager"))
352 			return stat_pager(argc - 1, argv + 1);
353 		if (!strcmp(argv[1], "--alloc"))
354 			return stat_alloc(argc - 1, argv + 1);
355 		if (!strcmp(argv[1], "--memleak"))
356 			return stat_memleak(argc - 1, argv + 1);
357 		if (!strcmp(argv[1], "--ta"))
358 			return stat_loaded_ta(argc - 1, argv + 1);
359 		if (!strcmp(argv[1], "--time"))
360 			return stat_system_time(argc - 1, argv + 1);
361 		if (!strcmp(argv[1], "--clocks"))
362 			return stat_driver_info(argc - 1,
363 						STATS_DRIVER_TYPE_CLOCK);
364 		if (!strcmp(argv[1], "--regulators"))
365 			return stat_driver_info(argc - 1,
366 						STATS_DRIVER_TYPE_REGULATOR);
367 	}
368 
369 	return usage();
370 }
371