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