blob: 8ba060ababf4bcdec63814a4fa089a92aee8db70 [file] [log] [blame]
Igor Opaniuk44aff4b2016-09-16 10:18:00 +03001/*
2 * Copyright (c) 2015, Linaro Limited
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <fcntl.h>
31#include <math.h>
32#include <stdint.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <strings.h>
37#include <time.h>
38#include <unistd.h>
39
40#include <tee_client_api.h>
41#include "ta_aes_perf.h"
Igor Opaniukf9b7fd22016-09-16 16:22:34 +030042#include "crypto_common.h"
Igor Opaniuk44aff4b2016-09-16 10:18:00 +030043
44/*
45 * TEE client stuff
46 */
47
48static TEEC_Context ctx;
49static TEEC_Session sess;
50/*
51 * in_shm and out_shm are both IN/OUT to support dynamically choosing
52 * in_place == 1 or in_place == 0.
53 */
54static TEEC_SharedMemory in_shm = {
55 .flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT
56};
57static TEEC_SharedMemory out_shm = {
58 .flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT
59};
60
Etienne Carriere2f18cc42017-04-26 15:01:26 +020061static void errx(const char *msg, TEEC_Result res, uint32_t *orig)
Igor Opaniuk44aff4b2016-09-16 10:18:00 +030062{
63 fprintf(stderr, "%s: 0x%08x", msg, res);
Etienne Carriere2f18cc42017-04-26 15:01:26 +020064 if (orig)
65 fprintf(stderr, " (orig=%d)", (int)*orig);
66 fprintf(stderr, "\n");
Igor Opaniuk44aff4b2016-09-16 10:18:00 +030067 exit (1);
68}
69
Etienne Carriere2f18cc42017-04-26 15:01:26 +020070static void check_res(TEEC_Result res, const char *errmsg, uint32_t *orig)
Igor Opaniuk44aff4b2016-09-16 10:18:00 +030071{
72 if (res != TEEC_SUCCESS)
Etienne Carriere2f18cc42017-04-26 15:01:26 +020073 errx(errmsg, res, orig);
Igor Opaniuk44aff4b2016-09-16 10:18:00 +030074}
75
76static void open_ta(void)
77{
78 TEEC_Result res;
79 TEEC_UUID uuid = TA_AES_PERF_UUID;
80 uint32_t err_origin;
81
82 res = TEEC_InitializeContext(NULL, &ctx);
Etienne Carriere2f18cc42017-04-26 15:01:26 +020083 check_res(res, "TEEC_InitializeContext", NULL);
Igor Opaniuk44aff4b2016-09-16 10:18:00 +030084
85 res = TEEC_OpenSession(&ctx, &sess, &uuid, TEEC_LOGIN_PUBLIC, NULL,
86 NULL, &err_origin);
Etienne Carriere2f18cc42017-04-26 15:01:26 +020087 check_res(res, "TEEC_OpenSession", &err_origin);
Igor Opaniuk44aff4b2016-09-16 10:18:00 +030088}
89
90/*
91 * Statistics
92 *
93 * We want to compute min, max, mean and standard deviation of processing time
94 */
95
96struct statistics {
97 int n;
98 double m;
99 double M2;
100 double min;
101 double max;
102 int initialized;
103};
104
105/* Take new sample into account (Knuth/Welford algorithm) */
106static void update_stats(struct statistics *s, uint64_t t)
107{
108 double x = (double)t;
109 double delta = x - s->m;
110
111 s->n++;
112 s->m += delta/s->n;
113 s->M2 += delta*(x - s->m);
114 if (!s->initialized) {
115 s->min = s->max = x;
116 s->initialized = 1;
117 } else {
118 if (s->min > x)
119 s->min = x;
120 if (s->max < x)
121 s->max = x;
122 }
123}
124
125static double stddev(struct statistics *s)
126{
127 if (s->n < 2)
128 return NAN;
129 return sqrt(s->M2/s->n);
130}
131
132static const char *mode_str(uint32_t mode)
133{
134 switch (mode) {
135 case TA_AES_ECB:
136 return "ECB";
137 case TA_AES_CBC:
138 return "CBC";
139 case TA_AES_CTR:
140 return "CTR";
141 case TA_AES_XTS:
142 return "XTS";
143 default:
144 return "???";
145 }
146}
147
148#define _TO_STR(x) #x
149#define TO_STR(x) _TO_STR(x)
150
151static void usage(const char *progname, int keysize, int mode,
152 size_t size, int warmup, unsigned int l, unsigned int n)
153{
Etienne Carrierec92b55f2017-03-24 10:28:09 +0100154 fprintf(stderr, "AES performance testing tool for OP-TEE\n\n");
Igor Opaniuk44aff4b2016-09-16 10:18:00 +0300155 fprintf(stderr, "Usage:\n");
156 fprintf(stderr, " %s -h\n", progname);
157 fprintf(stderr, " %s [-v] [-m mode] [-k keysize] ", progname);
158 fprintf(stderr, "[-s bufsize] [-r] [-i] [-n loops] [-l iloops] \n");
159 fprintf(stderr, "[-w warmup_time]\n");
160 fprintf(stderr, "Options:\n");
161 fprintf(stderr, " -h Print this help and exit\n");
162 fprintf(stderr, " -i Use same buffer for input and output (in ");
163 fprintf(stderr, "place)\n");
164 fprintf(stderr, " -k Key size in bits: 128, 192 or 256 [%u]\n",
165 keysize);
166 fprintf(stderr, " -l Inner loop iterations (TA calls ");
167 fprintf(stderr, "TEE_CipherUpdate() <x> times) [%u]\n", l);
168 fprintf(stderr, " -m AES mode: ECB, CBC, CTR, XTS [%s]\n",
169 mode_str(mode));
170 fprintf(stderr, " -n Outer loop iterations [%u]\n", n);
171 fprintf(stderr, " -r Get input data from /dev/urandom ");
172 fprintf(stderr, "(otherwise use zero-filled buffer)\n");
173 fprintf(stderr, " -s Buffer size (process <x> bytes at a time) ");
174 fprintf(stderr, "[%zu]\n", size);
175 fprintf(stderr, " -v Be verbose (use twice for greater effect)\n");
176 fprintf(stderr, " -w Warm-up time in seconds: execute a busy ");
177 fprintf(stderr, "loop before the test\n");
178 fprintf(stderr, " to mitigate the effects of cpufreq etc. ");
179 fprintf(stderr, "[%u]\n", warmup);
180}
181
182static void alloc_shm(size_t sz, int in_place)
183{
184 TEEC_Result res;
185
186 in_shm.buffer = NULL;
187 in_shm.size = sz;
188 res = TEEC_AllocateSharedMemory(&ctx, &in_shm);
Etienne Carriere2f18cc42017-04-26 15:01:26 +0200189 check_res(res, "TEEC_AllocateSharedMemory", NULL);
Igor Opaniuk44aff4b2016-09-16 10:18:00 +0300190
191 if (!in_place) {
192 out_shm.buffer = NULL;
193 out_shm.size = sz;
194 res = TEEC_AllocateSharedMemory(&ctx, &out_shm);
Etienne Carriere2f18cc42017-04-26 15:01:26 +0200195 check_res(res, "TEEC_AllocateSharedMemory", NULL);
Igor Opaniuk44aff4b2016-09-16 10:18:00 +0300196 }
197}
198
199static void free_shm(void)
200{
201 TEEC_ReleaseSharedMemory(&in_shm);
202 TEEC_ReleaseSharedMemory(&out_shm);
203}
204
205static ssize_t read_random(void *in, size_t rsize)
206{
207 static int rnd;
208 ssize_t s;
209
210 if (!rnd) {
211 rnd = open("/dev/urandom", O_RDONLY);
212 if (rnd < 0) {
213 perror("open");
214 return 1;
215 }
216 }
217 s = read(rnd, in, rsize);
218 if (s < 0) {
219 perror("read");
220 return 1;
221 }
222 if ((size_t)s != rsize) {
223 printf("read: requested %zu bytes, got %zd\n",
224 rsize, s);
225 }
226
227 return 0;
228}
229
230static void get_current_time(struct timespec *ts)
231{
232 if (clock_gettime(CLOCK_MONOTONIC, ts) < 0) {
233 perror("clock_gettime");
234 exit(1);
235 }
236}
237
238static uint64_t timespec_to_ns(struct timespec *ts)
239{
240 return ((uint64_t)ts->tv_sec * 1000000000) + ts->tv_nsec;
241}
242
243static uint64_t timespec_diff_ns(struct timespec *start, struct timespec *end)
244{
245 return timespec_to_ns(end) - timespec_to_ns(start);
246}
247
248static uint64_t run_test_once(void *in, size_t size, TEEC_Operation *op,
249 int random_in)
250{
251 struct timespec t0, t1;
252 TEEC_Result res;
253 uint32_t ret_origin;
254
255 if (random_in)
256 read_random(in, size);
257 get_current_time(&t0);
258 res = TEEC_InvokeCommand(&sess, TA_AES_PERF_CMD_PROCESS, op,
259 &ret_origin);
Etienne Carriere2f18cc42017-04-26 15:01:26 +0200260 check_res(res, "TEEC_InvokeCommand", &ret_origin);
Igor Opaniuk44aff4b2016-09-16 10:18:00 +0300261 get_current_time(&t1);
262
263 return timespec_diff_ns(&t0, &t1);
264}
265
266static void prepare_key(int decrypt, int keysize, int mode)
267{
268 TEEC_Result res;
269 uint32_t ret_origin;
270 TEEC_Operation op;
271
272 memset(&op, 0, sizeof(op));
273 op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_VALUE_INPUT,
274 TEEC_NONE, TEEC_NONE);
275 op.params[0].value.a = decrypt;
276 op.params[0].value.b = keysize;
277 op.params[1].value.a = mode;
278 res = TEEC_InvokeCommand(&sess, TA_AES_PERF_CMD_PREPARE_KEY, &op,
279 &ret_origin);
Etienne Carriere2f18cc42017-04-26 15:01:26 +0200280 check_res(res, "TEEC_InvokeCommand", &ret_origin);
Igor Opaniuk44aff4b2016-09-16 10:18:00 +0300281}
282
283static void do_warmup(int warmup)
284{
285 struct timespec t0, t;
286 int i;
287
288 get_current_time(&t0);
289 do {
290 for (i = 0; i < 100000; i++)
291 ;
292 get_current_time(&t);
293 } while (timespec_diff_ns(&t0, &t) < (uint64_t)warmup * 1000000000);
294}
295
296static const char *yesno(int v)
297{
298 return (v ? "yes" : "no");
299}
300
301static double mb_per_sec(size_t size, double usec)
302{
303 return (1000000000/usec)*((double)size/(1024*1024));
304}
305
306/* Encryption test: buffer of tsize byte. Run test n times. */
307void aes_perf_run_test(int mode, int keysize, int decrypt, size_t size,
308 unsigned int n, unsigned int l, int random_in,
309 int in_place, int warmup, int verbosity)
310{
311 uint64_t t;
312 struct statistics stats;
313 struct timespec ts;
314 TEEC_Operation op;
315 int n0 = n;
Jerome Forissierd99fe972017-02-21 15:30:57 +0100316 double sd;
Igor Opaniuk44aff4b2016-09-16 10:18:00 +0300317
Etienne Carrierec92b55f2017-03-24 10:28:09 +0100318 vverbose("aes-perf\n");
Igor Opaniuk44aff4b2016-09-16 10:18:00 +0300319 if (clock_getres(CLOCK_MONOTONIC, &ts) < 0) {
320 perror("clock_getres");
321 return;
322 }
323 vverbose("Clock resolution is %lu ns\n", ts.tv_sec*1000000000 +
324 ts.tv_nsec);
325
326 open_ta();
327 prepare_key(decrypt, keysize, mode);
328
329 memset(&stats, 0, sizeof(stats));
330
331 alloc_shm(size, in_place);
332
333 if (!random_in)
334 memset(in_shm.buffer, 0, size);
335
336 memset(&op, 0, sizeof(op));
337 /* Using INOUT to handle the case in_place == 1 */
338 op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_PARTIAL_INOUT,
339 TEEC_MEMREF_PARTIAL_INOUT,
340 TEEC_VALUE_INPUT, TEEC_NONE);
341 op.params[0].memref.parent = &in_shm;
342 op.params[0].memref.offset = 0;
343 op.params[0].memref.size = size;
344 op.params[1].memref.parent = in_place ? &in_shm : &out_shm;
345 op.params[1].memref.offset = 0;
346 op.params[1].memref.size = size;
347 op.params[2].value.a = l;
348
349 verbose("Starting test: %s, %scrypt, keysize=%u bits, size=%zu bytes, ",
350 mode_str(mode), (decrypt ? "de" : "en"), keysize, size);
351 verbose("random=%s, ", yesno(random_in));
352 verbose("in place=%s, ", yesno(in_place));
353 verbose("inner loops=%u, loops=%u, warm-up=%u s\n", l, n, warmup);
354
355 if (warmup)
356 do_warmup(warmup);
357
358 while (n-- > 0) {
359 t = run_test_once(in_shm.buffer, size, &op, random_in);
360 update_stats(&stats, t);
Etienne Carrierec92b55f2017-03-24 10:28:09 +0100361 if (n % (n0 / 10) == 0)
Igor Opaniuk44aff4b2016-09-16 10:18:00 +0300362 vverbose("#");
363 }
364 vverbose("\n");
Jerome Forissierd99fe972017-02-21 15:30:57 +0100365 sd = stddev(&stats);
Etienne Carrierec92b55f2017-03-24 10:28:09 +0100366 printf("min=%gus max=%gus mean=%gus stddev=%gus (cv %g%%) (%gMiB/s)\n",
367 stats.min / 1000, stats.max / 1000, stats.m / 1000,
368 sd / 1000, 100 * sd / stats.m, mb_per_sec(size, stats.m));
369 verbose("2-sigma interval: %g..%gus (%g..%gMiB/s)\n",
370 (stats.m - 2 * sd) / 1000, (stats.m + 2 * sd) / 1000,
371 mb_per_sec(size, stats.m + 2 * sd),
372 mb_per_sec(size, stats.m - 2 * sd));
Igor Opaniuk44aff4b2016-09-16 10:18:00 +0300373 free_shm();
374}
375
376#define NEXT_ARG(i) \
377 do { \
378 if (++i == argc) { \
379 fprintf(stderr, "%s: %s: missing argument\n", \
Etienne Carrierec92b55f2017-03-24 10:28:09 +0100380 argv[0], argv[i - 1]); \
Igor Opaniuk44aff4b2016-09-16 10:18:00 +0300381 return 1; \
382 } \
383 } while (0);
384
385int aes_perf_runner_cmd_parser(int argc, char *argv[])
386{
387 int i;
388
389 /*
390 * Command line parameters
391 */
392
393 size_t size = 1024; /* Buffer size (-s) */
Igor Opaniukf9b7fd22016-09-16 16:22:34 +0300394 unsigned int n = CRYPTO_DEF_COUNT; /*Number of measurements (-n)*/
395 unsigned int l = CRYPTO_DEF_LOOPS; /* Inner loops (-l) */
396 int verbosity = CRYPTO_DEF_VERBOSITY; /* Verbosity (-v) */
Igor Opaniuk44aff4b2016-09-16 10:18:00 +0300397 int decrypt = 0; /* Encrypt by default, -d to decrypt */
Igor Opaniukf9b7fd22016-09-16 16:22:34 +0300398 int keysize = AES_128; /* AES key size (-k) */
Igor Opaniuk44aff4b2016-09-16 10:18:00 +0300399 int mode = TA_AES_ECB; /* AES mode (-m) */
Igor Opaniukf9b7fd22016-09-16 16:22:34 +0300400 /* Get input data from /dev/urandom (-r) */
401 int random_in = CRYPTO_USE_RANDOM;
402 /* Use same buffer for in and out (-i) */
403 int in_place = AES_PERF_INPLACE;
404 int warmup = CRYPTO_DEF_WARMUP; /* Start with a 2-second busy loop (-w) */
Igor Opaniuk44aff4b2016-09-16 10:18:00 +0300405
406 /* Parse command line */
407 for (i = 1; i < argc; i++) {
408 if (!strcmp(argv[i], "-h")) {
409 usage(argv[0], keysize, mode, size, warmup, l, n);
410 return 0;
411 }
412 }
413 for (i = 1; i < argc; i++) {
414 if (!strcmp(argv[i], "-d")) {
415 decrypt = 1;
416 } else if (!strcmp(argv[i], "-i")) {
417 in_place = 1;
418 } else if (!strcmp(argv[i], "-k")) {
419 NEXT_ARG(i);
420 keysize = atoi(argv[i]);
421 if (keysize != AES_128 && keysize != AES_192 &&
422 keysize != AES_256) {
423 fprintf(stderr, "%s: invalid key size\n",
424 argv[0]);
425 usage(argv[0], keysize, mode, size, warmup, l, n);
426 return 1;
427 }
428 } else if (!strcmp(argv[i], "-l")) {
429 NEXT_ARG(i);
430 l = atoi(argv[i]);
431 } else if (!strcmp(argv[i], "-m")) {
432 NEXT_ARG(i);
433 if (!strcasecmp(argv[i], "ECB"))
434 mode = TA_AES_ECB;
435 else if (!strcasecmp(argv[i], "CBC"))
436 mode = TA_AES_CBC;
437 else if (!strcasecmp(argv[i], "CTR"))
438 mode = TA_AES_CTR;
439 else if (!strcasecmp(argv[i], "XTS"))
440 mode = TA_AES_XTS;
441 else {
442 fprintf(stderr, "%s, invalid mode\n",
443 argv[0]);
444 usage(argv[0], keysize, mode, size, warmup, l, n);
445 return 1;
446 }
447 } else if (!strcmp(argv[i], "-n")) {
448 NEXT_ARG(i);
449 n = atoi(argv[i]);
450 } else if (!strcmp(argv[i], "-r")) {
451 random_in = 1;
452 } else if (!strcmp(argv[i], "-s")) {
453 NEXT_ARG(i);
454 size = atoi(argv[i]);
455 } else if (!strcmp(argv[i], "-v")) {
456 verbosity++;
457 } else if (!strcmp(argv[i], "-w")) {
458 NEXT_ARG(i);
459 warmup = atoi(argv[i]);
460 } else {
461 fprintf(stderr, "%s: invalid argument: %s\n",
462 argv[0], argv[i]);
463 usage(argv[0], keysize, mode, size, warmup, l, n);
464 return 1;
465 }
466 }
467
Igor Opaniuk44aff4b2016-09-16 10:18:00 +0300468 aes_perf_run_test(mode, keysize, decrypt, size, n, l, random_in,
469 in_place, warmup, verbosity);
470
471 return 0;
472}