blob: e9d5443b110896014c1e7367ba522217cb907340 [file] [log] [blame]
Etienne Carriere41343db2017-03-17 15:38:52 +01001/*
2 * Copyright (c) 2016, 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 <err.h>
29#include <fcntl.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <sys/ioctl.h>
34#include <sys/mman.h>
35#include <tee_client_api.h>
36#include <tee_client_api_extensions.h>
37#include <unistd.h>
38
39#include "include/uapi/linux/ion.h"
40#include "ta_sdp_basic.h"
41#include "crypto_common.h"
42
43/*
44 * SDP basic test setup overview.
45 *
46 * - A dedicated trusted application (SDP basic TA) supports 3 commands:
47 * - 'inject' data from a nonsecure buffer into a secure buffer
48 * - 'transform' data inside a secure buffer (bitwise invert + unsigned incr)
49 * - 'dump' data from a secure buffer into a nonsecure buffer
50
51 * - This test client application (CA) invokes the TA for these 3 operations,
52 * inject random value, trasforming them then dump them.
53 *
54 * To do so, CA allocates a 'SDP secure buffer' and invoke the TA for these 3
55 * operations (inject then transform then dump) over the allocate buffer.
56 *
57 * The secure buffer is currently allocation through ION support adn
58 * registered to OP-TEE and as shared memory.
59 *
60 * To enhance test coverage against buffer alignement usecase, the CA invokes
61 * the TA with a variable offset inside the buffer. As CA injects random data
62 * into the buffer, the CA uses one of the random bytes to set the value of the
63 * offset in the accessed secure buffer.
64 *
65 * For debugging support, the CA may map (in nonsecure world) the secure
66 * buffer to read its content. As this is unsafe on a hardened platform, this
67 * operation is default disable. When enable, error only print out a warning
68 * trace but does not actually fail the test. This also give an easy way to
69 * check that some HW complains on access violation when nonsecure accesses
70 * secure data.
71 */
72
73static int verbosity = 1;
74
75struct tee_ctx {
76 TEEC_Context ctx;
77 TEEC_Session sess;
78};
79
80/* exported to xtest */
81int allocate_ion_buffer(size_t size, int heap_id);
82
83/* non zero value forces buffer to be mappeable from nonsecure */
84#define BUF_MUST_MAP 0
85
86#define DEFAULT_ION_HEAP_TYPE ION_HEAP_TYPE_UNMAPPED
87
88int allocate_ion_buffer(size_t size, int heap_id)
89{
90 struct ion_allocation_data alloc_data;
91 struct ion_handle_data hdl_data;
92 struct ion_fd_data fd_data;
93 int ion;
94 int fd = -1;
95
96 ion = open("/dev/ion", O_RDWR);
97 if (ion < 0) {
98 fprintf(stderr, "Error; failed to open /dev/ion\n");
99 verbose("Seems no ION heap is available.\n");
100 verbose("To test ION allocation you can enable\n");
101 verbose("CONFIG_ION and CONFIG_ION_DUMMY in your\n");
102 verbose("linux kernel configuration.\n");
103 return fd;
104 }
105
106 if (heap_id < 0)
107 heap_id = DEFAULT_ION_HEAP_TYPE;
108
109 verbose("Allocate in ION heap '%s'\n",
110 heap_id == ION_HEAP_TYPE_SYSTEM ? "system" :
111 heap_id == ION_HEAP_TYPE_SYSTEM_CONTIG ? "system contig" :
112 heap_id == ION_HEAP_TYPE_CARVEOUT ? "carveout" :
113 heap_id == ION_HEAP_TYPE_CHUNK ? "chunk" :
114 heap_id == ION_HEAP_TYPE_DMA ? "dma" :
115 heap_id == ION_HEAP_TYPE_UNMAPPED ? "unmapped" :
116 "custom");
117
118 alloc_data.len = size;
119 alloc_data.align = 0;
120 alloc_data.flags = 0;
121 alloc_data.heap_id_mask = 1 << heap_id;
122 if (ioctl(ion, ION_IOC_ALLOC, &alloc_data) == -1)
123 goto out;
124
125 fd_data.handle = alloc_data.handle;
126 if (ioctl(ion, ION_IOC_SHARE, &fd_data) != -1)
127 fd = fd_data.fd;
128
129 hdl_data.handle = alloc_data.handle;
130 (void)ioctl(ion, ION_IOC_FREE, &hdl_data);
131out:
132 close(ion);
133 return fd;
134}
135
136static void finalize_tee_ctx(struct tee_ctx *ctx)
137{
138 if (!ctx)
139 return;
140
141 TEEC_CloseSession(&ctx->sess);
142 TEEC_FinalizeContext(&ctx->ctx);
143}
144
145static int create_tee_ctx(struct tee_ctx *ctx)
146{
147 TEEC_Result teerc;
148 TEEC_UUID uuid = TA_SDP_BASIC_UUID;
149 uint32_t err_origin;
150
151 teerc = TEEC_InitializeContext(NULL, &ctx->ctx);
152 if (teerc != TEEC_SUCCESS)
153 return -1;
154
155 teerc = TEEC_OpenSession(&ctx->ctx, &ctx->sess, &uuid,
156 TEEC_LOGIN_PUBLIC, NULL, NULL, &err_origin);
157 if (teerc != TEEC_SUCCESS)
158 fprintf(stderr, "Error: open session to SDP test TA failed %x %d\n",
159 teerc, err_origin);
160
161 return (teerc == TEEC_SUCCESS) ? 0 : -1;
162}
163
164static int tee_register_buffer(struct tee_ctx *ctx, void **shm_ref, int fd)
165{
166 TEEC_Result teerc;
167 TEEC_SharedMemory *shm;
168
169 shm = malloc(sizeof(*shm));
170 if (!shm)
171 return 1;
172
173 shm->flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT;
174 teerc = TEEC_RegisterSharedMemoryFileDescriptor(&ctx->ctx, shm, fd);
175 if (teerc != TEEC_SUCCESS) {
176 fprintf(stderr, "Error: TEEC_RegisterMemoryFileDescriptor() failed %x\n",
177 teerc);
178 return 1;
179 }
180
181 *shm_ref = shm;
182 return 0;
183}
184
185static void tee_deregister_buffer(struct tee_ctx *ctx, void *shm_ref)
186{
187 (void)ctx;
188
189 if (!shm_ref)
190 return;
191
192 TEEC_ReleaseSharedMemory((TEEC_SharedMemory *)shm_ref);
193 free(shm_ref);
194}
195
196static int inject_sdp_data(struct tee_ctx *ctx,
Etienne Carrieref690b912017-03-21 15:44:13 +0100197 void *in, size_t offset, size_t len, void *shm_ref, int ind)
Etienne Carriere41343db2017-03-17 15:38:52 +0100198{
199 TEEC_SharedMemory *shm = (TEEC_SharedMemory *)shm_ref;
200 TEEC_Result teerc;
201 TEEC_Operation op;
202 uint32_t err_origin;
Etienne Carrieref690b912017-03-21 15:44:13 +0100203 unsigned cmd = ind ? TA_SDP_BASIC_CMD_INVOKE_INJECT :
204 TA_SDP_BASIC_CMD_INJECT;
Etienne Carriere41343db2017-03-17 15:38:52 +0100205
206 memset(&op, 0, sizeof(op));
207 op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
208 TEEC_MEMREF_PARTIAL_OUTPUT,
209 TEEC_NONE, TEEC_NONE);
210
211 op.params[0].tmpref.buffer = in;
212 op.params[0].tmpref.size = len;
213
214 op.params[1].memref.parent = shm;
215 op.params[1].memref.size = len;
216 op.params[1].memref.offset = offset;
217
Etienne Carrieref690b912017-03-21 15:44:13 +0100218 teerc = TEEC_InvokeCommand(&ctx->sess, cmd, &op, &err_origin);
Etienne Carriere41343db2017-03-17 15:38:52 +0100219 if (teerc != TEEC_SUCCESS)
220 fprintf(stderr, "Error: invoke SDP test TA (inject) failed %x %d\n",
221 teerc, err_origin);
222
223 return (teerc == TEEC_SUCCESS) ? 0 : -1;
224}
225
226static int transform_sdp_data(struct tee_ctx *ctx,
Etienne Carrieref690b912017-03-21 15:44:13 +0100227 size_t offset, size_t len, void *shm_ref, int ind)
Etienne Carriere41343db2017-03-17 15:38:52 +0100228{
229 TEEC_SharedMemory *shm = (TEEC_SharedMemory *)shm_ref;
230 TEEC_Result teerc;
231 TEEC_Operation op;
232 uint32_t err_origin;
Etienne Carrieref690b912017-03-21 15:44:13 +0100233 unsigned cmd = ind ? TA_SDP_BASIC_CMD_INVOKE_TRANSFORM :
234 TA_SDP_BASIC_CMD_TRANSFORM;
Etienne Carriere41343db2017-03-17 15:38:52 +0100235
236 memset(&op, 0, sizeof(op));
237 op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_PARTIAL_INOUT,
238 TEEC_NONE, TEEC_NONE, TEEC_NONE);
239 op.params[0].memref.parent = shm;
240 op.params[0].memref.size = len;
241 op.params[0].memref.offset = offset;
242
Etienne Carrieref690b912017-03-21 15:44:13 +0100243 teerc = TEEC_InvokeCommand(&ctx->sess, cmd, &op, &err_origin);
Etienne Carriere41343db2017-03-17 15:38:52 +0100244 if (teerc != TEEC_SUCCESS)
245 fprintf(stderr, "Error: invoke SDP test TA (transform) failed %x %d\n",
246 teerc, err_origin);
247
248 return (teerc == TEEC_SUCCESS) ? 0 : -1;
249}
250
251static int dump_sdp_data(struct tee_ctx *ctx,
Etienne Carrieref690b912017-03-21 15:44:13 +0100252 void *out, size_t offset, size_t len, void *shm_ref, int ind)
Etienne Carriere41343db2017-03-17 15:38:52 +0100253{
254 TEEC_SharedMemory *shm = (TEEC_SharedMemory *)shm_ref;
255 TEEC_Result teerc;
256 TEEC_Operation op;
257 uint32_t err_origin;
Etienne Carrieref690b912017-03-21 15:44:13 +0100258 unsigned cmd = ind ? TA_SDP_BASIC_CMD_INVOKE_DUMP :
259 TA_SDP_BASIC_CMD_DUMP;
Etienne Carriere41343db2017-03-17 15:38:52 +0100260
261 memset(&op, 0, sizeof(op));
262 op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_PARTIAL_INPUT,
263 TEEC_MEMREF_TEMP_OUTPUT,
264 TEEC_NONE, TEEC_NONE);
265 op.params[0].memref.parent = shm;
266 op.params[0].memref.size = len;
267 op.params[0].memref.offset = offset;
268
269 op.params[1].tmpref.buffer = out;
270 op.params[1].tmpref.size = len;
271
Etienne Carrieref690b912017-03-21 15:44:13 +0100272 teerc = TEEC_InvokeCommand(&ctx->sess, cmd, &op, &err_origin);
Etienne Carriere41343db2017-03-17 15:38:52 +0100273 if (teerc != TEEC_SUCCESS)
274 fprintf(stderr, "Error: invoke SDP test TA (dump) failed %x %d\n",
275 teerc, err_origin);
276
277 return (teerc == TEEC_SUCCESS) ? 0 : -1;
278}
279
280static int check_sdp_dumped(struct tee_ctx *ctx, void *ref, size_t len,
281 void *out)
282{
283 char *bref = (char *)ref;
284 char *data = (char *)out;
285 int err = 0;
286
287 (void)ctx;
288
289 while(len--)
290 if (*data++ != (unsigned char)(~(*bref++) + 1))
291 err++;
292
293 return err;
294}
295
296/*
297 * Consider 32kByte + 1 of random data is sufficient for an accurate test
298 * whatever the test buffer size is. Random buffer is read as a ring buffer.
299 */
300#define RANDOM_BUFFER_SIZE (32 * 1024 + 1)
301static int get_random_bytes(char *out, size_t len)
302{
303 static char *rand_buf = NULL;
304 static size_t rand_idx = 0;
305 int rc;
306
307 if (!rand_buf) {
308 const char rand_dev[] = "/dev/urandom";
309 int fd;
310
311 rand_buf = malloc(RANDOM_BUFFER_SIZE);
312 if (!rand_buf) {
313 fprintf(stderr, "failed to random buffer memory (%d bytes)\n",
314 RANDOM_BUFFER_SIZE);
315 return -1;
316 }
317
318 fd = open(rand_dev, O_RDONLY);
319 if (fd < 0) {
320 fprintf(stderr, "failed to open %s\n", rand_dev);
321 return -1;
322 }
323
324 rc = read(fd, rand_buf, RANDOM_BUFFER_SIZE);
325 if (rc != RANDOM_BUFFER_SIZE) {
326 fprintf(stderr, "failed to read %d bytes from %s\n",
327 RANDOM_BUFFER_SIZE, rand_dev);
328 return -1;
329 }
330 close(fd);
331 }
332
333 while (len) {
334 size_t t_len = (RANDOM_BUFFER_SIZE < len) ? RANDOM_BUFFER_SIZE : len;
335
336 if ((rand_idx + t_len) > RANDOM_BUFFER_SIZE) {
337 int sz_end = RANDOM_BUFFER_SIZE - rand_idx;
338 int sz_beg = t_len - sz_end;
339
340 memcpy(out, rand_buf + rand_idx, sz_end);
341 memcpy(out + sz_end, rand_buf , sz_beg);
342 rand_idx = sz_beg;
343 } else {
344 memcpy(out, rand_buf + rand_idx, t_len);
345 rand_idx += t_len;
346 }
347 len -= t_len;
348 }
349 return 0;
350}
351
352
353static int sdp_basic_test(size_t size, size_t loop, int ion_heap, int rnd_offset)
354{
355 struct tee_ctx *ctx = NULL;
356 unsigned char *test_buf = NULL;
357 unsigned char *ref_buf = NULL;
358 void *shm_ref = NULL;
359 unsigned int err = 1;
360 int fd = -1;
361 size_t sdp_size = size;
362 size_t offset;
Etienne Carrieref690b912017-03-21 15:44:13 +0100363 size_t loop_cnt;
Etienne Carriere41343db2017-03-17 15:38:52 +0100364
365 if (!loop) {
366 fprintf(stderr, "Error: null loop value\n");
367 return 1;
368 }
369
370 /* reduce size to enable offset tests (max offset is 255 bytes) */
371 if (rnd_offset)
372 size -= 255;
373
374 test_buf = malloc(size);
375 ref_buf = malloc(size);
376 if (!test_buf || !ref_buf) {
377 verbose("failed to allocate memory\n");
378 goto out;
379 }
380
381 fd = allocate_ion_buffer(sdp_size, ion_heap);
382 if (fd < 0) {
383 verbose("Failed to allocate SDP buffer (%lu bytes) in ION heap %d: %d\n",
384 sdp_size, ion_heap, fd);
385 goto out;
386 }
387
388 /* register secure buffer to TEE */
389 ctx = malloc(sizeof(*ctx));
390 if (!ctx)
391 goto out;
392 if (create_tee_ctx(ctx))
393 goto out;
394 if (tee_register_buffer(ctx, &shm_ref, fd))
395 goto out;
396
397 /* release registered fd: tee should still hold refcount on resource */
398 close(fd);
399 fd = -1;
400
401 /* invoke trusted application with secure buffer as memref parameter */
Etienne Carrieref690b912017-03-21 15:44:13 +0100402 for (loop_cnt = loop; loop_cnt; loop_cnt--) {
Etienne Carriere41343db2017-03-17 15:38:52 +0100403 /* get an buffer of random-like values */
404 if (get_random_bytes((char *)ref_buf, size))
405 goto out;
406 memcpy(test_buf, ref_buf, size);
407 /* random offset [0 255] */
408 offset = (unsigned int)*ref_buf;
409
410 /* TA writes into SDP buffer */
Etienne Carrieref690b912017-03-21 15:44:13 +0100411 if (inject_sdp_data(ctx, test_buf, offset, size, shm_ref, 0))
Etienne Carriere41343db2017-03-17 15:38:52 +0100412 goto out;
413
414 /* TA reads/writes into SDP buffer */
Etienne Carrieref690b912017-03-21 15:44:13 +0100415 if (transform_sdp_data(ctx, offset, size, shm_ref, 0))
Etienne Carriere41343db2017-03-17 15:38:52 +0100416 goto out;
417
418 /* TA reads into SDP buffer */
Etienne Carrieref690b912017-03-21 15:44:13 +0100419 if (dump_sdp_data(ctx, test_buf, offset, size, shm_ref, 0))
Etienne Carriere41343db2017-03-17 15:38:52 +0100420 goto out;
421
422 /* check dumped data are the expected ones */
423 if (check_sdp_dumped(ctx, ref_buf, size, test_buf)) {
424 fprintf(stderr, "check SDP data: %d errors\n", err);
425 goto out;
426 }
427 }
Etienne Carrieref690b912017-03-21 15:44:13 +0100428
429 /* invoke trusted application with secure buffer as memref parameter */
430 for (loop_cnt = loop; loop_cnt; loop_cnt--) {
431 /* get an buffer of random-like values */
432 if (get_random_bytes((char *)ref_buf, size))
433 goto out;
434 memcpy(test_buf, ref_buf, size);
435 /* random offset [0 255] */
436 offset = (unsigned int)*ref_buf;
437
438 /* TA writes into SDP buffer */
439 if (inject_sdp_data(ctx, test_buf, offset, size, shm_ref, 1))
440 goto out;
441
442 /* TA reads/writes into SDP buffer */
443 if (transform_sdp_data(ctx, offset, size, shm_ref, 1))
444 goto out;
445
446 /* TA reads into SDP buffer */
447 if (dump_sdp_data(ctx, test_buf, offset, size, shm_ref, 1))
448 goto out;
449
450 /* check dumped data are the expected ones */
451 if (check_sdp_dumped(ctx, ref_buf, size, test_buf)) {
452 fprintf(stderr, "check SDP data: %d errors\n", err);
453 goto out;
454 }
455 }
456
Etienne Carriere41343db2017-03-17 15:38:52 +0100457 err = 0;
458 verbose("%s: successed\n", __func__);
459out:
460 if (err)
461 verbose("test failed\n");
462 if (fd >= 0)
463 close(fd);
464 if (shm_ref)
465 tee_deregister_buffer(ctx, shm_ref);
466 finalize_tee_ctx(ctx);
467 free(ctx);
468 free(ref_buf);
469 free(test_buf);
470 return err;
471}
472
473#define _TO_STR(x) #x
474#define TO_STR(x) _TO_STR(x)
475
476static void usage(const char *progname, size_t size, int loop, int ion_heap)
477{
478 fprintf(stderr, "Usage: %s [OPTION]\n", progname);
479 fprintf(stderr,
480 "Testing basic accesses to secure buffer (SDP) on OP-TEE.\n"
481 "Allocates a secure buffer and invoke a TA to access it.\n"
482 "TA is used to init/transform/dump the secure buffer.\n"
483 "CA check dumped content.\n\n");
484
485 fprintf(stderr, "Options:\n");
486 fprintf(stderr, " -h|--help Print this help and exit\n");
487 fprintf(stderr, " -v Be verbose\n");
488 fprintf(stderr, " -s SIZE SDP buffer byte size [%zu]\n", size);
489 fprintf(stderr, " -n LOOP Test loop iterations [%u]\n", loop);
490 fprintf(stderr, " --ion-heap ID Target ION heap ID [%d]\n", ion_heap);
491 fprintf(stderr, " --no-offset No random offset [0 255] in buffer\n");
492}
493
494#define NEXT_ARG(i) \
495 do { \
496 if (++i == argc) { \
497 fprintf(stderr, "%s: %s: missing argument\n", \
498 argv[0], argv[i-1]); \
499 return 1; \
500 } \
501 } while (0);
502
503int sdp_basic_runner_cmd_parser(int argc, char *argv[])
504{
505 size_t test_size = 5000;
506 size_t test_loop = 1000;
507 int ion_heap = DEFAULT_ION_HEAP_TYPE;
508 int rnd_offset = 1;
509 int i;
510
511 /* Parse command line */
512 for (i = 1; i < argc; i++) {
513 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
514 usage(argv[0], test_size, test_loop, ion_heap);
515 return 0;
516 }
517 }
518 for (i = 1; i < argc; i++) {
519 if (!strcmp(argv[i], "-v")) {
520 verbosity++;
521 } else if (!strcmp(argv[i], "-s")) {
522 NEXT_ARG(i);
523 test_size = atoi(argv[i]);
524 } else if (!strcmp(argv[i], "-n")) {
525 NEXT_ARG(i);
526 test_loop = atoi(argv[i]);
527 } else if (!strcmp(argv[i], "--ion-heap")) {
528 NEXT_ARG(i);
529 ion_heap = atoi(argv[i]);
530 } else if (!strcmp(argv[i], "--no-offset")) {
531 rnd_offset = 0;
532 } else {
533 fprintf(stderr, "%s: invalid argument: %s\n",
534 argv[0], argv[i]);
535 usage(argv[0], test_size, test_loop, ion_heap);
536 return 1;
537 }
538 }
539
540 verbose("Secure Data Path basic accesses from trusted applications\n");
541
542 if (sdp_basic_test(test_size, test_loop, ion_heap, rnd_offset))
543 return 1;
544
545 return 0;
546}