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_common.h> 29 #include <kernel/handle.h> 30 #include <tee/tee_rpmb_fs.h> 31 #include <tee/tee_rpmb.h> 32 #include <tee/tee_fs_defs.h> 33 #include <tee/tee_fs.h> 34 #include <mm/tee_mm.h> 35 #include <trace.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <string_ext.h> 39 #include <util.h> 40 #include <sys/queue.h> 41 42 #define RPMB_STORAGE_START_ADDRESS 0 43 #define RPMB_FS_FAT_START_ADDRESS 512 44 #define RPMB_BLOCK_SIZE_SHIFT 8 45 46 #define RPMB_FS_MAGIC 0x52504D42 47 #define FS_VERSION 2 48 #define N_ENTRIES 8 49 50 #define FILE_IS_ACTIVE (1u << 0) 51 #define FILE_IS_LAST_ENTRY (1u << 1) 52 53 /** 54 * FS parameters: Information often used by internal functions. 55 * fat_start_address will be set by rpmb_fs_setup(). 56 * rpmb_fs_parameters can be read by any other function. 57 */ 58 struct rpmb_fs_parameters { 59 uint32_t fat_start_address; 60 uint32_t max_rpmb_address; 61 }; 62 63 /** 64 * File entry for a single file in a RPMB_FS partition. 65 */ 66 struct rpmb_fat_entry { 67 uint32_t start_address; 68 uint32_t data_size; 69 uint32_t flags; 70 uint32_t write_counter; 71 char filename[TEE_RPMB_FS_FILENAME_LENGTH]; 72 }; 73 74 /** 75 * FAT entry context with reference to a FAT entry and its 76 * location in RPMB. 77 */ 78 struct rpmb_file_handle { 79 /* Pointer to a fat_entry */ 80 struct rpmb_fat_entry fat_entry; 81 /* Pointer to a filename */ 82 char filename[TEE_RPMB_FS_FILENAME_LENGTH]; 83 /* Adress for current entry in RPMB */ 84 uint32_t rpmb_fat_address; 85 /* Current position */ 86 uint32_t pos; 87 }; 88 89 /** 90 * RPMB_FS partition data 91 */ 92 struct rpmb_fs_partition { 93 uint32_t rpmb_fs_magic; 94 uint32_t fs_version; 95 uint32_t write_counter; 96 uint32_t fat_start_address; 97 /* Do not use reserved[] for other purpose than partition data. */ 98 uint8_t reserved[112]; 99 }; 100 101 /** 102 * A node in a list of directory entries. entry->name is a 103 * pointer to name here. 104 */ 105 struct tee_rpmb_fs_dirent { 106 struct tee_fs_dirent entry; 107 char name[TEE_RPMB_FS_FILENAME_LENGTH]; 108 SIMPLEQ_ENTRY(tee_rpmb_fs_dirent) link; 109 }; 110 111 /** 112 * The RPMB directory representation. It contains a queue of 113 * RPMB directory entries: 'next'. 114 * The current pointer points to the last directory entry 115 * returned by readdir(). 116 */ 117 struct tee_fs_dir { 118 struct tee_rpmb_fs_dirent *current; 119 SIMPLEQ_HEAD(next_head, tee_rpmb_fs_dirent) next; 120 }; 121 122 static TEE_Result get_fat_start_address(uint32_t *addr); 123 124 static struct rpmb_fs_parameters *fs_par; 125 126 static struct handle_db fs_handle_db = HANDLE_DB_INITIALIZER; 127 128 static void dump_fat(void) 129 { 130 TEE_Result res = TEE_ERROR_GENERIC; 131 struct rpmb_fat_entry *fat_entries = NULL; 132 uint32_t fat_address; 133 size_t size; 134 int i; 135 bool last_entry_found = false; 136 137 res = get_fat_start_address(&fat_address); 138 if (res != TEE_SUCCESS) 139 goto out; 140 141 size = N_ENTRIES * sizeof(struct rpmb_fat_entry); 142 fat_entries = malloc(size); 143 if (!fat_entries) { 144 res = TEE_ERROR_OUT_OF_MEMORY; 145 goto out; 146 } 147 148 while (!last_entry_found) { 149 res = tee_rpmb_read(CFG_RPMB_FS_DEV_ID, fat_address, 150 (uint8_t *)fat_entries, size); 151 if (res != TEE_SUCCESS) 152 goto out; 153 154 for (i = 0; i < N_ENTRIES; i++) { 155 156 FMSG("flags 0x%x, size %d, address 0x%x, filename '%s'", 157 fat_entries[i].flags, 158 fat_entries[i].data_size, 159 fat_entries[i].start_address, 160 fat_entries[i].filename); 161 162 if ((fat_entries[i].flags & FILE_IS_LAST_ENTRY) != 0) { 163 last_entry_found = true; 164 break; 165 } 166 167 /* Move to next fat_entry. */ 168 fat_address += sizeof(struct rpmb_fat_entry); 169 } 170 } 171 172 out: 173 free(fat_entries); 174 } 175 176 #if (TRACE_LEVEL >= TRACE_DEBUG) 177 static void dump_fh(struct rpmb_file_handle *fh) 178 { 179 DMSG("fh->filename=%s", fh->filename); 180 DMSG("fh->pos=%u", fh->pos); 181 DMSG("fh->rpmb_fat_address=%u", fh->rpmb_fat_address); 182 DMSG("fh->fat_entry.start_address=%u", fh->fat_entry.start_address); 183 DMSG("fh->fat_entry.data_size=%u", fh->fat_entry.data_size); 184 } 185 #else 186 static void dump_fh(struct rpmb_file_handle *fh __unused) 187 { 188 } 189 #endif 190 191 static struct rpmb_file_handle *alloc_file_handle(const char *filename) 192 { 193 struct rpmb_file_handle *fh = NULL; 194 195 fh = calloc(1, sizeof(struct rpmb_file_handle)); 196 if (!fh) 197 return NULL; 198 199 if (filename) 200 strlcpy(fh->filename, filename, sizeof(fh->filename)); 201 202 return fh; 203 } 204 205 /** 206 * write_fat_entry: Store info in a fat_entry to RPMB. 207 */ 208 static TEE_Result write_fat_entry(struct rpmb_file_handle *fh, 209 bool update_write_counter) 210 { 211 TEE_Result res = TEE_ERROR_GENERIC; 212 213 /* Protect partition data. */ 214 if (fh->rpmb_fat_address < sizeof(struct rpmb_fs_partition)) { 215 res = TEE_ERROR_ACCESS_CONFLICT; 216 goto out; 217 } 218 219 if (fh->rpmb_fat_address % sizeof(struct rpmb_fat_entry) != 0) { 220 res = TEE_ERROR_BAD_PARAMETERS; 221 goto out; 222 } 223 224 if (update_write_counter) { 225 res = tee_rpmb_get_write_counter(CFG_RPMB_FS_DEV_ID, 226 &fh->fat_entry.write_counter); 227 if (res != TEE_SUCCESS) 228 goto out; 229 } 230 231 res = tee_rpmb_write(CFG_RPMB_FS_DEV_ID, fh->rpmb_fat_address, 232 (uint8_t *)&fh->fat_entry, 233 sizeof(struct rpmb_fat_entry)); 234 235 dump_fat(); 236 237 out: 238 return res; 239 } 240 241 /** 242 * rpmb_fs_setup: Setup rpmb fs. 243 * Set initial partition and FS values and write to RPMB. 244 * Store frequently used data in RAM. 245 */ 246 static TEE_Result rpmb_fs_setup(void) 247 { 248 TEE_Result res = TEE_ERROR_GENERIC; 249 struct rpmb_fs_partition *partition_data = NULL; 250 struct rpmb_file_handle *fh = NULL; 251 uint32_t max_rpmb_block = 0; 252 253 if (fs_par) { 254 res = TEE_SUCCESS; 255 goto out; 256 } 257 258 res = tee_rpmb_get_max_block(CFG_RPMB_FS_DEV_ID, &max_rpmb_block); 259 if (res != TEE_SUCCESS) 260 goto out; 261 262 partition_data = calloc(1, sizeof(struct rpmb_fs_partition)); 263 if (!partition_data) { 264 res = TEE_ERROR_OUT_OF_MEMORY; 265 goto out; 266 } 267 268 res = tee_rpmb_read(CFG_RPMB_FS_DEV_ID, RPMB_STORAGE_START_ADDRESS, 269 (uint8_t *)partition_data, 270 sizeof(struct rpmb_fs_partition)); 271 if (res != TEE_SUCCESS) 272 goto out; 273 274 #ifndef CFG_RPMB_RESET_FAT 275 if (partition_data->rpmb_fs_magic == RPMB_FS_MAGIC) { 276 if (partition_data->fs_version == FS_VERSION) { 277 res = TEE_SUCCESS; 278 goto store_fs_par; 279 } else { 280 /* Wrong software is in use. */ 281 res = TEE_ERROR_ACCESS_DENIED; 282 goto out; 283 } 284 } 285 #else 286 EMSG("**** Clearing Storage ****"); 287 #endif 288 289 /* Setup new partition data. */ 290 partition_data->rpmb_fs_magic = RPMB_FS_MAGIC; 291 partition_data->fs_version = FS_VERSION; 292 partition_data->fat_start_address = RPMB_FS_FAT_START_ADDRESS; 293 294 /* Initial FAT entry with FILE_IS_LAST_ENTRY flag set. */ 295 fh = alloc_file_handle(NULL); 296 if (!fh) { 297 res = TEE_ERROR_OUT_OF_MEMORY; 298 goto out; 299 } 300 fh->fat_entry.flags = FILE_IS_LAST_ENTRY; 301 fh->rpmb_fat_address = partition_data->fat_start_address; 302 303 /* Write init FAT entry and partition data to RPMB. */ 304 res = write_fat_entry(fh, true); 305 if (res != TEE_SUCCESS) 306 goto out; 307 308 res = 309 tee_rpmb_get_write_counter(CFG_RPMB_FS_DEV_ID, 310 &partition_data->write_counter); 311 if (res != TEE_SUCCESS) 312 goto out; 313 res = tee_rpmb_write(CFG_RPMB_FS_DEV_ID, RPMB_STORAGE_START_ADDRESS, 314 (uint8_t *)partition_data, 315 sizeof(struct rpmb_fs_partition)); 316 317 #ifndef CFG_RPMB_RESET_FAT 318 store_fs_par: 319 #endif 320 321 /* Store FAT start address. */ 322 fs_par = calloc(1, sizeof(struct rpmb_fs_parameters)); 323 if (!fs_par) { 324 res = TEE_ERROR_OUT_OF_MEMORY; 325 goto out; 326 } 327 328 fs_par->fat_start_address = partition_data->fat_start_address; 329 fs_par->max_rpmb_address = max_rpmb_block << RPMB_BLOCK_SIZE_SHIFT; 330 331 dump_fat(); 332 333 out: 334 free(fh); 335 free(partition_data); 336 return res; 337 } 338 339 /** 340 * get_fat_start_address: 341 * FAT start_address from fs_par. 342 */ 343 static TEE_Result get_fat_start_address(uint32_t *addr) 344 { 345 if (!fs_par) 346 return TEE_ERROR_NO_DATA; 347 348 *addr = fs_par->fat_start_address; 349 350 return TEE_SUCCESS; 351 } 352 353 /** 354 * read_fat: Read FAT entries 355 * Return matching FAT entry for read, rm rename and stat. 356 * Build up memory pool and return matching entry for write operation. 357 * "Last FAT entry" can be returned during write. 358 */ 359 static TEE_Result read_fat(struct rpmb_file_handle *fh, tee_mm_pool_t *p) 360 { 361 TEE_Result res = TEE_ERROR_GENERIC; 362 tee_mm_entry_t *mm = NULL; 363 struct rpmb_fat_entry *fat_entries = NULL; 364 uint32_t fat_address; 365 size_t size; 366 int i; 367 bool entry_found = false; 368 bool last_entry_found = false; 369 bool expand_fat = false; 370 struct rpmb_file_handle last_fh; 371 372 DMSG("fat_address %d", fh->rpmb_fat_address); 373 374 res = rpmb_fs_setup(); 375 if (res != TEE_SUCCESS) 376 goto out; 377 378 res = get_fat_start_address(&fat_address); 379 if (res != TEE_SUCCESS) 380 goto out; 381 382 size = N_ENTRIES * sizeof(struct rpmb_fat_entry); 383 fat_entries = malloc(size); 384 if (!fat_entries) { 385 res = TEE_ERROR_OUT_OF_MEMORY; 386 goto out; 387 } 388 389 /* 390 * The pool is used to represent the current RPMB layout. To find 391 * a slot for the file tee_mm_alloc is called on the pool. Thus 392 * if it is not NULL the entire FAT must be traversed to fill in 393 * the pool. 394 */ 395 while (!last_entry_found && (!entry_found || p)) { 396 res = tee_rpmb_read(CFG_RPMB_FS_DEV_ID, fat_address, 397 (uint8_t *)fat_entries, size); 398 if (res != TEE_SUCCESS) 399 goto out; 400 401 for (i = 0; i < N_ENTRIES; i++) { 402 /* 403 * Look for an entry, matching filenames. (read, rm, 404 * rename and stat.). Only store first filename match. 405 */ 406 if (fh->filename && 407 (strcmp(fh->filename, 408 fat_entries[i].filename) == 0) && 409 (fat_entries[i].flags & FILE_IS_ACTIVE) && 410 (!entry_found)) { 411 entry_found = true; 412 fh->rpmb_fat_address = fat_address; 413 memcpy(&fh->fat_entry, &fat_entries[i], 414 sizeof(struct rpmb_fat_entry)); 415 if (!p) 416 break; 417 } 418 419 /* Add existing files to memory pool. (write) */ 420 if (p) { 421 if ((fat_entries[i].flags & FILE_IS_ACTIVE) && 422 (fat_entries[i].data_size > 0)) { 423 424 mm = tee_mm_alloc2 425 (p, 426 fat_entries[i].start_address, 427 fat_entries[i].data_size); 428 if (!mm) { 429 res = TEE_ERROR_OUT_OF_MEMORY; 430 goto out; 431 } 432 } 433 434 /* Unused FAT entries can be reused (write) */ 435 if (((fat_entries[i].flags & FILE_IS_ACTIVE) == 436 0) && (fh->rpmb_fat_address == 0)) { 437 fh->rpmb_fat_address = fat_address; 438 memcpy(&fh->fat_entry, &fat_entries[i], 439 sizeof(struct rpmb_fat_entry)); 440 } 441 } 442 443 if ((fat_entries[i].flags & FILE_IS_LAST_ENTRY) != 0) { 444 last_entry_found = true; 445 446 /* 447 * If the last entry was reached and was chosen 448 * by the previous check, then the FAT needs to 449 * be expanded. 450 * fh->rpmb_fat_address is the address chosen 451 * to store the files FAT entry and fat_address 452 * is the current FAT entry address being 453 * compared. 454 */ 455 if (p && fh->rpmb_fat_address == fat_address) 456 expand_fat = true; 457 break; 458 } 459 460 /* Move to next fat_entry. */ 461 fat_address += sizeof(struct rpmb_fat_entry); 462 } 463 } 464 465 /* 466 * Represent the FAT table in the pool. 467 */ 468 if (p) { 469 /* 470 * Since fat_address is the start of the last entry it needs to 471 * be moved up by an entry. 472 */ 473 fat_address += sizeof(struct rpmb_fat_entry); 474 475 /* Make room for yet a FAT entry and add to memory pool. */ 476 if (expand_fat) 477 fat_address += sizeof(struct rpmb_fat_entry); 478 479 mm = tee_mm_alloc2(p, RPMB_STORAGE_START_ADDRESS, fat_address); 480 if (!mm) { 481 res = TEE_ERROR_OUT_OF_MEMORY; 482 goto out; 483 } 484 485 if (expand_fat) { 486 /* 487 * Point fat_address to the beginning of the new 488 * entry. 489 */ 490 fat_address -= sizeof(struct rpmb_fat_entry); 491 memset(&last_fh, 0, sizeof(last_fh)); 492 last_fh.fat_entry.flags = FILE_IS_LAST_ENTRY; 493 last_fh.rpmb_fat_address = fat_address; 494 res = write_fat_entry(&last_fh, true); 495 if (res != TEE_SUCCESS) 496 goto out; 497 } 498 } 499 500 if (fh->filename && !fh->rpmb_fat_address) 501 res = TEE_ERROR_FILE_NOT_FOUND; 502 503 out: 504 free(fat_entries); 505 return res; 506 } 507 508 int tee_rpmb_fs_open(const char *file, int flags, ...) 509 { 510 int fd = -1; 511 struct rpmb_file_handle *fh = NULL; 512 size_t filelen; 513 tee_mm_pool_t p; 514 bool pool_result; 515 TEE_Result res = TEE_ERROR_GENERIC; 516 517 if (!file) { 518 res = TEE_ERROR_BAD_PARAMETERS; 519 goto out; 520 } 521 522 filelen = strlen(file); 523 if (filelen >= TEE_RPMB_FS_FILENAME_LENGTH - 1 || filelen == 0) { 524 res = TEE_ERROR_BAD_PARAMETERS; 525 goto out; 526 } 527 528 if (file[filelen - 1] == '/') { 529 res = TEE_ERROR_BAD_PARAMETERS; 530 goto out; 531 } 532 533 fh = alloc_file_handle(file); 534 if (!fh) { 535 res = TEE_ERROR_OUT_OF_MEMORY; 536 goto out; 537 } 538 539 /* We need to do setup in order to make sure fs_par is filled in */ 540 res = rpmb_fs_setup(); 541 if (res != TEE_SUCCESS) 542 goto out; 543 544 if (flags & TEE_FS_O_CREATE) { 545 /* Upper memory allocation must be used for RPMB_FS. */ 546 pool_result = tee_mm_init(&p, 547 RPMB_STORAGE_START_ADDRESS, 548 fs_par->max_rpmb_address, 549 RPMB_BLOCK_SIZE_SHIFT, 550 TEE_MM_POOL_HI_ALLOC); 551 552 if (!pool_result) { 553 res = TEE_ERROR_OUT_OF_MEMORY; 554 goto out; 555 } 556 557 res = read_fat(fh, &p); 558 tee_mm_final(&p); 559 if (res != TEE_SUCCESS) 560 goto out; 561 562 } else { 563 res = read_fat(fh, NULL); 564 if (res != TEE_SUCCESS) 565 goto out; 566 } 567 568 /* Add the handle to the db */ 569 fd = handle_get(&fs_handle_db, fh); 570 if (fd == -1) { 571 res = TEE_ERROR_OUT_OF_MEMORY; 572 goto out; 573 } 574 575 /* 576 * If this is opened with create and the entry found was not active 577 * then this is a new file and the FAT entry must be written 578 */ 579 if (flags & TEE_FS_O_CREATE) { 580 if ((fh->fat_entry.flags & FILE_IS_ACTIVE) == 0) { 581 memset(&fh->fat_entry, 0, 582 sizeof(struct rpmb_fat_entry)); 583 memcpy(fh->fat_entry.filename, file, strlen(file)); 584 /* Start address and size are 0 */ 585 fh->fat_entry.flags = FILE_IS_ACTIVE; 586 587 res = write_fat_entry(fh, true); 588 if (res != TEE_SUCCESS) { 589 handle_put(&fs_handle_db, fd); 590 fd = -1; 591 goto out; 592 } 593 } 594 } 595 596 res = TEE_SUCCESS; 597 598 out: 599 if (res != TEE_SUCCESS) { 600 if (fh) 601 free(fh); 602 603 fd = -1; 604 } 605 606 return fd; 607 } 608 609 int tee_rpmb_fs_close(int fd) 610 { 611 struct rpmb_file_handle *fh; 612 613 fh = handle_put(&fs_handle_db, fd); 614 if (fh) { 615 free(fh); 616 return 0; 617 } 618 619 return -1; 620 } 621 622 int tee_rpmb_fs_read(int fd, uint8_t *buf, size_t size) 623 { 624 TEE_Result res = TEE_ERROR_GENERIC; 625 struct rpmb_file_handle *fh; 626 int read_size = -1; 627 628 if (!size) 629 return 0; 630 631 if (!buf) { 632 res = TEE_ERROR_BAD_PARAMETERS; 633 goto out; 634 } 635 636 fh = handle_lookup(&fs_handle_db, fd); 637 if (!fh) { 638 res = TEE_ERROR_BAD_PARAMETERS; 639 goto out; 640 } 641 dump_fh(fh); 642 643 res = read_fat(fh, NULL); 644 if (res != TEE_SUCCESS) 645 goto out; 646 647 size = MIN(size, fh->fat_entry.data_size - fh->pos); 648 if (size > 0) { 649 res = tee_rpmb_read(CFG_RPMB_FS_DEV_ID, 650 fh->fat_entry.start_address + fh->pos, buf, 651 size); 652 if (res != TEE_SUCCESS) 653 goto out; 654 } 655 656 read_size = size; 657 res = TEE_SUCCESS; 658 659 out: 660 if (res != TEE_SUCCESS) 661 read_size = -1; 662 663 return read_size; 664 } 665 666 int tee_rpmb_fs_write(int fd, uint8_t *buf, size_t size) 667 { 668 TEE_Result res = TEE_ERROR_GENERIC; 669 struct rpmb_file_handle *fh; 670 tee_mm_pool_t p; 671 bool pool_result = false; 672 tee_mm_entry_t *mm; 673 size_t end; 674 size_t newsize; 675 uint8_t *newbuf = NULL; 676 uintptr_t newaddr; 677 uint32_t start_addr; 678 679 if (!size) 680 return 0; 681 682 if (!buf) { 683 res = TEE_ERROR_BAD_PARAMETERS; 684 goto out; 685 } 686 687 if (!fs_par) { 688 res = TEE_ERROR_GENERIC; 689 goto out; 690 } 691 692 fh = handle_lookup(&fs_handle_db, fd); 693 if (!fh) { 694 res = TEE_ERROR_BAD_PARAMETERS; 695 goto out; 696 } 697 dump_fh(fh); 698 699 /* Upper memory allocation must be used for RPMB_FS. */ 700 pool_result = tee_mm_init(&p, 701 RPMB_STORAGE_START_ADDRESS, 702 fs_par->max_rpmb_address, 703 RPMB_BLOCK_SIZE_SHIFT, 704 TEE_MM_POOL_HI_ALLOC); 705 if (!pool_result) { 706 res = TEE_ERROR_OUT_OF_MEMORY; 707 goto out; 708 } 709 710 res = read_fat(fh, &p); 711 if (res != TEE_SUCCESS) 712 goto out; 713 714 TEE_ASSERT(!(fh->fat_entry.flags & FILE_IS_LAST_ENTRY)); 715 716 end = fh->pos + size; 717 start_addr = fh->fat_entry.start_address + fh->pos; 718 719 if (end <= fh->fat_entry.data_size && 720 tee_rpmb_write_is_atomic(CFG_RPMB_FS_DEV_ID, start_addr, size)) { 721 722 DMSG("Updating data in-place"); 723 res = tee_rpmb_write(CFG_RPMB_FS_DEV_ID, start_addr, buf, 724 size); 725 if (res != TEE_SUCCESS) 726 goto out; 727 } else { 728 /* 729 * File must be extended, or update cannot be atomic: allocate, 730 * read, update, write. 731 */ 732 733 DMSG("Need to re-allocate"); 734 newsize = MAX(end, fh->fat_entry.data_size); 735 mm = tee_mm_alloc(&p, newsize); 736 newbuf = calloc(newsize, 1); 737 if (!mm || !newbuf) { 738 res = TEE_ERROR_OUT_OF_MEMORY; 739 goto out; 740 } 741 742 if (fh->fat_entry.data_size) { 743 res = tee_rpmb_read(CFG_RPMB_FS_DEV_ID, 744 fh->fat_entry.start_address, 745 newbuf, fh->fat_entry.data_size); 746 if (res != TEE_SUCCESS) 747 goto out; 748 } 749 750 memcpy(newbuf + fh->pos, buf, size); 751 752 newaddr = tee_mm_get_smem(mm); 753 res = tee_rpmb_write(CFG_RPMB_FS_DEV_ID, newaddr, newbuf, 754 newsize); 755 if (res != TEE_SUCCESS) 756 goto out; 757 758 fh->fat_entry.data_size = newsize; 759 fh->fat_entry.start_address = newaddr; 760 res = write_fat_entry(fh, true); 761 if (res != TEE_SUCCESS) 762 goto out; 763 } 764 765 fh->pos += size; 766 out: 767 if (pool_result) 768 tee_mm_final(&p); 769 if (newbuf) 770 free(newbuf); 771 772 if (res == TEE_SUCCESS) 773 return size; 774 775 return -1; 776 } 777 778 tee_fs_off_t tee_rpmb_fs_lseek(int fd, tee_fs_off_t offset, int whence) 779 { 780 struct rpmb_file_handle *fh; 781 TEE_Result res; 782 tee_fs_off_t ret = -1; 783 tee_fs_off_t new_pos; 784 785 fh = handle_lookup(&fs_handle_db, fd); 786 if (!fh) 787 return TEE_ERROR_BAD_PARAMETERS; 788 789 res = read_fat(fh, NULL); 790 if (res != TEE_SUCCESS) 791 return -1; 792 793 switch (whence) { 794 case TEE_FS_SEEK_SET: 795 new_pos = offset; 796 break; 797 798 case TEE_FS_SEEK_CUR: 799 new_pos = fh->pos + offset; 800 break; 801 802 case TEE_FS_SEEK_END: 803 new_pos = fh->fat_entry.data_size + offset; 804 break; 805 806 default: 807 goto exit; 808 } 809 810 if (new_pos < 0) 811 new_pos = 0; 812 813 if (new_pos > TEE_DATA_MAX_POSITION) { 814 EMSG("Position is beyond TEE_DATA_MAX_POSITION"); 815 goto exit; 816 } 817 818 ret = fh->pos = new_pos; 819 exit: 820 return ret; 821 } 822 823 TEE_Result tee_rpmb_fs_rm(const char *filename) 824 { 825 TEE_Result res = TEE_ERROR_GENERIC; 826 struct rpmb_file_handle *fh = NULL; 827 828 if (!filename || strlen(filename) >= TEE_RPMB_FS_FILENAME_LENGTH - 1) { 829 res = TEE_ERROR_BAD_PARAMETERS; 830 goto out; 831 } 832 833 fh = alloc_file_handle(filename); 834 if (!fh) { 835 res = TEE_ERROR_OUT_OF_MEMORY; 836 goto out; 837 } 838 839 res = read_fat(fh, NULL); 840 if (res != TEE_SUCCESS) 841 goto out; 842 843 /* Clear this file entry. */ 844 memset(&fh->fat_entry, 0, sizeof(struct rpmb_fat_entry)); 845 res = write_fat_entry(fh, false); 846 847 out: 848 free(fh); 849 IMSG("Deleting file %s returned 0x%x\n", filename, res); 850 return res; 851 } 852 853 TEE_Result tee_rpmb_fs_rename(const char *old_name, const char *new_name) 854 { 855 TEE_Result res = TEE_ERROR_GENERIC; 856 struct rpmb_file_handle *fh_old = NULL; 857 struct rpmb_file_handle *fh_new = NULL; 858 uint32_t old_len; 859 uint32_t new_len; 860 861 if (!old_name || !new_name) { 862 res = TEE_ERROR_BAD_PARAMETERS; 863 goto out; 864 } 865 866 old_len = strlen(old_name); 867 new_len = strlen(new_name); 868 869 if ((old_len >= TEE_RPMB_FS_FILENAME_LENGTH - 1) || 870 (new_len >= TEE_RPMB_FS_FILENAME_LENGTH - 1) || (new_len == 0)) { 871 872 res = TEE_ERROR_BAD_PARAMETERS; 873 goto out; 874 } 875 876 fh_old = alloc_file_handle(old_name); 877 if (!fh_old) { 878 res = TEE_ERROR_OUT_OF_MEMORY; 879 goto out; 880 } 881 882 fh_new = alloc_file_handle(new_name); 883 if (!fh_new) { 884 res = TEE_ERROR_OUT_OF_MEMORY; 885 goto out; 886 } 887 888 res = read_fat(fh_old, NULL); 889 if (res != TEE_SUCCESS) 890 goto out; 891 892 res = read_fat(fh_new, NULL); 893 if (res == TEE_SUCCESS) { 894 res = TEE_ERROR_BAD_PARAMETERS; 895 goto out; 896 } 897 898 memset(fh_old->fat_entry.filename, 0, TEE_RPMB_FS_FILENAME_LENGTH); 899 memcpy(fh_old->fat_entry.filename, new_name, new_len); 900 901 res = write_fat_entry(fh_old, false); 902 903 out: 904 free(fh_old); 905 free(fh_new); 906 907 return res; 908 } 909 910 int tee_rpmb_fs_mkdir(const char *path __unused, tee_fs_mode_t mode __unused) 911 { 912 /* 913 * FIXME: mkdir() should really create some entry in the FAT so that 914 * access() would return success when the directory exists but is 915 * empty. This does not matter for the current use cases. 916 */ 917 return 0; 918 } 919 920 int tee_rpmb_fs_ftruncate(int fd, tee_fs_off_t length) 921 { 922 struct rpmb_file_handle *fh; 923 tee_mm_pool_t p; 924 bool pool_result = false; 925 tee_mm_entry_t *mm; 926 uint32_t newsize; 927 uint8_t *newbuf = NULL; 928 uintptr_t newaddr; 929 TEE_Result res = TEE_ERROR_GENERIC; 930 931 if (length < 0 || length > INT32_MAX) { 932 res = TEE_ERROR_BAD_PARAMETERS; 933 goto out; 934 } 935 newsize = length; 936 937 fh = handle_lookup(&fs_handle_db, fd); 938 if (!fh) { 939 res = TEE_ERROR_BAD_PARAMETERS; 940 goto out; 941 } 942 943 res = read_fat(fh, NULL); 944 if (res != TEE_SUCCESS) 945 goto out; 946 947 if (newsize > fh->fat_entry.data_size) { 948 /* Extend file */ 949 950 pool_result = tee_mm_init(&p, 951 RPMB_STORAGE_START_ADDRESS, 952 fs_par->max_rpmb_address, 953 RPMB_BLOCK_SIZE_SHIFT, 954 TEE_MM_POOL_HI_ALLOC); 955 if (!pool_result) { 956 res = TEE_ERROR_OUT_OF_MEMORY; 957 goto out; 958 } 959 res = read_fat(fh, &p); 960 if (res != TEE_SUCCESS) 961 goto out; 962 963 mm = tee_mm_alloc(&p, newsize); 964 newbuf = calloc(newsize, 1); 965 if (!mm || !newbuf) { 966 res = TEE_ERROR_OUT_OF_MEMORY; 967 goto out; 968 } 969 970 if (fh->fat_entry.data_size) { 971 res = tee_rpmb_read(CFG_RPMB_FS_DEV_ID, 972 fh->fat_entry.start_address, 973 newbuf, fh->fat_entry.data_size); 974 if (res != TEE_SUCCESS) 975 goto out; 976 } 977 978 newaddr = tee_mm_get_smem(mm); 979 res = tee_rpmb_write(CFG_RPMB_FS_DEV_ID, newaddr, newbuf, 980 newsize); 981 if (res != TEE_SUCCESS) 982 goto out; 983 984 } else { 985 /* Don't change file location */ 986 newaddr = fh->fat_entry.start_address; 987 } 988 989 /* fh->pos is unchanged */ 990 fh->fat_entry.data_size = newsize; 991 fh->fat_entry.start_address = newaddr; 992 res = write_fat_entry(fh, true); 993 994 out: 995 if (pool_result) 996 tee_mm_final(&p); 997 if (newbuf) 998 free(newbuf); 999 1000 if (res == TEE_SUCCESS) 1001 return 0; 1002 1003 return -1; 1004 } 1005 1006 static void tee_rpmb_fs_dir_free(tee_fs_dir *dir) 1007 { 1008 struct tee_rpmb_fs_dirent *e; 1009 1010 if (!dir) 1011 return; 1012 1013 free(dir->current); 1014 1015 while ((e = SIMPLEQ_FIRST(&dir->next))) { 1016 SIMPLEQ_REMOVE_HEAD(&dir->next, link); 1017 free(e); 1018 } 1019 } 1020 1021 static TEE_Result tee_rpmb_fs_dir_populate(const char *path, tee_fs_dir *dir) 1022 { 1023 struct tee_rpmb_fs_dirent *current = NULL; 1024 struct rpmb_fat_entry *fat_entries = NULL; 1025 uint32_t fat_address; 1026 uint32_t filelen; 1027 char *filename; 1028 int i; 1029 bool last_entry_found = false; 1030 bool matched; 1031 struct tee_rpmb_fs_dirent *next = NULL; 1032 uint32_t pathlen; 1033 TEE_Result res = TEE_ERROR_GENERIC; 1034 uint32_t size; 1035 char temp; 1036 1037 res = rpmb_fs_setup(); 1038 if (res != TEE_SUCCESS) 1039 goto out; 1040 1041 res = get_fat_start_address(&fat_address); 1042 if (res != TEE_SUCCESS) 1043 goto out; 1044 1045 size = N_ENTRIES * sizeof(struct rpmb_fat_entry); 1046 fat_entries = malloc(size); 1047 if (!fat_entries) { 1048 res = TEE_ERROR_OUT_OF_MEMORY; 1049 goto out; 1050 } 1051 1052 pathlen = strlen(path); 1053 while (!last_entry_found) { 1054 res = tee_rpmb_read(CFG_RPMB_FS_DEV_ID, fat_address, 1055 (uint8_t *)fat_entries, size); 1056 if (res != TEE_SUCCESS) 1057 goto out; 1058 1059 for (i = 0; i < N_ENTRIES; i++) { 1060 filename = fat_entries[i].filename; 1061 if (fat_entries[i].flags & FILE_IS_ACTIVE) { 1062 matched = false; 1063 filelen = strlen(filename); 1064 if (filelen > pathlen) { 1065 temp = filename[pathlen]; 1066 filename[pathlen] = '\0'; 1067 if (strcmp(filename, path) == 0) 1068 matched = true; 1069 1070 filename[pathlen] = temp; 1071 } 1072 1073 if (matched) { 1074 next = malloc(sizeof(*next)); 1075 if (!next) { 1076 res = TEE_ERROR_OUT_OF_MEMORY; 1077 goto out; 1078 } 1079 1080 memset(next, 0, sizeof(*next)); 1081 next->entry.d_name = next->name; 1082 memcpy(next->name, 1083 &filename[pathlen], 1084 filelen - pathlen); 1085 1086 SIMPLEQ_INSERT_TAIL(&dir->next, next, 1087 link); 1088 current = next; 1089 } 1090 } 1091 1092 if (fat_entries[i].flags & FILE_IS_LAST_ENTRY) { 1093 last_entry_found = true; 1094 break; 1095 } 1096 1097 /* Move to next fat_entry. */ 1098 fat_address += sizeof(struct rpmb_fat_entry); 1099 } 1100 } 1101 1102 /* No directories were found. */ 1103 if (!current) { 1104 res = TEE_ERROR_NO_DATA; 1105 goto out; 1106 } 1107 1108 res = TEE_SUCCESS; 1109 1110 out: 1111 if (res != TEE_SUCCESS) 1112 tee_rpmb_fs_dir_free(dir); 1113 if (fat_entries) 1114 free(fat_entries); 1115 1116 return res; 1117 } 1118 1119 static TEE_Result tee_rpmb_fs_opendir_internal(const char *path, 1120 tee_fs_dir **dir) 1121 { 1122 uint32_t len; 1123 uint32_t max_size; 1124 char path_local[TEE_RPMB_FS_FILENAME_LENGTH]; 1125 TEE_Result res = TEE_ERROR_GENERIC; 1126 tee_fs_dir *rpmb_dir = NULL; 1127 1128 if (!path || !dir) { 1129 res = TEE_ERROR_BAD_PARAMETERS; 1130 goto out; 1131 } 1132 1133 /* 1134 * There must be room for at least the NULL char and a char for the 1135 * filename after the path. 1136 */ 1137 max_size = TEE_RPMB_FS_FILENAME_LENGTH - 2; 1138 len = strlen(path); 1139 if (len > max_size || len == 0) { 1140 res = TEE_ERROR_BAD_PARAMETERS; 1141 goto out; 1142 } 1143 1144 memset(path_local, 0, sizeof(path_local)); 1145 memcpy(path_local, path, len); 1146 1147 /* Add a slash to correctly match the full directory name. */ 1148 if (path_local[len - 1] != '/') 1149 path_local[len] = '/'; 1150 1151 rpmb_dir = calloc(1, sizeof(tee_fs_dir)); 1152 if (!rpmb_dir) { 1153 res = TEE_ERROR_OUT_OF_MEMORY; 1154 goto out; 1155 } 1156 SIMPLEQ_INIT(&rpmb_dir->next); 1157 1158 res = tee_rpmb_fs_dir_populate(path_local, rpmb_dir); 1159 if (res != TEE_SUCCESS) { 1160 free(rpmb_dir); 1161 rpmb_dir = NULL; 1162 goto out; 1163 } 1164 1165 *dir = rpmb_dir; 1166 1167 out: 1168 return res; 1169 } 1170 1171 tee_fs_dir *tee_rpmb_fs_opendir(const char *path) 1172 { 1173 tee_fs_dir *dir = NULL; 1174 TEE_Result res = TEE_ERROR_GENERIC; 1175 1176 res = tee_rpmb_fs_opendir_internal(path, &dir); 1177 if (res != TEE_SUCCESS) 1178 dir = NULL; 1179 1180 return dir; 1181 } 1182 1183 1184 struct tee_fs_dirent *tee_rpmb_fs_readdir(tee_fs_dir *dir) 1185 { 1186 if (!dir) 1187 return NULL; 1188 1189 free(dir->current); 1190 1191 dir->current = SIMPLEQ_FIRST(&dir->next); 1192 if (!dir->current) 1193 return NULL; 1194 1195 SIMPLEQ_REMOVE_HEAD(&dir->next, link); 1196 1197 return &dir->current->entry; 1198 } 1199 1200 int tee_rpmb_fs_closedir(tee_fs_dir *dir) 1201 { 1202 TEE_Result res = TEE_ERROR_GENERIC; 1203 1204 if (!dir) { 1205 res = TEE_SUCCESS; 1206 goto out; 1207 } 1208 1209 tee_rpmb_fs_dir_free(dir); 1210 free(dir); 1211 res = TEE_SUCCESS; 1212 out: 1213 if (res == TEE_SUCCESS) 1214 return 0; 1215 1216 return -1; 1217 } 1218 1219 int tee_rpmb_fs_rmdir(const char *path) 1220 { 1221 tee_fs_dir *dir = NULL; 1222 TEE_Result res = TEE_ERROR_GENERIC; 1223 int ret = -1; 1224 1225 /* Open the directory anyting other than NO_DATA is a failure */ 1226 res = tee_rpmb_fs_opendir_internal(path, &dir); 1227 if (res == TEE_SUCCESS) { 1228 tee_rpmb_fs_closedir(dir); 1229 ret = -1; 1230 1231 } else if (res == TEE_ERROR_NO_DATA) { 1232 ret = 0; 1233 1234 } else { 1235 /* The case any other failure is returned */ 1236 ret = -1; 1237 } 1238 1239 1240 return ret; 1241 } 1242 1243 TEE_Result tee_rpmb_fs_stat(const char *filename, 1244 struct tee_rpmb_fs_stat *stat) 1245 { 1246 TEE_Result res = TEE_ERROR_GENERIC; 1247 struct rpmb_file_handle *fh = NULL; 1248 1249 if (!stat || !filename) { 1250 res = TEE_ERROR_BAD_PARAMETERS; 1251 goto out; 1252 } 1253 1254 fh = alloc_file_handle(filename); 1255 if (!fh) { 1256 res = TEE_ERROR_OUT_OF_MEMORY; 1257 goto out; 1258 } 1259 1260 res = read_fat(fh, NULL); 1261 if (res != TEE_SUCCESS) 1262 goto out; 1263 1264 stat->size = (size_t)fh->fat_entry.data_size; 1265 stat->reserved = 0; 1266 1267 out: 1268 free(fh); 1269 return res; 1270 } 1271 1272 int tee_rpmb_fs_access(const char *filename, int mode) 1273 { 1274 struct tee_rpmb_fs_stat stat; 1275 TEE_Result res; 1276 1277 /* Mode is currently ignored, this only checks for existence */ 1278 (void)mode; 1279 1280 res = tee_rpmb_fs_stat(filename, &stat); 1281 1282 if (res == TEE_SUCCESS) 1283 return 0; 1284 1285 return -1; 1286 } 1287 1288