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