blob: a0fb3ab471af56b03a745da1d77c5fbed979c89b [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,
197 void *in, size_t offset, size_t len, void *shm_ref)
198{
199 TEEC_SharedMemory *shm = (TEEC_SharedMemory *)shm_ref;
200 TEEC_Result teerc;
201 TEEC_Operation op;
202 uint32_t err_origin;
203
204 memset(&op, 0, sizeof(op));
205 op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
206 TEEC_MEMREF_PARTIAL_OUTPUT,
207 TEEC_NONE, TEEC_NONE);
208
209 op.params[0].tmpref.buffer = in;
210 op.params[0].tmpref.size = len;
211
212 op.params[1].memref.parent = shm;
213 op.params[1].memref.size = len;
214 op.params[1].memref.offset = offset;
215
216 teerc = TEEC_InvokeCommand(&ctx->sess, TA_SDP_BASIC_CMD_INJECT, &op,
217 &err_origin);
218 if (teerc != TEEC_SUCCESS)
219 fprintf(stderr, "Error: invoke SDP test TA (inject) failed %x %d\n",
220 teerc, err_origin);
221
222 return (teerc == TEEC_SUCCESS) ? 0 : -1;
223}
224
225static int transform_sdp_data(struct tee_ctx *ctx,
226 size_t offset, size_t len, void *shm_ref)
227{
228 TEEC_SharedMemory *shm = (TEEC_SharedMemory *)shm_ref;
229 TEEC_Result teerc;
230 TEEC_Operation op;
231 uint32_t err_origin;
232
233 memset(&op, 0, sizeof(op));
234 op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_PARTIAL_INOUT,
235 TEEC_NONE, TEEC_NONE, TEEC_NONE);
236 op.params[0].memref.parent = shm;
237 op.params[0].memref.size = len;
238 op.params[0].memref.offset = offset;
239
240 teerc = TEEC_InvokeCommand(&ctx->sess, TA_SDP_BASIC_CMD_TRANSFORM, &op,
241 &err_origin);
242 if (teerc != TEEC_SUCCESS)
243 fprintf(stderr, "Error: invoke SDP test TA (transform) failed %x %d\n",
244 teerc, err_origin);
245
246 return (teerc == TEEC_SUCCESS) ? 0 : -1;
247}
248
249static int dump_sdp_data(struct tee_ctx *ctx,
250 void *out, size_t offset, size_t len, void *shm_ref)
251{
252 TEEC_SharedMemory *shm = (TEEC_SharedMemory *)shm_ref;
253 TEEC_Result teerc;
254 TEEC_Operation op;
255 uint32_t err_origin;
256
257 memset(&op, 0, sizeof(op));
258 op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_PARTIAL_INPUT,
259 TEEC_MEMREF_TEMP_OUTPUT,
260 TEEC_NONE, TEEC_NONE);
261 op.params[0].memref.parent = shm;
262 op.params[0].memref.size = len;
263 op.params[0].memref.offset = offset;
264
265 op.params[1].tmpref.buffer = out;
266 op.params[1].tmpref.size = len;
267
268 teerc = TEEC_InvokeCommand(&ctx->sess, TA_SDP_BASIC_CMD_DUMP, &op,
269 &err_origin);
270 if (teerc != TEEC_SUCCESS)
271 fprintf(stderr, "Error: invoke SDP test TA (dump) failed %x %d\n",
272 teerc, err_origin);
273
274 return (teerc == TEEC_SUCCESS) ? 0 : -1;
275}
276
277static int check_sdp_dumped(struct tee_ctx *ctx, void *ref, size_t len,
278 void *out)
279{
280 char *bref = (char *)ref;
281 char *data = (char *)out;
282 int err = 0;
283
284 (void)ctx;
285
286 while(len--)
287 if (*data++ != (unsigned char)(~(*bref++) + 1))
288 err++;
289
290 return err;
291}
292
293/*
294 * Consider 32kByte + 1 of random data is sufficient for an accurate test
295 * whatever the test buffer size is. Random buffer is read as a ring buffer.
296 */
297#define RANDOM_BUFFER_SIZE (32 * 1024 + 1)
298static int get_random_bytes(char *out, size_t len)
299{
300 static char *rand_buf = NULL;
301 static size_t rand_idx = 0;
302 int rc;
303
304 if (!rand_buf) {
305 const char rand_dev[] = "/dev/urandom";
306 int fd;
307
308 rand_buf = malloc(RANDOM_BUFFER_SIZE);
309 if (!rand_buf) {
310 fprintf(stderr, "failed to random buffer memory (%d bytes)\n",
311 RANDOM_BUFFER_SIZE);
312 return -1;
313 }
314
315 fd = open(rand_dev, O_RDONLY);
316 if (fd < 0) {
317 fprintf(stderr, "failed to open %s\n", rand_dev);
318 return -1;
319 }
320
321 rc = read(fd, rand_buf, RANDOM_BUFFER_SIZE);
322 if (rc != RANDOM_BUFFER_SIZE) {
323 fprintf(stderr, "failed to read %d bytes from %s\n",
324 RANDOM_BUFFER_SIZE, rand_dev);
325 return -1;
326 }
327 close(fd);
328 }
329
330 while (len) {
331 size_t t_len = (RANDOM_BUFFER_SIZE < len) ? RANDOM_BUFFER_SIZE : len;
332
333 if ((rand_idx + t_len) > RANDOM_BUFFER_SIZE) {
334 int sz_end = RANDOM_BUFFER_SIZE - rand_idx;
335 int sz_beg = t_len - sz_end;
336
337 memcpy(out, rand_buf + rand_idx, sz_end);
338 memcpy(out + sz_end, rand_buf , sz_beg);
339 rand_idx = sz_beg;
340 } else {
341 memcpy(out, rand_buf + rand_idx, t_len);
342 rand_idx += t_len;
343 }
344 len -= t_len;
345 }
346 return 0;
347}
348
349
350static int sdp_basic_test(size_t size, size_t loop, int ion_heap, int rnd_offset)
351{
352 struct tee_ctx *ctx = NULL;
353 unsigned char *test_buf = NULL;
354 unsigned char *ref_buf = NULL;
355 void *shm_ref = NULL;
356 unsigned int err = 1;
357 int fd = -1;
358 size_t sdp_size = size;
359 size_t offset;
360
361 if (!loop) {
362 fprintf(stderr, "Error: null loop value\n");
363 return 1;
364 }
365
366 /* reduce size to enable offset tests (max offset is 255 bytes) */
367 if (rnd_offset)
368 size -= 255;
369
370 test_buf = malloc(size);
371 ref_buf = malloc(size);
372 if (!test_buf || !ref_buf) {
373 verbose("failed to allocate memory\n");
374 goto out;
375 }
376
377 fd = allocate_ion_buffer(sdp_size, ion_heap);
378 if (fd < 0) {
379 verbose("Failed to allocate SDP buffer (%lu bytes) in ION heap %d: %d\n",
380 sdp_size, ion_heap, fd);
381 goto out;
382 }
383
384 /* register secure buffer to TEE */
385 ctx = malloc(sizeof(*ctx));
386 if (!ctx)
387 goto out;
388 if (create_tee_ctx(ctx))
389 goto out;
390 if (tee_register_buffer(ctx, &shm_ref, fd))
391 goto out;
392
393 /* release registered fd: tee should still hold refcount on resource */
394 close(fd);
395 fd = -1;
396
397 /* invoke trusted application with secure buffer as memref parameter */
398 while (loop--) {
399 /* get an buffer of random-like values */
400 if (get_random_bytes((char *)ref_buf, size))
401 goto out;
402 memcpy(test_buf, ref_buf, size);
403 /* random offset [0 255] */
404 offset = (unsigned int)*ref_buf;
405
406 /* TA writes into SDP buffer */
407 if (inject_sdp_data(ctx, test_buf, offset, size, shm_ref))
408 goto out;
409
410 /* TA reads/writes into SDP buffer */
411 if (transform_sdp_data(ctx, offset, size, shm_ref))
412 goto out;
413
414 /* TA reads into SDP buffer */
415 if (dump_sdp_data(ctx, test_buf, offset, size, shm_ref))
416 goto out;
417
418 /* check dumped data are the expected ones */
419 if (check_sdp_dumped(ctx, ref_buf, size, test_buf)) {
420 fprintf(stderr, "check SDP data: %d errors\n", err);
421 goto out;
422 }
423 }
424 err = 0;
425 verbose("%s: successed\n", __func__);
426out:
427 if (err)
428 verbose("test failed\n");
429 if (fd >= 0)
430 close(fd);
431 if (shm_ref)
432 tee_deregister_buffer(ctx, shm_ref);
433 finalize_tee_ctx(ctx);
434 free(ctx);
435 free(ref_buf);
436 free(test_buf);
437 return err;
438}
439
440#define _TO_STR(x) #x
441#define TO_STR(x) _TO_STR(x)
442
443static void usage(const char *progname, size_t size, int loop, int ion_heap)
444{
445 fprintf(stderr, "Usage: %s [OPTION]\n", progname);
446 fprintf(stderr,
447 "Testing basic accesses to secure buffer (SDP) on OP-TEE.\n"
448 "Allocates a secure buffer and invoke a TA to access it.\n"
449 "TA is used to init/transform/dump the secure buffer.\n"
450 "CA check dumped content.\n\n");
451
452 fprintf(stderr, "Options:\n");
453 fprintf(stderr, " -h|--help Print this help and exit\n");
454 fprintf(stderr, " -v Be verbose\n");
455 fprintf(stderr, " -s SIZE SDP buffer byte size [%zu]\n", size);
456 fprintf(stderr, " -n LOOP Test loop iterations [%u]\n", loop);
457 fprintf(stderr, " --ion-heap ID Target ION heap ID [%d]\n", ion_heap);
458 fprintf(stderr, " --no-offset No random offset [0 255] in buffer\n");
459}
460
461#define NEXT_ARG(i) \
462 do { \
463 if (++i == argc) { \
464 fprintf(stderr, "%s: %s: missing argument\n", \
465 argv[0], argv[i-1]); \
466 return 1; \
467 } \
468 } while (0);
469
470int sdp_basic_runner_cmd_parser(int argc, char *argv[])
471{
472 size_t test_size = 5000;
473 size_t test_loop = 1000;
474 int ion_heap = DEFAULT_ION_HEAP_TYPE;
475 int rnd_offset = 1;
476 int i;
477
478 /* Parse command line */
479 for (i = 1; i < argc; i++) {
480 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
481 usage(argv[0], test_size, test_loop, ion_heap);
482 return 0;
483 }
484 }
485 for (i = 1; i < argc; i++) {
486 if (!strcmp(argv[i], "-v")) {
487 verbosity++;
488 } else if (!strcmp(argv[i], "-s")) {
489 NEXT_ARG(i);
490 test_size = atoi(argv[i]);
491 } else if (!strcmp(argv[i], "-n")) {
492 NEXT_ARG(i);
493 test_loop = atoi(argv[i]);
494 } else if (!strcmp(argv[i], "--ion-heap")) {
495 NEXT_ARG(i);
496 ion_heap = atoi(argv[i]);
497 } else if (!strcmp(argv[i], "--no-offset")) {
498 rnd_offset = 0;
499 } else {
500 fprintf(stderr, "%s: invalid argument: %s\n",
501 argv[0], argv[i]);
502 usage(argv[0], test_size, test_loop, ion_heap);
503 return 1;
504 }
505 }
506
507 verbose("Secure Data Path basic accesses from trusted applications\n");
508
509 if (sdp_basic_test(test_size, test_loop, ion_heap, rnd_offset))
510 return 1;
511
512 return 0;
513}