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 <tee/tee_rpmb_fs.h> 30 #include <tee/tee_rpmb.h> 31 #include <mm/tee_mm.h> 32 #include <stdlib.h> 33 #include <string.h> 34 35 #define RPMB_STORAGE_START_ADDRESS 0 36 #define RPMB_FS_FAT_START_ADDRESS 512 37 #define RPMB_STORAGE_END_ADDRESS ((1024 * 128) - 1) 38 #define RPMB_BLOCK_SIZE_SHIFT 8 39 40 #define DEV_ID 0 41 #define RPMB_FS_MAGIC 0x52504D42 42 #define FS_VERSION 1 43 #define N_ENTRIES 16 44 45 #define FILE_IS_ACTIVE (1u << 0) 46 #define FILE_IS_LAST_ENTRY (1u << 1) 47 48 /** 49 * FS parameters: Information often used by internal functions. 50 * fat_start_address will be set by rpmb_fs_setup(). 51 * rpmb_fs_parameters can be read by any other function. 52 */ 53 struct rpmb_fs_parameters { 54 uint32_t fat_start_address; 55 }; 56 57 /** 58 * File entry for a single file in a RPMB_FS partition. 59 */ 60 struct rpmb_fat_entry { 61 uint32_t start_address; 62 uint32_t data_size; 63 uint32_t flags; 64 uint32_t write_counter; 65 char filename[FILENAME_LENGTH]; 66 }; 67 68 /** 69 * FAT entry context with reference to a FAT entry and its 70 * location in RPMB. 71 */ 72 struct file_handle { 73 /* Pointer to a fat_entry */ 74 struct rpmb_fat_entry fat_entry; 75 /* Pointer to a filename */ 76 const char *filename; 77 /* Adress for current entry in RPMB */ 78 uint32_t rpmb_fat_address; 79 }; 80 81 /** 82 * RPMB_FS partition data 83 */ 84 struct rpmb_fs_partition { 85 uint32_t rpmb_fs_magic; 86 uint32_t fs_version; 87 uint32_t write_counter; 88 uint32_t fat_start_address; 89 /* Do not use reserved[] for other purpose than partition data. */ 90 uint8_t reserved[112]; 91 }; 92 93 static struct rpmb_fs_parameters *fs_par; 94 95 static struct file_handle *alloc_file_handle(const char *filename) 96 { 97 struct file_handle *fh = NULL; 98 99 fh = calloc(1, sizeof(struct file_handle)); 100 if (fh == NULL) 101 return NULL; 102 103 if (filename != NULL) 104 fh->filename = filename; 105 106 return fh; 107 } 108 109 /** 110 * write_fat_entry: Store info in a fat_entry to RPMB. 111 */ 112 static TEE_Result write_fat_entry(struct file_handle *fh, 113 bool update_write_counter) 114 { 115 TEE_Result res = TEE_ERROR_GENERIC; 116 117 /* Protect partition data. */ 118 if (fh->rpmb_fat_address < sizeof(struct rpmb_fs_partition)) { 119 res = TEE_ERROR_ACCESS_CONFLICT; 120 goto out; 121 } 122 123 if (fh->rpmb_fat_address % sizeof(struct rpmb_fat_entry) != 0) { 124 res = TEE_ERROR_BAD_PARAMETERS; 125 goto out; 126 } 127 128 if (update_write_counter) { 129 res = tee_rpmb_get_write_counter(DEV_ID, 130 &fh->fat_entry.write_counter); 131 if (res != TEE_SUCCESS) 132 goto out; 133 } 134 135 res = tee_rpmb_write(DEV_ID, fh->rpmb_fat_address, 136 (uint8_t *)&fh->fat_entry, 137 sizeof(struct rpmb_fat_entry)); 138 139 out: 140 return res; 141 } 142 143 /** 144 * rpmb_fs_setup: Setup rpmb fs. 145 * Set initial partition and FS values and write to RPMB. 146 * Store frequently used data in RAM. 147 */ 148 static TEE_Result rpmb_fs_setup(void) 149 { 150 TEE_Result res = TEE_ERROR_GENERIC; 151 struct rpmb_fs_partition *partition_data = NULL; 152 struct file_handle *fh = NULL; 153 154 partition_data = calloc(1, sizeof(struct rpmb_fs_partition)); 155 if (partition_data == NULL) { 156 res = TEE_ERROR_OUT_OF_MEMORY; 157 goto out; 158 } 159 160 res = tee_rpmb_read(DEV_ID, RPMB_STORAGE_START_ADDRESS, 161 (uint8_t *)partition_data, 162 sizeof(struct rpmb_fs_partition)); 163 if (res != TEE_SUCCESS) 164 goto out; 165 166 if (partition_data->rpmb_fs_magic == RPMB_FS_MAGIC) { 167 if (partition_data->fs_version == FS_VERSION) { 168 res = TEE_SUCCESS; 169 goto store_fs_par; 170 } else { 171 /* Wrong software is in use. */ 172 res = TEE_ERROR_ACCESS_DENIED; 173 goto out; 174 } 175 } 176 177 /* Setup new partition data. */ 178 partition_data->rpmb_fs_magic = RPMB_FS_MAGIC; 179 partition_data->fs_version = FS_VERSION; 180 partition_data->fat_start_address = RPMB_FS_FAT_START_ADDRESS; 181 182 /* Initial FAT entry with FILE_IS_LAST_ENTRY flag set. */ 183 fh = alloc_file_handle(NULL); 184 if (fh == NULL) { 185 res = TEE_ERROR_OUT_OF_MEMORY; 186 goto out; 187 } 188 fh->fat_entry.flags = FILE_IS_LAST_ENTRY; 189 fh->rpmb_fat_address = partition_data->fat_start_address; 190 191 /* Write init FAT entry and partition data to RPMB. */ 192 res = write_fat_entry(fh, true); 193 if (res != TEE_SUCCESS) 194 goto out; 195 196 res = 197 tee_rpmb_get_write_counter(DEV_ID, &partition_data->write_counter); 198 if (res != TEE_SUCCESS) 199 goto out; 200 res = tee_rpmb_write(DEV_ID, RPMB_STORAGE_START_ADDRESS, 201 (uint8_t *)partition_data, 202 sizeof(struct rpmb_fs_partition)); 203 204 store_fs_par: 205 /* Store FAT start address. */ 206 if (fs_par == NULL) { 207 fs_par = calloc(1, sizeof(struct rpmb_fs_parameters)); 208 if (fs_par == NULL) { 209 res = TEE_ERROR_OUT_OF_MEMORY; 210 goto out; 211 } 212 } 213 214 fs_par->fat_start_address = partition_data->fat_start_address; 215 216 out: 217 free(fh); 218 free(partition_data); 219 220 return res; 221 } 222 223 /** 224 * get_fat_start_address: 225 * FAT start_address from fs_par. 226 */ 227 static TEE_Result get_fat_start_address(uint32_t *addr) 228 { 229 TEE_Result res = TEE_ERROR_GENERIC; 230 231 if (fs_par == NULL) { 232 res = rpmb_fs_setup(); 233 if (res != TEE_SUCCESS) 234 goto out; 235 } 236 237 *addr = fs_par->fat_start_address; 238 res = TEE_SUCCESS; 239 240 out: 241 return res; 242 } 243 244 /** 245 * read_fat: Read FAT entries 246 * Return matching FAT entry for read, rm rename and stat. 247 * Build up memory pool and return matching entry for write operation. 248 * "Last FAT entry" can be returned during write. 249 */ 250 static TEE_Result read_fat(struct file_handle *fh, tee_mm_pool_t *p) 251 { 252 TEE_Result res = TEE_ERROR_GENERIC; 253 tee_mm_entry_t *mm = NULL; 254 struct rpmb_fat_entry *fat_entries = NULL; 255 uint32_t fat_address; 256 size_t size; 257 int i; 258 bool entry_found = false; 259 bool last_entry_found = false; 260 261 res = rpmb_fs_setup(); 262 if (res != TEE_SUCCESS) 263 goto out; 264 265 res = get_fat_start_address(&fat_address); 266 if (res != TEE_SUCCESS) 267 goto out; 268 269 size = N_ENTRIES * sizeof(struct rpmb_fat_entry); 270 fat_entries = malloc(size); 271 if (fat_entries == NULL) { 272 res = TEE_ERROR_OUT_OF_MEMORY; 273 goto out; 274 } 275 276 while (!last_entry_found && !entry_found) { 277 res = tee_rpmb_read(DEV_ID, fat_address, 278 (uint8_t *)fat_entries, size); 279 if (res != TEE_SUCCESS) 280 goto out; 281 282 for (i = 0; i < N_ENTRIES; i++) { 283 /* 284 * Look for an entry, matching filenames. (read, rm, 285 * rename and stat.). Only store first filename match. 286 */ 287 if ((fh->filename != NULL) && 288 (strcmp(fh->filename, 289 fat_entries[i].filename) == 0) && 290 (fat_entries[i].flags & FILE_IS_ACTIVE) && 291 (!entry_found)) { 292 entry_found = true; 293 fh->rpmb_fat_address = fat_address; 294 memcpy(&fh->fat_entry, &fat_entries[i], 295 sizeof(struct rpmb_fat_entry)); 296 if (p == NULL) 297 break; 298 } 299 300 /* Add existing files to memory pool. (write) */ 301 if (p != NULL) { 302 if ((fat_entries[i].flags & FILE_IS_ACTIVE) != 303 0) { 304 mm = tee_mm_alloc2 305 (p, 306 fat_entries[i].start_address, 307 fat_entries[i].data_size); 308 if (mm == NULL) { 309 res = TEE_ERROR_OUT_OF_MEMORY; 310 goto out; 311 } 312 } 313 314 /* Unused FAT entries can be reused (write) */ 315 if (((fat_entries[i].flags & FILE_IS_ACTIVE) == 316 0) && (fh->rpmb_fat_address == 0)) { 317 fh->rpmb_fat_address = fat_address; 318 memcpy(&fh->fat_entry, &fat_entries[i], 319 sizeof(struct rpmb_fat_entry)); 320 } 321 } 322 323 if ((fat_entries[i].flags & FILE_IS_LAST_ENTRY) != 0) { 324 last_entry_found = true; 325 if (p != NULL && fh->rpmb_fat_address == 0) { 326 fh->rpmb_fat_address = fat_address; 327 fh->fat_entry.flags = 328 FILE_IS_LAST_ENTRY; 329 } 330 break; 331 } 332 333 /* Move to next fat_entry. */ 334 fat_address += sizeof(struct rpmb_fat_entry); 335 } 336 } 337 338 if ((p != NULL) && last_entry_found) { 339 /* Make room for yet a FAT entry and add to memory pool. */ 340 fat_address += 2 * sizeof(struct rpmb_fat_entry); 341 mm = tee_mm_alloc2(p, RPMB_STORAGE_START_ADDRESS, fat_address); 342 if (mm == NULL) { 343 res = TEE_ERROR_OUT_OF_MEMORY; 344 goto out; 345 } 346 } 347 348 if (fh->filename != NULL && fh->rpmb_fat_address == 0) 349 res = TEE_ERROR_FILE_NOT_FOUND; 350 351 out: 352 free(fat_entries); 353 return res; 354 } 355 356 /** 357 * add_fat_entry: 358 * Populate last FAT entry. 359 */ 360 static TEE_Result add_fat_entry(struct file_handle *fh) 361 { 362 TEE_Result res = TEE_ERROR_GENERIC; 363 364 fh->rpmb_fat_address += sizeof(struct rpmb_fat_entry); 365 res = write_fat_entry(fh, true); 366 fh->rpmb_fat_address -= sizeof(struct rpmb_fat_entry); 367 368 return res; 369 } 370 371 int tee_rpmb_fs_read(const char *filename, uint8_t *buf, size_t size) 372 { 373 TEE_Result res = TEE_ERROR_GENERIC; 374 struct file_handle *fh = NULL; 375 int read_size = -1; 376 377 if (filename == NULL || buf == NULL || 378 strlen(filename) >= FILENAME_LENGTH - 1) { 379 res = TEE_ERROR_BAD_PARAMETERS; 380 goto out; 381 } 382 383 fh = alloc_file_handle(filename); 384 if (fh == NULL) { 385 res = TEE_ERROR_OUT_OF_MEMORY; 386 goto out; 387 } 388 389 res = read_fat(fh, NULL); 390 if (res != TEE_SUCCESS) 391 goto out; 392 393 if (size < fh->fat_entry.data_size) { 394 res = TEE_ERROR_SHORT_BUFFER; 395 goto out; 396 } 397 398 res = tee_rpmb_read(DEV_ID, fh->fat_entry.start_address, buf, 399 fh->fat_entry.data_size); 400 401 out: 402 if (res == TEE_SUCCESS) 403 read_size = fh->fat_entry.data_size; 404 405 free(fh); 406 407 return read_size; 408 } 409 410 int tee_rpmb_fs_write(const char *filename, uint8_t *buf, size_t size) 411 { 412 TEE_Result res = TEE_ERROR_GENERIC; 413 struct file_handle *fh = NULL; 414 tee_mm_pool_t p; 415 tee_mm_entry_t *mm = NULL; 416 size_t length; 417 uint32_t mm_flags; 418 419 if (filename == NULL || buf == NULL) { 420 res = TEE_ERROR_BAD_PARAMETERS; 421 goto out; 422 } 423 424 length = strlen(filename); 425 if ((length >= FILENAME_LENGTH - 1) || (length == 0)) { 426 res = TEE_ERROR_BAD_PARAMETERS; 427 goto out; 428 } 429 430 /* Create a FAT entry for the file to write. */ 431 fh = alloc_file_handle(filename); 432 if (fh == NULL) { 433 res = TEE_ERROR_OUT_OF_MEMORY; 434 goto out; 435 } 436 437 /* Upper memory allocation must be used for RPMB_FS. */ 438 mm_flags = TEE_MM_POOL_HI_ALLOC; 439 if (!tee_mm_init 440 (&p, RPMB_STORAGE_START_ADDRESS, RPMB_STORAGE_END_ADDRESS, 441 RPMB_BLOCK_SIZE_SHIFT, mm_flags)) { 442 res = TEE_ERROR_OUT_OF_MEMORY; 443 goto out; 444 } 445 446 res = read_fat(fh, &p); 447 if (res != TEE_SUCCESS) 448 goto out; 449 450 mm = tee_mm_alloc(&p, size); 451 if (mm == NULL) { 452 res = TEE_ERROR_OUT_OF_MEMORY; 453 goto out; 454 } 455 456 if ((fh->fat_entry.flags & FILE_IS_LAST_ENTRY) != 0) { 457 res = add_fat_entry(fh); 458 if (res != TEE_SUCCESS) 459 goto out; 460 } 461 462 memset(&fh->fat_entry, 0, sizeof(struct rpmb_fat_entry)); 463 memcpy(fh->fat_entry.filename, filename, length); 464 fh->fat_entry.data_size = size; 465 fh->fat_entry.flags = FILE_IS_ACTIVE; 466 fh->fat_entry.start_address = tee_mm_get_smem(mm); 467 468 res = tee_rpmb_write(DEV_ID, fh->fat_entry.start_address, buf, size); 469 if (res != TEE_SUCCESS) 470 goto out; 471 472 res = write_fat_entry(fh, true); 473 474 out: 475 free(fh); 476 if (mm != NULL) 477 tee_mm_final(&p); 478 479 if (res == TEE_SUCCESS) 480 return size; 481 482 return -1; 483 } 484 485 TEE_Result tee_rpmb_fs_rm(const char *filename) 486 { 487 TEE_Result res = TEE_ERROR_GENERIC; 488 struct file_handle *fh = NULL; 489 490 if (filename == NULL || strlen(filename) >= FILENAME_LENGTH - 1) { 491 res = TEE_ERROR_BAD_PARAMETERS; 492 goto out; 493 } 494 495 fh = alloc_file_handle(filename); 496 if (fh == NULL) { 497 res = TEE_ERROR_OUT_OF_MEMORY; 498 goto out; 499 } 500 501 res = read_fat(fh, NULL); 502 if (res != TEE_SUCCESS) 503 goto out; 504 505 /* Clear this file entry. */ 506 memset(&fh->fat_entry, 0, sizeof(struct rpmb_fat_entry)); 507 res = write_fat_entry(fh, false); 508 509 out: 510 free(fh); 511 512 return res; 513 } 514 515 TEE_Result tee_rpmb_fs_rename(const char *old_name, const char *new_name) 516 { 517 TEE_Result res = TEE_ERROR_GENERIC; 518 struct file_handle *fh_old = NULL; 519 struct file_handle *fh_new = NULL; 520 uint32_t old_len; 521 uint32_t new_len; 522 523 if (old_name == NULL || new_name == NULL) { 524 res = TEE_ERROR_BAD_PARAMETERS; 525 goto out; 526 } 527 528 old_len = strlen(old_name); 529 new_len = strlen(new_name); 530 531 if ((old_len >= FILENAME_LENGTH - 1) || 532 (new_len >= FILENAME_LENGTH - 1) || (new_len == 0)) { 533 res = TEE_ERROR_BAD_PARAMETERS; 534 goto out; 535 } 536 537 fh_old = alloc_file_handle(old_name); 538 if (fh_old == NULL) { 539 res = TEE_ERROR_OUT_OF_MEMORY; 540 goto out; 541 } 542 543 fh_new = alloc_file_handle(new_name); 544 if (fh_new == NULL) { 545 res = TEE_ERROR_OUT_OF_MEMORY; 546 goto out; 547 } 548 549 res = read_fat(fh_old, NULL); 550 if (res != TEE_SUCCESS) 551 goto out; 552 553 res = read_fat(fh_new, NULL); 554 if (res == TEE_SUCCESS) { 555 res = TEE_ERROR_BAD_PARAMETERS; 556 goto out; 557 } 558 559 memset(fh_old->fat_entry.filename, 0, FILENAME_LENGTH); 560 memcpy(fh_old->fat_entry.filename, new_name, new_len); 561 562 res = write_fat_entry(fh_old, false); 563 564 out: 565 free(fh_old); 566 free(fh_new); 567 568 return res; 569 } 570 571 TEE_Result tee_rpmb_fs_stat(const char *filename, 572 struct tee_rpmb_fs_stat *stat) 573 { 574 TEE_Result res = TEE_ERROR_GENERIC; 575 struct file_handle *fh = NULL; 576 577 if (stat == NULL || filename == NULL) { 578 res = TEE_ERROR_BAD_PARAMETERS; 579 goto out; 580 } 581 582 fh = alloc_file_handle(filename); 583 if (fh == NULL) { 584 res = TEE_ERROR_OUT_OF_MEMORY; 585 goto out; 586 } 587 588 res = read_fat(fh, NULL); 589 if (res != TEE_SUCCESS) 590 goto out; 591 592 stat->size = fh->fat_entry.data_size; 593 stat->reserved = 0; 594 595 out: 596 free(fh); 597 return res; 598 } 599