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 
31 #define TEE_ALLOCATOR_DESC_LENGTH 32
32 struct malloc_stats {
33 	char desc[TEE_ALLOCATOR_DESC_LENGTH];
34 	uint32_t allocated;		  /* Bytes currently allocated */
35 	uint32_t max_allocated;		  /* Tracks max value of allocated */
36 	uint32_t size;			  /* Total size for this allocator */
37 	uint32_t num_alloc_fail;	  /* Number of failed alloc requests */
38 	uint32_t biggest_alloc_fail;	  /* Size of biggest failed alloc */
39 	uint32_t biggest_alloc_fail_used; /* Alloc bytes when above occurred */
40 };
41 
42 static const char *stats_progname = "xtest --stats";
43 
usage(void)44 static int usage(void)
45 {
46 	fprintf(stderr, "Usage: %s [OPTION]\n", stats_progname);
47 	fprintf(stderr, "Displays statistics from OP-TEE\n");
48 	fprintf(stderr, "Options:\n");
49 	fprintf(stderr, " -h|--help      Print this help and exit\n");
50 	fprintf(stderr, " --pager        Print pager statistics\n");
51 	fprintf(stderr, " --alloc        Print allocation statistics\n");
52 	fprintf(stderr, " --memleak      Dump memory leak data on secure console\n");
53 
54 	return EXIT_FAILURE;
55 }
56 
open_sess(TEEC_Context * ctx,TEEC_Session * sess)57 static void open_sess(TEEC_Context *ctx, TEEC_Session *sess)
58 {
59 	TEEC_UUID uuid = STATS_UUID;
60 	TEEC_Result res = TEEC_ERROR_GENERIC;
61 	uint32_t eo = 0;
62 
63 	res = TEEC_InitializeContext(NULL, ctx);
64 	if (res)
65 		errx(EXIT_FAILURE, "TEEC_InitializeContext: %#"PRIx32, res);
66 
67 	res = TEEC_OpenSession(ctx, sess, &uuid, TEEC_LOGIN_PUBLIC, NULL,
68 			       NULL, &eo);
69 	if (res)
70 		errx(EXIT_FAILURE,
71 		     "TEEC_OpenSession: res %#"PRIx32" err_orig %#"PRIx32,
72 			res, eo);
73 }
74 
close_sess(TEEC_Context * ctx,TEEC_Session * sess)75 static int close_sess(TEEC_Context *ctx, TEEC_Session *sess)
76 {
77 	TEEC_CloseSession(sess);
78 	TEEC_FinalizeContext(ctx);
79 
80 	return EXIT_SUCCESS;
81 }
82 
stat_pager(int argc,char * argv[])83 static int stat_pager(int argc, char *argv[])
84 {
85 	TEEC_Context ctx = { };
86 	TEEC_Session sess = { };
87 	TEEC_Result res = TEEC_ERROR_GENERIC;
88 	uint32_t eo = 0;
89 	TEEC_Operation op = { };
90 
91 	UNUSED(argv);
92 	if (argc != 1)
93 		return usage();
94 
95 	open_sess(&ctx, &sess);
96 
97 	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_OUTPUT, TEEC_VALUE_OUTPUT,
98 					 TEEC_VALUE_OUTPUT, TEEC_NONE);
99 
100 	res = TEEC_InvokeCommand(&sess, STATS_CMD_PAGER_STATS, &op, &eo);
101 	if (res)
102 		errx(EXIT_FAILURE,
103 		     "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
104 		     res, eo);
105 
106 	printf("Pager statistics (Number of):\n");
107 	printf("Unlocked pages:     %"PRId32"\n", op.params[0].value.a);
108 	printf("Page pool size:     %"PRId32"\n", op.params[0].value.b);
109 	printf("R/O faults:         %"PRId32"\n", op.params[1].value.a);
110 	printf("R/W faults:         %"PRId32"\n", op.params[1].value.b);
111 	printf("Hidden faults:      %"PRId32"\n", op.params[2].value.a);
112 	printf("Zi pages released:  %"PRId32"\n", op.params[2].value.b);
113 
114 	return close_sess(&ctx, &sess);
115 }
116 
stat_alloc(int argc,char * argv[])117 static int stat_alloc(int argc, char *argv[])
118 {
119 	TEEC_Context ctx = { };
120 	TEEC_Session sess = { };
121 	TEEC_Result res = TEEC_ERROR_GENERIC;
122 	uint32_t eo = 0;
123 	TEEC_Operation op = { };
124 	struct malloc_stats *stats = NULL;
125 	size_t stats_size_bytes = 0;
126 	size_t n = 0;
127 
128 	UNUSED(argv);
129 	if (argc != 1)
130 		return usage();
131 
132 	open_sess(&ctx, &sess);
133 
134 	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT,
135 					 TEEC_MEMREF_TEMP_OUTPUT,
136 					 TEEC_NONE, TEEC_NONE);
137 	res = TEEC_InvokeCommand(&sess, STATS_CMD_ALLOC_STATS, &op, &eo);
138 	if (res != TEEC_ERROR_SHORT_BUFFER)
139 		errx(EXIT_FAILURE,
140 		     "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
141 		     res, eo);
142 
143 	stats_size_bytes = op.params[1].tmpref.size;
144 	if (stats_size_bytes % sizeof(*stats))
145 		errx(EXIT_FAILURE,
146 		     "STATS_CMD_PAGER_STATS: %zu not a multiple of %zu",
147 		     stats_size_bytes, sizeof(*stats));
148 	stats = calloc(1, stats_size_bytes);
149 	if (!stats)
150 		err(EXIT_FAILURE, "calloc(1, %zu)", stats_size_bytes);
151 
152 	op.params[1].tmpref.buffer = stats;
153 	op.params[1].tmpref.size = stats_size_bytes;
154 	res = TEEC_InvokeCommand(&sess, STATS_CMD_ALLOC_STATS, &op, &eo);
155 	if (res)
156 		errx(EXIT_FAILURE,
157 		     "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
158 		     res, eo);
159 
160 	if (op.params[1].tmpref.size != stats_size_bytes)
161 		errx(EXIT_FAILURE,
162 		     "STATS_CMD_PAGER_STATS: expected size %zu, got %zu",
163 		     stats_size_bytes, op.params[1].tmpref.size);
164 
165 	for (n = 0; n < stats_size_bytes / sizeof(*stats); n++) {
166 		if (n)
167 			printf("\n");
168 		printf("Pool:                %*s\n",
169 		       (int)strnlen(stats[n].desc, sizeof(stats[n].desc)),
170 		       stats[n].desc);
171 		printf("Bytes allocated:                       %"PRId32"\n",
172 		       stats[n].allocated);
173 		printf("Max bytes allocated:                   %"PRId32"\n",
174 		       stats[n].max_allocated);
175 		printf("Size of pool:                          %"PRId32"\n",
176 		       stats[n].size);
177 		printf("Number of failed allocations:          %"PRId32"\n",
178 		       stats[n].num_alloc_fail);
179 		printf("Size of larges allocation failure:     %"PRId32"\n",
180 		       stats[n].biggest_alloc_fail);
181 		printf("Total bytes allocated at that failure: %"PRId32"\n",
182 		       stats[n].biggest_alloc_fail_used);
183 	}
184 
185 	free(stats);
186 
187 	return close_sess(&ctx, &sess);
188 }
189 
stat_memleak(int argc,char * argv[])190 static int stat_memleak(int argc, char *argv[])
191 {
192 	TEEC_Context ctx = { };
193 	TEEC_Session sess = { };
194 	TEEC_Result res = TEEC_ERROR_GENERIC;
195 	uint32_t eo = 0;
196 
197 	UNUSED(argv);
198 	if (argc != 1)
199 		return usage();
200 
201 	open_sess(&ctx, &sess);
202 
203 	res = TEEC_InvokeCommand(&sess, STATS_CMD_MEMLEAK_STATS, NULL, &eo);
204 	if (res)
205 		errx(EXIT_FAILURE,
206 		     "TEEC_InvokeCommand: res %#"PRIx32" err_orig %#"PRIx32,
207 		     res, eo);
208 
209 	return close_sess(&ctx, &sess);
210 }
211 
stats_runner_cmd_parser(int argc,char * argv[])212 int stats_runner_cmd_parser(int argc, char *argv[])
213 {
214 	if (argc > 1) {
215 		if (!strcmp(argv[1], "--pager"))
216 			return stat_pager(argc - 1, argv + 1);
217 		if (!strcmp(argv[1], "--alloc"))
218 			return stat_alloc(argc - 1, argv + 1);
219 		if (!strcmp(argv[1], "--memleak"))
220 			return stat_memleak(argc - 1, argv + 1);
221 	}
222 
223 	return usage();
224 }
225