1 /* 2 * Copyright (c) 2014, STMicroelectronics International N.V. 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 <kernel/tee_ta_manager.h> 29 #include <kernel/tee_misc.h> 30 #include <mm/tee_mmu.h> 31 #include <tee/tee_fs.h> 32 #include <tee/tee_fs_defs.h> 33 #include <tee/tee_obj.h> 34 #include <tee/tee_svc.h> 35 #include <tee/tee_pobj.h> 36 #include <tee/tee_svc_storage.h> 37 #include <tee/tee_svc_cryp.h> 38 #include <tee_api_defines.h> 39 #include <tee_api_defines_extensions.h> 40 #include <trace.h> 41 42 /* 43 * Returns the appropriate tee_file_operations for the specified storage ID. 44 * The value TEE_STORAGE_PRIVATE will select the REE FS if available, otherwise 45 * RPMB. 46 */ 47 static const struct tee_file_operations *file_ops(uint32_t storage_id) 48 { 49 50 switch (storage_id) { 51 case TEE_STORAGE_PRIVATE: 52 #if defined(CFG_REE_FS) 53 return &ree_fs_ops; 54 #elif defined(CFG_RPMB_FS) 55 return &rpmb_fs_ops; 56 #else 57 #error At least one filesystem must be enabled. 58 #endif 59 #ifdef CFG_REE_FS 60 case TEE_STORAGE_PRIVATE_REE: 61 return &ree_fs_ops; 62 #endif 63 #ifdef CFG_RPMB_FS 64 case TEE_STORAGE_PRIVATE_RPMB: 65 return &rpmb_fs_ops; 66 #endif 67 default: 68 return NULL; 69 } 70 } 71 72 /* SSF (Secure Storage File version 00 */ 73 #define TEE_SVC_STORAGE_MAGIC 0x53534600; 74 75 /* Header of GP formated secure storage files */ 76 struct tee_svc_storage_head { 77 uint32_t magic; 78 uint32_t head_size; 79 uint32_t meta_size; 80 uint32_t ds_size; 81 uint32_t keySize; 82 uint32_t maxKeySize; 83 uint32_t objectUsage; 84 uint32_t objectType; 85 uint32_t have_attrs; 86 }; 87 88 struct tee_storage_enum { 89 TAILQ_ENTRY(tee_storage_enum) link; 90 struct tee_fs_dir *dir; 91 const struct tee_file_operations *fops; 92 }; 93 94 static TEE_Result tee_svc_storage_get_enum(struct user_ta_ctx *utc, 95 uint32_t enum_id, 96 struct tee_storage_enum **e_out) 97 { 98 struct tee_storage_enum *e; 99 100 TAILQ_FOREACH(e, &utc->storage_enums, link) { 101 if (enum_id == (vaddr_t)e) { 102 *e_out = e; 103 return TEE_SUCCESS; 104 } 105 } 106 return TEE_ERROR_BAD_PARAMETERS; 107 } 108 109 static TEE_Result tee_svc_close_enum(struct user_ta_ctx *utc, 110 struct tee_storage_enum *e) 111 { 112 int ret; 113 114 if (e == NULL || utc == NULL) 115 return TEE_ERROR_BAD_PARAMETERS; 116 117 TAILQ_REMOVE(&utc->storage_enums, e, link); 118 119 if (!e->fops) 120 return TEE_ERROR_ITEM_NOT_FOUND; 121 122 ret = e->fops->closedir(e->dir); 123 e->dir = NULL; 124 e->fops = NULL; 125 126 free(e); 127 128 if (ret != 0) 129 return TEE_ERROR_ITEM_NOT_FOUND; 130 131 return TEE_SUCCESS; 132 } 133 134 char *tee_svc_storage_create_filename(struct tee_ta_session *sess, 135 void *object_id, 136 uint32_t object_id_len, 137 bool transient) 138 { 139 uint8_t *file = NULL; 140 /* +1 for the '/' (default) */ 141 uint32_t hslen = 142 TEE_B2HS_HSBUF_SIZE(sizeof(TEE_UUID) + object_id_len) + 1; 143 uint32_t pos; 144 145 /* +1 for the '.' (temporary persistent object) */ 146 if (transient) 147 hslen++; 148 149 file = malloc(hslen); 150 151 if (file == NULL) 152 return NULL; 153 154 pos = tee_b2hs((uint8_t *)&sess->ctx->uuid, file, 155 sizeof(TEE_UUID), hslen); 156 file[pos] = '/'; 157 pos++; 158 159 /* temporary persistent object : uuid/.object_id_len_of(object_id) */ 160 if (transient) { 161 file[pos] = '.'; 162 pos++; 163 } 164 165 tee_b2hs(object_id, file + pos, object_id_len, hslen - pos); 166 167 return (char *)file; 168 } 169 170 char *tee_svc_storage_create_dirname(struct tee_ta_session *sess) 171 { 172 uint8_t *dir = NULL; 173 uint32_t hslen = TEE_B2HS_HSBUF_SIZE(sizeof(TEE_UUID)); 174 175 dir = malloc(hslen); 176 177 if (dir == NULL) 178 return NULL; 179 180 tee_b2hs((uint8_t *)&sess->ctx->uuid, dir, sizeof(TEE_UUID), 181 hslen); 182 183 return (char *)dir; 184 } 185 186 static TEE_Result tee_svc_storage_remove_corrupt_obj( 187 struct tee_ta_session *sess, 188 struct tee_obj *o) 189 { 190 TEE_Result res; 191 char *file = NULL; 192 char *dir = NULL; 193 const struct tee_file_operations *fops = o->pobj->fops; 194 195 file = tee_svc_storage_create_filename(sess, 196 o->pobj->obj_id, 197 o->pobj->obj_id_len, 198 false); 199 if (file == NULL) { 200 res = TEE_ERROR_OUT_OF_MEMORY; 201 goto exit; 202 } 203 204 tee_obj_close(to_user_ta_ctx(sess->ctx), o); 205 fops->unlink(file); 206 free(file); 207 dir = tee_svc_storage_create_dirname(sess); 208 if (dir != NULL) { 209 fops->rmdir(dir); 210 free(dir); 211 } 212 213 res = TEE_SUCCESS; 214 215 exit: 216 return res; 217 } 218 219 static uint32_t tee_svc_storage_conv_oflags(uint32_t flags) 220 { 221 uint32_t out = 0; 222 223 if (flags & (TEE_DATA_FLAG_ACCESS_READ | TEE_DATA_FLAG_SHARE_READ)) { 224 if (flags & (TEE_DATA_FLAG_ACCESS_WRITE | 225 TEE_DATA_FLAG_ACCESS_WRITE_META | 226 TEE_DATA_FLAG_SHARE_WRITE)) 227 out |= TEE_FS_O_RDWR; 228 else 229 out |= TEE_FS_O_RDONLY; 230 } else { 231 if (flags & (TEE_DATA_FLAG_ACCESS_WRITE | 232 TEE_DATA_FLAG_ACCESS_WRITE_META | 233 TEE_DATA_FLAG_SHARE_WRITE)) 234 out |= TEE_FS_O_WRONLY; 235 } 236 237 return out; 238 } 239 240 static int tee_svc_storage_conv_whence(TEE_Whence whence) 241 { 242 switch (whence) { 243 case TEE_DATA_SEEK_SET: 244 return TEE_FS_SEEK_SET; 245 case TEE_DATA_SEEK_CUR: 246 return TEE_FS_SEEK_CUR; 247 case TEE_DATA_SEEK_END: 248 return TEE_FS_SEEK_END; 249 default: 250 return -1; 251 } 252 } 253 254 static TEE_Result tee_svc_storage_create_file(struct tee_ta_session *sess, 255 char *file, 256 const struct tee_file_operations *fops, 257 int *fd) 258 { 259 TEE_Result res = TEE_SUCCESS; 260 char *dir = NULL; 261 int tmp; 262 int err; 263 uint32_t cflags = TEE_FS_O_WRONLY | 264 TEE_FS_O_CREATE | TEE_FS_O_TRUNC; 265 266 dir = tee_svc_storage_create_dirname(sess); 267 if (dir == NULL) { 268 res = TEE_ERROR_OUT_OF_MEMORY; 269 goto exit; 270 } 271 272 /* try and make directory */ 273 err = fops->access(dir, TEE_FS_F_OK); 274 if (err) { 275 /* directory does not exists */ 276 tmp = fops->mkdir(dir, TEE_FS_S_IRUSR | TEE_FS_S_IWUSR); 277 278 if (tmp < 0) { 279 /* error codes needs better granularity */ 280 res = TEE_ERROR_GENERIC; 281 goto exit; 282 } 283 } 284 285 /* try and open again */ 286 *fd = fops->open(&res, file, cflags); 287 288 exit: 289 free(dir); 290 291 return res; 292 } 293 294 static TEE_Result tee_svc_storage_read_head(struct tee_ta_session *sess, 295 struct tee_obj *o) 296 { 297 TEE_Result res = TEE_SUCCESS; 298 int fd = -1; 299 int err; 300 struct tee_svc_storage_head head; 301 char *file = NULL; 302 const struct tee_file_operations *fops; 303 void *attr = NULL; 304 305 if (o == NULL || o->pobj == NULL) 306 return TEE_ERROR_BAD_PARAMETERS; 307 308 fops = o->pobj->fops; 309 310 file = tee_svc_storage_create_filename(sess, 311 o->pobj->obj_id, 312 o->pobj->obj_id_len, 313 false); 314 if (file == NULL) { 315 res = TEE_ERROR_OUT_OF_MEMORY; 316 goto exit; 317 } 318 319 fd = fops->open(&res, file, TEE_FS_O_RDONLY); 320 free(file); 321 if (fd < 0) 322 goto exit; 323 324 /* read head */ 325 err = fops->read(&res, fd, &head, 326 sizeof(struct tee_svc_storage_head)); 327 if (err < 0) { 328 if (res == TEE_ERROR_CORRUPT_OBJECT) 329 EMSG("Head corrupt\n"); 330 goto exit; 331 } 332 333 if (err != sizeof(struct tee_svc_storage_head)) { 334 res = TEE_ERROR_BAD_FORMAT; 335 goto exit; 336 } 337 338 res = tee_obj_set_type(o, head.objectType, head.maxKeySize); 339 if (res != TEE_SUCCESS) 340 goto exit; 341 342 if (head.meta_size) { 343 attr = malloc(head.meta_size); 344 if (!attr) { 345 res = TEE_ERROR_OUT_OF_MEMORY; 346 goto exit; 347 } 348 349 /* read meta */ 350 err = fops->read(&res, fd, attr, head.meta_size); 351 if (err != (int)head.meta_size) { 352 res = TEE_ERROR_CORRUPT_OBJECT; 353 goto exit; 354 } 355 } 356 357 res = tee_obj_attr_from_binary(o, attr, head.meta_size); 358 if (res != TEE_SUCCESS) 359 goto exit; 360 361 o->info.dataSize = head.ds_size; 362 o->info.keySize = head.keySize; 363 o->info.objectUsage = head.objectUsage; 364 o->info.objectType = head.objectType; 365 o->have_attrs = head.have_attrs; 366 367 exit: 368 free(attr); 369 if (fd >= 0) 370 fops->close(fd); 371 372 return res; 373 } 374 375 static TEE_Result tee_svc_storage_init_file(struct tee_ta_session *sess, 376 struct tee_obj *o, 377 struct tee_obj *attr_o, void *data, 378 uint32_t len) 379 { 380 TEE_Result res = TEE_SUCCESS; 381 int fd = -1; 382 int err = -1; 383 struct tee_svc_storage_head head; 384 char *tmpfile = NULL; 385 const struct tee_file_operations *fops; 386 void *attr = NULL; 387 size_t attr_size = 0; 388 389 if (o == NULL || o->pobj == NULL) 390 return TEE_ERROR_BAD_PARAMETERS; 391 392 fops = o->pobj->fops; 393 394 /* create temporary persistent object filename */ 395 tmpfile = tee_svc_storage_create_filename(sess, 396 o->pobj->obj_id, 397 o->pobj->obj_id_len, 398 true); 399 400 if (tmpfile == NULL) { 401 res = TEE_ERROR_OUT_OF_MEMORY; 402 goto exit; 403 } 404 405 res = tee_svc_storage_create_file(sess, tmpfile, fops, &fd); 406 if (res != TEE_SUCCESS) 407 goto exit; 408 409 if (attr_o) { 410 res = tee_obj_set_type(o, attr_o->info.objectType, 411 attr_o->info.maxKeySize); 412 if (res != TEE_SUCCESS) 413 goto exit; 414 res = tee_obj_attr_copy_from(o, attr_o); 415 if (res != TEE_SUCCESS) 416 goto exit; 417 o->have_attrs = attr_o->have_attrs; 418 o->info.objectUsage = attr_o->info.objectUsage; 419 o->info.keySize = attr_o->info.keySize; 420 res = tee_obj_attr_to_binary(o, NULL, &attr_size); 421 if (res != TEE_SUCCESS) 422 goto exit; 423 if (attr_size) { 424 attr = malloc(attr_size); 425 if (!attr) { 426 res = TEE_ERROR_OUT_OF_MEMORY; 427 goto exit; 428 } 429 res = tee_obj_attr_to_binary(o, attr, &attr_size); 430 if (res != TEE_SUCCESS) 431 goto exit; 432 } 433 } else { 434 res = tee_obj_set_type(o, TEE_TYPE_DATA, 0); 435 if (res != TEE_SUCCESS) 436 goto exit; 437 } 438 439 /* write head */ 440 head.magic = TEE_SVC_STORAGE_MAGIC; 441 head.head_size = sizeof(struct tee_svc_storage_head); 442 head.meta_size = attr_size; 443 head.ds_size = len; 444 head.keySize = o->info.keySize; 445 head.maxKeySize = o->info.maxKeySize; 446 head.objectUsage = o->info.objectUsage; 447 head.objectType = o->info.objectType; 448 head.have_attrs = o->have_attrs; 449 450 /* write head */ 451 err = fops->write(&res, fd, &head, 452 sizeof(struct tee_svc_storage_head)); 453 /* error codes needs better granularity */ 454 if (err != sizeof(struct tee_svc_storage_head)) 455 goto exit; 456 457 /* write meta */ 458 err = fops->write(&res, fd, attr, attr_size); 459 if (err != (int)attr_size) 460 goto exit; 461 462 /* write init data */ 463 o->info.dataSize = len; 464 465 /* write data to fs if needed */ 466 if (data && len) { 467 err = fops->write(&res, fd, data, len); 468 if (err != (int)len) 469 goto exit; 470 } 471 472 exit: 473 free(attr); 474 free(tmpfile); 475 tmpfile = NULL; 476 if (fd != -1) 477 fops->close(fd); 478 479 return res; 480 } 481 482 TEE_Result syscall_storage_obj_open(unsigned long storage_id, void *object_id, 483 size_t object_id_len, unsigned long flags, 484 uint32_t *obj) 485 { 486 TEE_Result res; 487 struct tee_ta_session *sess; 488 struct tee_obj *o; 489 char *file = NULL; 490 int fs_flags; 491 int fd = -1; 492 tee_fs_off_t off; 493 tee_fs_off_t e_off; 494 struct tee_pobj *po = NULL; 495 int err = -1; 496 struct user_ta_ctx *utc; 497 const struct tee_file_operations *fops = file_ops(storage_id); 498 size_t attr_size; 499 500 if (!fops) { 501 res = TEE_ERROR_STORAGE_NOT_AVAILABLE; 502 goto exit; 503 } 504 505 if (object_id_len > TEE_OBJECT_ID_MAX_LEN) { 506 res = TEE_ERROR_BAD_PARAMETERS; 507 goto exit; 508 } 509 510 res = tee_ta_get_current_session(&sess); 511 if (res != TEE_SUCCESS) 512 goto err; 513 utc = to_user_ta_ctx(sess->ctx); 514 515 res = tee_mmu_check_access_rights(utc, 516 TEE_MEMORY_ACCESS_READ | 517 TEE_MEMORY_ACCESS_ANY_OWNER, 518 (tee_uaddr_t) object_id, 519 object_id_len); 520 if (res != TEE_SUCCESS) 521 goto err; 522 523 res = tee_pobj_get((void *)&sess->ctx->uuid, object_id, 524 object_id_len, flags, fops, &po); 525 if (res != TEE_SUCCESS) 526 goto err; 527 528 fs_flags = tee_svc_storage_conv_oflags(flags); 529 530 o = tee_obj_alloc(); 531 if (o == NULL) { 532 res = TEE_ERROR_OUT_OF_MEMORY; 533 goto err; 534 } 535 536 o->info.handleFlags = 537 TEE_HANDLE_FLAG_PERSISTENT | TEE_HANDLE_FLAG_INITIALIZED; 538 o->flags = flags; 539 o->pobj = po; 540 541 res = tee_svc_storage_read_head(sess, o); 542 if (res != TEE_SUCCESS) { 543 tee_obj_add(utc, o); 544 if (res == TEE_ERROR_CORRUPT_OBJECT) { 545 EMSG("Object corrupt\n"); 546 res = tee_svc_storage_remove_corrupt_obj(sess, o); 547 if (res != TEE_SUCCESS) 548 goto exit; 549 res = TEE_ERROR_CORRUPT_OBJECT; 550 goto exit; 551 } 552 goto oclose; 553 } 554 555 file = tee_svc_storage_create_filename(sess, object_id, 556 object_id_len, false); 557 if (file == NULL) { 558 res = TEE_ERROR_OUT_OF_MEMORY; 559 goto err; 560 } 561 562 err = fops->access(file, TEE_FS_F_OK); 563 if (err) { 564 /* file not found */ 565 res = TEE_ERROR_STORAGE_NOT_AVAILABLE; 566 goto err; 567 } 568 569 fd = fops->open(&res, file, fs_flags); 570 if (fd < 0) { 571 goto err; 572 } 573 o->fd = fd; 574 575 tee_obj_add(utc, o); 576 577 res = tee_svc_copy_kaddr_to_uref(obj, o); 578 if (res != TEE_SUCCESS) 579 goto oclose; 580 581 res = tee_obj_attr_to_binary(o, NULL, &attr_size); 582 if (res != TEE_SUCCESS && res) 583 goto oclose; 584 585 e_off = sizeof(struct tee_svc_storage_head) + attr_size; 586 off = fops->lseek(&res, fd, e_off, TEE_FS_SEEK_SET); 587 if (off != e_off) { 588 res = TEE_ERROR_NO_DATA; 589 goto oclose; 590 } 591 592 goto exit; 593 594 oclose: 595 tee_obj_close(utc, o); 596 597 err: 598 if (res == TEE_ERROR_NO_DATA || res == TEE_ERROR_BAD_FORMAT) 599 res = TEE_ERROR_CORRUPT_OBJECT; 600 if (res == TEE_ERROR_CORRUPT_OBJECT) 601 fops->unlink(file); 602 if (fd >= 0) 603 fops->close(fd); 604 if (po) 605 tee_pobj_release(po); 606 607 exit: 608 free(file); 609 file = NULL; 610 return res; 611 } 612 613 TEE_Result syscall_storage_obj_create(unsigned long storage_id, void *object_id, 614 size_t object_id_len, unsigned long flags, 615 unsigned long attr, void *data, size_t len, 616 uint32_t *obj) 617 { 618 TEE_Result res; 619 struct tee_ta_session *sess; 620 struct tee_obj *o = NULL; 621 struct tee_obj *attr_o = NULL; 622 char *file = NULL; 623 int fd = -1; 624 int fs_flags; 625 tee_fs_off_t off; 626 tee_fs_off_t e_off; 627 struct tee_pobj *po = NULL; 628 char *tmpfile = NULL; 629 int err = -1; 630 int filedoesnotexist; 631 struct user_ta_ctx *utc; 632 const struct tee_file_operations *fops = file_ops(storage_id); 633 size_t attr_size; 634 635 if (!fops) 636 return TEE_ERROR_STORAGE_NOT_AVAILABLE; 637 638 if (object_id_len > TEE_OBJECT_ID_MAX_LEN) 639 return TEE_ERROR_BAD_PARAMETERS; 640 641 res = tee_ta_get_current_session(&sess); 642 if (res != TEE_SUCCESS) 643 return res; 644 utc = to_user_ta_ctx(sess->ctx); 645 646 res = tee_mmu_check_access_rights(utc, 647 TEE_MEMORY_ACCESS_READ | 648 TEE_MEMORY_ACCESS_ANY_OWNER, 649 (tee_uaddr_t) object_id, 650 object_id_len); 651 if (res != TEE_SUCCESS) 652 goto err; 653 654 res = tee_pobj_get((void *)&sess->ctx->uuid, object_id, 655 object_id_len, flags, fops, &po); 656 if (res != TEE_SUCCESS) 657 goto err; 658 659 /* check rights of the provided buffer */ 660 if (data && len) { 661 res = tee_mmu_check_access_rights(utc, 662 TEE_MEMORY_ACCESS_READ | 663 TEE_MEMORY_ACCESS_ANY_OWNER, 664 (tee_uaddr_t) data, len); 665 666 if (res != TEE_SUCCESS) 667 goto err; 668 } 669 670 o = tee_obj_alloc(); 671 if (o == NULL) { 672 res = TEE_ERROR_OUT_OF_MEMORY; 673 goto err; 674 } 675 676 o->info.handleFlags = 677 TEE_HANDLE_FLAG_PERSISTENT | TEE_HANDLE_FLAG_INITIALIZED; 678 o->flags = flags; 679 o->pobj = po; 680 681 if (attr != TEE_HANDLE_NULL) { 682 res = tee_obj_get(utc, tee_svc_uref_to_vaddr(attr), 683 &attr_o); 684 if (res != TEE_SUCCESS) 685 goto err; 686 } 687 688 res = tee_svc_storage_init_file(sess, o, attr_o, data, len); 689 if (res != TEE_SUCCESS) 690 goto err; 691 692 /* create persistent object filename */ 693 file = tee_svc_storage_create_filename(sess, object_id, 694 object_id_len, false); 695 if (file == NULL) { 696 res = TEE_ERROR_OUT_OF_MEMORY; 697 goto err; 698 } 699 700 filedoesnotexist = fops->access(file, TEE_FS_F_OK); 701 if (!filedoesnotexist) { 702 /* file exists */ 703 if (!(flags & TEE_DATA_FLAG_OVERWRITE)) { 704 res = TEE_ERROR_ACCESS_CONFLICT; 705 goto err; 706 } 707 } 708 709 /* create temporary persistent object filename */ 710 tmpfile = tee_svc_storage_create_filename(sess, object_id, 711 object_id_len, 712 true); 713 if (tmpfile == NULL) { 714 res = TEE_ERROR_OUT_OF_MEMORY; 715 goto err; 716 } 717 718 /* 719 * remove the file if it exists, because rename does not perform 720 * this operation. Note that it delete and rename should be atomic, 721 * which is not the case currently. 722 * Fixme: unlink must be removed once rename() support prior deletion 723 * of the new file name when it already exists. 724 */ 725 if (!filedoesnotexist) 726 fops->unlink(file); 727 /* rename temporary persistent object filename */ 728 err = fops->rename(tmpfile, file); 729 if (err) { 730 /* error codes needs better granularity */ 731 res = TEE_ERROR_GENERIC; 732 goto rmfile; 733 } 734 735 fs_flags = tee_svc_storage_conv_oflags(flags); 736 737 fd = fops->open(&res, file, fs_flags); 738 if (fd < 0) { 739 goto err; 740 } 741 o->fd = fd; 742 743 tee_obj_add(utc, o); 744 745 res = tee_svc_copy_kaddr_to_uref(obj, o); 746 if (res != TEE_SUCCESS) 747 goto oclose; 748 749 res = tee_obj_attr_to_binary(o, NULL, &attr_size); 750 if (res != TEE_SUCCESS) 751 goto oclose; 752 753 e_off = sizeof(struct tee_svc_storage_head) + attr_size; 754 off = fops->lseek(&res, fd, e_off, TEE_FS_SEEK_SET); 755 if (off != e_off) { 756 res = TEE_ERROR_NO_DATA; 757 goto oclose; 758 } 759 760 goto exit; 761 762 oclose: 763 tee_obj_close(utc, o); 764 goto exit; 765 766 rmfile: 767 fops->unlink(tmpfile); 768 769 err: 770 if (res == TEE_ERROR_NO_DATA || res == TEE_ERROR_BAD_FORMAT) 771 res = TEE_ERROR_CORRUPT_OBJECT; 772 if (res == TEE_ERROR_CORRUPT_OBJECT) 773 fops->unlink(file); 774 if (fd >= 0) 775 fops->close(fd); 776 if (po) 777 tee_pobj_release(po); 778 if (o) 779 free(o); 780 781 exit: 782 free(file); 783 file = NULL; 784 free(tmpfile); 785 tmpfile = NULL; 786 787 return res; 788 } 789 790 TEE_Result syscall_storage_obj_del(unsigned long obj) 791 { 792 TEE_Result res; 793 struct tee_ta_session *sess; 794 struct tee_obj *o; 795 int err; 796 char *file; 797 char *dir; 798 struct user_ta_ctx *utc; 799 const struct tee_file_operations *fops; 800 801 res = tee_ta_get_current_session(&sess); 802 if (res != TEE_SUCCESS) 803 return res; 804 utc = to_user_ta_ctx(sess->ctx); 805 806 res = tee_obj_get(utc, tee_svc_uref_to_vaddr(obj), &o); 807 if (res != TEE_SUCCESS) 808 return res; 809 810 if (!(o->flags & TEE_DATA_FLAG_ACCESS_WRITE_META)) 811 return TEE_ERROR_ACCESS_CONFLICT; 812 813 if (o->pobj == NULL || o->pobj->obj_id == NULL) 814 return TEE_ERROR_BAD_STATE; 815 816 file = tee_svc_storage_create_filename(sess, o->pobj->obj_id, 817 o->pobj->obj_id_len, false); 818 if (file == NULL) 819 return TEE_ERROR_OUT_OF_MEMORY; 820 821 fops = o->pobj->fops; 822 tee_obj_close(utc, o); 823 824 err = fops->access(file, TEE_FS_F_OK); 825 if (err) 826 /* file not found */ 827 return TEE_ERROR_STORAGE_NOT_AVAILABLE; 828 829 err = fops->unlink(file); 830 free(file); 831 if (err) 832 /* error codes needs better granularity */ 833 return TEE_ERROR_GENERIC; 834 835 /* try and remove dir */ 836 dir = tee_svc_storage_create_dirname(sess); 837 if (dir == NULL) 838 return TEE_ERROR_OUT_OF_MEMORY; 839 /* ignore result */ 840 fops->rmdir(dir); 841 free(dir); 842 843 return TEE_SUCCESS; 844 } 845 846 TEE_Result syscall_storage_obj_rename(unsigned long obj, void *object_id, 847 size_t object_id_len) 848 { 849 TEE_Result res; 850 struct tee_ta_session *sess; 851 struct tee_obj *o; 852 struct tee_pobj *po = NULL; 853 char *new_file = NULL; 854 char *old_file = NULL; 855 int err = -1; 856 struct user_ta_ctx *utc; 857 const struct tee_file_operations *fops; 858 859 if (object_id_len > TEE_OBJECT_ID_MAX_LEN) 860 return TEE_ERROR_BAD_PARAMETERS; 861 862 res = tee_ta_get_current_session(&sess); 863 if (res != TEE_SUCCESS) 864 return res; 865 utc = to_user_ta_ctx(sess->ctx); 866 867 res = tee_obj_get(utc, tee_svc_uref_to_vaddr(obj), &o); 868 if (res != TEE_SUCCESS) 869 return res; 870 871 if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) { 872 res = TEE_ERROR_BAD_STATE; 873 goto exit; 874 } 875 876 if (!(o->flags & TEE_DATA_FLAG_ACCESS_WRITE_META)) { 877 res = TEE_ERROR_BAD_STATE; 878 goto exit; 879 } 880 881 if (o->pobj == NULL || o->pobj->obj_id == NULL) { 882 res = TEE_ERROR_BAD_STATE; 883 goto exit; 884 } 885 886 res = tee_mmu_check_access_rights(utc, 887 TEE_MEMORY_ACCESS_READ | 888 TEE_MEMORY_ACCESS_ANY_OWNER, 889 (tee_uaddr_t) object_id, object_id_len); 890 if (res != TEE_SUCCESS) 891 goto exit; 892 893 res = tee_obj_verify(sess, o); 894 if (res != TEE_SUCCESS) 895 goto exit; 896 897 /* get new ds name */ 898 new_file = tee_svc_storage_create_filename(sess, object_id, 899 object_id_len, false); 900 if (new_file == NULL) { 901 res = TEE_ERROR_OUT_OF_MEMORY; 902 goto exit; 903 } 904 905 old_file = tee_svc_storage_create_filename(sess, o->pobj->obj_id, 906 o->pobj->obj_id_len, false); 907 if (old_file == NULL) { 908 res = TEE_ERROR_OUT_OF_MEMORY; 909 goto exit; 910 } 911 912 /* reserve dest name */ 913 fops = o->pobj->fops; 914 res = tee_pobj_get((void *)&sess->ctx->uuid, object_id, 915 object_id_len, TEE_DATA_FLAG_ACCESS_WRITE_META, 916 fops, &po); 917 if (res != TEE_SUCCESS) 918 goto exit; 919 920 err = fops->access(new_file, TEE_FS_F_OK); 921 if (err == 0) { 922 /* file exists */ 923 res = TEE_ERROR_ACCESS_CONFLICT; 924 goto exit; 925 } 926 927 /* move */ 928 err = fops->rename(old_file, new_file); 929 if (err) { 930 /* error codes needs better granularity */ 931 res = TEE_ERROR_GENERIC; 932 goto exit; 933 } 934 935 res = tee_pobj_rename(o->pobj, object_id, object_id_len); 936 937 exit: 938 tee_pobj_release(po); 939 940 free(new_file); 941 free(old_file); 942 943 return res; 944 } 945 946 TEE_Result syscall_storage_alloc_enum(uint32_t *obj_enum) 947 { 948 struct tee_storage_enum *e; 949 struct tee_ta_session *sess; 950 TEE_Result res; 951 struct user_ta_ctx *utc; 952 953 if (obj_enum == NULL) 954 return TEE_ERROR_BAD_PARAMETERS; 955 956 res = tee_ta_get_current_session(&sess); 957 if (res != TEE_SUCCESS) 958 return res; 959 utc = to_user_ta_ctx(sess->ctx); 960 961 e = malloc(sizeof(struct tee_storage_enum)); 962 if (e == NULL) 963 return TEE_ERROR_OUT_OF_MEMORY; 964 965 e->dir = NULL; 966 e->fops = NULL; 967 TAILQ_INSERT_TAIL(&utc->storage_enums, e, link); 968 969 return tee_svc_copy_kaddr_to_uref(obj_enum, e); 970 } 971 972 TEE_Result syscall_storage_free_enum(unsigned long obj_enum) 973 { 974 struct tee_storage_enum *e; 975 TEE_Result res; 976 struct tee_ta_session *sess; 977 struct user_ta_ctx *utc; 978 979 res = tee_ta_get_current_session(&sess); 980 if (res != TEE_SUCCESS) 981 return res; 982 utc = to_user_ta_ctx(sess->ctx); 983 984 res = tee_svc_storage_get_enum(utc, 985 tee_svc_uref_to_vaddr(obj_enum), &e); 986 if (res != TEE_SUCCESS) 987 return res; 988 989 return tee_svc_close_enum(utc, e); 990 } 991 992 TEE_Result syscall_storage_reset_enum(unsigned long obj_enum) 993 { 994 struct tee_storage_enum *e; 995 TEE_Result res; 996 struct tee_ta_session *sess; 997 998 res = tee_ta_get_current_session(&sess); 999 if (res != TEE_SUCCESS) 1000 return res; 1001 1002 res = tee_svc_storage_get_enum(to_user_ta_ctx(sess->ctx), 1003 tee_svc_uref_to_vaddr(obj_enum), &e); 1004 if (res != TEE_SUCCESS) 1005 return res; 1006 1007 res = e->fops->closedir(e->dir); 1008 e->dir = NULL; 1009 if (res != 0) 1010 return TEE_ERROR_GENERIC; 1011 1012 return TEE_SUCCESS; 1013 } 1014 1015 static TEE_Result tee_svc_storage_set_enum(char *d_name, 1016 const struct tee_file_operations *fops, 1017 struct tee_obj *o) 1018 { 1019 TEE_Result res; 1020 uint32_t blen; 1021 uint32_t hslen; 1022 1023 o->info.handleFlags = 1024 TEE_HANDLE_FLAG_PERSISTENT | TEE_HANDLE_FLAG_INITIALIZED; 1025 o->info.objectUsage = TEE_USAGE_DEFAULT; 1026 1027 hslen = strlen(d_name); 1028 blen = TEE_HS2B_BBUF_SIZE(hslen); 1029 o->pobj->obj_id = malloc(blen); 1030 if (!o->pobj->obj_id) { 1031 res = TEE_ERROR_OUT_OF_MEMORY; 1032 goto exit; 1033 } 1034 tee_hs2b((uint8_t *)d_name, o->pobj->obj_id, hslen, blen); 1035 o->pobj->obj_id_len = blen; 1036 o->pobj->fops = fops; 1037 1038 res = TEE_SUCCESS; 1039 1040 exit: 1041 return res; 1042 1043 } 1044 1045 TEE_Result syscall_storage_start_enum(unsigned long obj_enum, 1046 unsigned long storage_id) 1047 { 1048 struct tee_storage_enum *e; 1049 char *dir; 1050 TEE_Result res; 1051 struct tee_ta_session *sess; 1052 struct tee_fs_dirent *d = NULL; 1053 struct tee_obj *o = NULL; 1054 const struct tee_file_operations *fops = file_ops(storage_id); 1055 1056 res = tee_ta_get_current_session(&sess); 1057 if (res != TEE_SUCCESS) 1058 return res; 1059 1060 res = tee_svc_storage_get_enum(to_user_ta_ctx(sess->ctx), 1061 tee_svc_uref_to_vaddr(obj_enum), &e); 1062 if (res != TEE_SUCCESS) 1063 return res; 1064 1065 if (!fops) 1066 return TEE_ERROR_STORAGE_NOT_AVAILABLE; 1067 1068 dir = tee_svc_storage_create_dirname(sess); 1069 if (dir == NULL) 1070 return TEE_ERROR_OUT_OF_MEMORY; 1071 1072 e->fops = fops; 1073 e->dir = fops->opendir(dir); 1074 free(dir); 1075 if (e->dir == NULL) 1076 /* error codes needs better granularity */ 1077 return TEE_ERROR_ITEM_NOT_FOUND; 1078 1079 /* verify object */ 1080 o = tee_obj_alloc(); 1081 if (o == NULL) { 1082 res = TEE_ERROR_OUT_OF_MEMORY; 1083 goto exit; 1084 } 1085 1086 o->pobj = calloc(1, sizeof(struct tee_pobj)); 1087 if (!o->pobj) { 1088 res = TEE_ERROR_OUT_OF_MEMORY; 1089 goto exit; 1090 } 1091 1092 /* object enumeration loop */ 1093 do { 1094 d = fops->readdir(e->dir); 1095 if (d) { 1096 /* allocate obj_id and set object */ 1097 res = tee_svc_storage_set_enum(d->d_name, fops, o); 1098 if (res != TEE_SUCCESS) 1099 goto exit; 1100 res = tee_obj_verify(sess, o); 1101 if (res != TEE_SUCCESS) 1102 goto exit; 1103 /* free obj_id for each iteration */ 1104 free(o->pobj->obj_id); 1105 /* force obj_id to skip freeing at exit statement */ 1106 o->pobj->obj_id = NULL; 1107 } 1108 } while (d); 1109 1110 /* re-start */ 1111 res = fops->closedir(e->dir); 1112 e->dir = NULL; 1113 if (res != 0) { 1114 res = TEE_ERROR_GENERIC; 1115 goto exit; 1116 } 1117 1118 dir = tee_svc_storage_create_dirname(sess); 1119 if (dir == NULL) { 1120 res = TEE_ERROR_OUT_OF_MEMORY; 1121 goto exit; 1122 } 1123 1124 e->dir = fops->opendir(dir); 1125 free(dir); 1126 1127 exit: 1128 if (o) { 1129 if (o->pobj) 1130 free(o->pobj->obj_id); 1131 free(o->pobj); 1132 tee_obj_free(o); 1133 } 1134 1135 return res; 1136 } 1137 1138 TEE_Result syscall_storage_next_enum(unsigned long obj_enum, 1139 TEE_ObjectInfo *info, void *obj_id, uint64_t *len) 1140 { 1141 struct tee_storage_enum *e; 1142 struct tee_fs_dirent *d; 1143 TEE_Result res = TEE_SUCCESS; 1144 struct tee_ta_session *sess; 1145 struct tee_obj *o = NULL; 1146 uint64_t l; 1147 struct user_ta_ctx *utc; 1148 1149 res = tee_ta_get_current_session(&sess); 1150 if (res != TEE_SUCCESS) 1151 goto exit; 1152 utc = to_user_ta_ctx(sess->ctx); 1153 1154 res = tee_svc_storage_get_enum(utc, 1155 tee_svc_uref_to_vaddr(obj_enum), &e); 1156 if (res != TEE_SUCCESS) 1157 goto exit; 1158 1159 /* check rights of the provided buffers */ 1160 res = tee_mmu_check_access_rights(utc, 1161 TEE_MEMORY_ACCESS_WRITE | 1162 TEE_MEMORY_ACCESS_ANY_OWNER, 1163 (tee_uaddr_t) info, 1164 sizeof(TEE_ObjectInfo)); 1165 if (res != TEE_SUCCESS) 1166 goto exit; 1167 1168 res = tee_mmu_check_access_rights(utc, 1169 TEE_MEMORY_ACCESS_WRITE | 1170 TEE_MEMORY_ACCESS_ANY_OWNER, 1171 (tee_uaddr_t) obj_id, 1172 TEE_OBJECT_ID_MAX_LEN); 1173 if (res != TEE_SUCCESS) 1174 goto exit; 1175 1176 if (!e->fops) { 1177 res = TEE_ERROR_ITEM_NOT_FOUND; 1178 goto exit; 1179 } 1180 1181 d = e->fops->readdir(e->dir); 1182 if (d == NULL) { 1183 res = TEE_ERROR_ITEM_NOT_FOUND; 1184 goto exit; 1185 } 1186 1187 o = tee_obj_alloc(); 1188 if (o == NULL) { 1189 res = TEE_ERROR_OUT_OF_MEMORY; 1190 goto exit; 1191 } 1192 1193 o->pobj = calloc(1, sizeof(struct tee_pobj)); 1194 if (!o->pobj) { 1195 res = TEE_ERROR_OUT_OF_MEMORY; 1196 goto exit; 1197 } 1198 1199 res = tee_svc_storage_set_enum(d->d_name, e->fops, o); 1200 if (res != TEE_SUCCESS) 1201 goto exit; 1202 1203 res = tee_obj_verify(sess, o); 1204 if (res != TEE_SUCCESS) 1205 goto exit; 1206 1207 res = tee_svc_storage_read_head(sess, o); 1208 if (res != TEE_SUCCESS) 1209 goto exit; 1210 1211 memcpy(info, &o->info, sizeof(TEE_ObjectInfo)); 1212 memcpy(obj_id, o->pobj->obj_id, o->pobj->obj_id_len); 1213 1214 l = o->pobj->obj_id_len; 1215 res = tee_svc_copy_to_user(len, &l, sizeof(*len)); 1216 1217 exit: 1218 if (o) { 1219 if (o->pobj) 1220 free(o->pobj->obj_id); 1221 free(o->pobj); 1222 tee_obj_free(o); 1223 } 1224 1225 return res; 1226 } 1227 1228 TEE_Result syscall_storage_obj_read(unsigned long obj, void *data, size_t len, 1229 uint64_t *count) 1230 { 1231 TEE_Result res; 1232 struct tee_ta_session *sess; 1233 struct tee_obj *o; 1234 int n_count; 1235 uint64_t u_count; 1236 struct user_ta_ctx *utc; 1237 1238 res = tee_ta_get_current_session(&sess); 1239 if (res != TEE_SUCCESS) 1240 goto exit; 1241 utc = to_user_ta_ctx(sess->ctx); 1242 1243 res = tee_obj_get(utc, tee_svc_uref_to_vaddr(obj), &o); 1244 if (res != TEE_SUCCESS) 1245 goto exit; 1246 1247 if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) { 1248 res = TEE_ERROR_BAD_STATE; 1249 goto exit; 1250 } 1251 1252 if (!(o->flags & TEE_DATA_FLAG_ACCESS_READ)) { 1253 res = TEE_ERROR_ACCESS_CONFLICT; 1254 goto exit; 1255 } 1256 1257 /* check rights of the provided buffer */ 1258 res = tee_mmu_check_access_rights(utc, 1259 TEE_MEMORY_ACCESS_WRITE | 1260 TEE_MEMORY_ACCESS_ANY_OWNER, 1261 (tee_uaddr_t) data, len); 1262 if (res != TEE_SUCCESS) 1263 goto exit; 1264 1265 n_count = o->pobj->fops->read(&res, o->fd, data, len); 1266 if (n_count < 0) { 1267 EMSG("Error code=%x\n", (uint32_t)res); 1268 if (res == TEE_ERROR_CORRUPT_OBJECT) { 1269 EMSG("Object corrupt\n"); 1270 tee_svc_storage_remove_corrupt_obj(sess, o); 1271 } 1272 goto exit; 1273 } 1274 u_count = (uint64_t)((n_count < 0) ? 0 : n_count); 1275 1276 res = tee_svc_copy_to_user(count, &u_count, sizeof(*count)); 1277 1278 o->info.dataPosition += u_count; 1279 1280 res = TEE_SUCCESS; 1281 1282 exit: 1283 return res; 1284 } 1285 1286 TEE_Result syscall_storage_obj_write(unsigned long obj, void *data, size_t len) 1287 { 1288 TEE_Result res; 1289 struct tee_ta_session *sess; 1290 struct tee_obj *o; 1291 int err; 1292 struct user_ta_ctx *utc; 1293 1294 res = tee_ta_get_current_session(&sess); 1295 if (res != TEE_SUCCESS) 1296 goto exit; 1297 utc = to_user_ta_ctx(sess->ctx); 1298 1299 res = tee_obj_get(utc, tee_svc_uref_to_vaddr(obj), &o); 1300 if (res != TEE_SUCCESS) 1301 goto exit; 1302 1303 if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) { 1304 res = TEE_ERROR_BAD_STATE; 1305 goto exit; 1306 } 1307 1308 if (!(o->flags & TEE_DATA_FLAG_ACCESS_WRITE)) { 1309 res = TEE_ERROR_ACCESS_CONFLICT; 1310 goto exit; 1311 } 1312 1313 /* check rights of the provided buffer */ 1314 res = tee_mmu_check_access_rights(utc, 1315 TEE_MEMORY_ACCESS_READ | 1316 TEE_MEMORY_ACCESS_ANY_OWNER, 1317 (tee_uaddr_t) data, len); 1318 1319 err = o->pobj->fops->write(&res, o->fd, data, len); 1320 1321 if (err != (int)len) 1322 goto exit; 1323 1324 o->info.dataPosition += len; 1325 if (o->info.dataPosition > o->info.dataSize) 1326 o->info.dataSize = o->info.dataPosition; 1327 1328 res = TEE_SUCCESS; 1329 exit: 1330 return res; 1331 } 1332 1333 TEE_Result syscall_storage_obj_trunc(unsigned long obj, size_t len) 1334 { 1335 TEE_Result res; 1336 struct tee_ta_session *sess; 1337 struct tee_obj *o; 1338 int err; 1339 tee_fs_off_t off; 1340 size_t attr_size; 1341 1342 res = tee_ta_get_current_session(&sess); 1343 if (res != TEE_SUCCESS) 1344 goto exit; 1345 1346 res = tee_obj_get(to_user_ta_ctx(sess->ctx), 1347 tee_svc_uref_to_vaddr(obj), &o); 1348 if (res != TEE_SUCCESS) 1349 goto exit; 1350 1351 if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) { 1352 res = TEE_ERROR_BAD_STATE; 1353 goto exit; 1354 } 1355 1356 if (!(o->flags & TEE_DATA_FLAG_ACCESS_WRITE)) { 1357 res = TEE_ERROR_ACCESS_CONFLICT; 1358 goto exit; 1359 } 1360 1361 res = tee_obj_verify(sess, o); 1362 if (res != TEE_SUCCESS) 1363 goto exit; 1364 1365 res = tee_obj_attr_to_binary(o, NULL, &attr_size); 1366 if (res != TEE_SUCCESS) 1367 goto exit; 1368 1369 off = sizeof(struct tee_svc_storage_head) + attr_size; 1370 err = o->pobj->fops->ftruncate(&res, o->fd, len + off); 1371 if (err) { 1372 if (res == TEE_ERROR_CORRUPT_OBJECT) { 1373 EMSG("Object corrupt\n"); 1374 res = tee_svc_storage_remove_corrupt_obj(sess, o); 1375 if (res != TEE_SUCCESS) 1376 goto exit; 1377 res = TEE_ERROR_CORRUPT_OBJECT; 1378 goto exit; 1379 } else 1380 res = TEE_ERROR_GENERIC; 1381 } 1382 1383 exit: 1384 return res; 1385 } 1386 1387 TEE_Result syscall_storage_obj_seek(unsigned long obj, long offset, 1388 unsigned long whence) 1389 { 1390 TEE_Result res; 1391 struct tee_ta_session *sess; 1392 struct tee_obj *o; 1393 int fw; 1394 tee_fs_off_t off; 1395 tee_fs_off_t e_off = 0; 1396 size_t attr_size; 1397 1398 res = tee_ta_get_current_session(&sess); 1399 if (res != TEE_SUCCESS) 1400 goto exit; 1401 1402 res = tee_obj_get(to_user_ta_ctx(sess->ctx), 1403 tee_svc_uref_to_vaddr(obj), &o); 1404 if (res != TEE_SUCCESS) 1405 goto exit; 1406 1407 if (!(o->info.handleFlags & TEE_HANDLE_FLAG_PERSISTENT)) { 1408 res = TEE_ERROR_BAD_STATE; 1409 goto exit; 1410 } 1411 1412 res = tee_obj_verify(sess, o); 1413 if (res != TEE_SUCCESS) 1414 goto exit; 1415 1416 res = tee_obj_attr_to_binary(o, NULL, &attr_size); 1417 if (res != TEE_SUCCESS) 1418 goto exit; 1419 1420 fw = tee_svc_storage_conv_whence(whence); 1421 1422 if (whence == TEE_DATA_SEEK_SET) 1423 e_off = sizeof(struct tee_svc_storage_head) + attr_size; 1424 1425 off = o->pobj->fops->lseek(&res, o->fd, e_off + offset, fw); 1426 if (off > -1 && off >= e_off) 1427 o->info.dataPosition = off - 1428 (sizeof(struct tee_svc_storage_head) + attr_size); 1429 else { 1430 res = TEE_ERROR_GENERIC; 1431 goto exit; 1432 } 1433 1434 res = TEE_SUCCESS; 1435 1436 exit: 1437 return res; 1438 } 1439 1440 void tee_svc_storage_close_all_enum(struct user_ta_ctx *utc) 1441 { 1442 struct tee_storage_enum_head *eh = &utc->storage_enums; 1443 1444 /* disregard return value */ 1445 while (!TAILQ_EMPTY(eh)) 1446 tee_svc_close_enum(utc, TAILQ_FIRST(eh)); 1447 } 1448