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