1 /* 2 * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * Redistributions of source code must retain the above copyright notice, this 8 * list of conditions and the following disclaimer. 9 * 10 * Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * Neither the name of ARM nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without specific 16 * prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 34 #include <assert.h> 35 #include <errno.h> 36 #include <getopt.h> 37 #include <limits.h> 38 #include <stdarg.h> 39 #include <stdint.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 45 #include <openssl/sha.h> 46 47 #include "fiptool.h" 48 #include "firmware_image_package.h" 49 #include "tbbr_config.h" 50 51 #define OPT_TOC_ENTRY 0 52 #define OPT_PLAT_TOC_FLAGS 1 53 54 static int info_cmd(int argc, char *argv[]); 55 static void info_usage(void); 56 static int create_cmd(int argc, char *argv[]); 57 static void create_usage(void); 58 static int update_cmd(int argc, char *argv[]); 59 static void update_usage(void); 60 static int unpack_cmd(int argc, char *argv[]); 61 static void unpack_usage(void); 62 static int remove_cmd(int argc, char *argv[]); 63 static void remove_usage(void); 64 static int version_cmd(int argc, char *argv[]); 65 static void version_usage(void); 66 static int help_cmd(int argc, char *argv[]); 67 static void usage(void); 68 69 /* Available subcommands. */ 70 static cmd_t cmds[] = { 71 { .name = "info", .handler = info_cmd, .usage = info_usage }, 72 { .name = "create", .handler = create_cmd, .usage = create_usage }, 73 { .name = "update", .handler = update_cmd, .usage = update_usage }, 74 { .name = "unpack", .handler = unpack_cmd, .usage = unpack_usage }, 75 { .name = "remove", .handler = remove_cmd, .usage = remove_usage }, 76 { .name = "version", .handler = version_cmd, .usage = version_usage }, 77 { .name = "help", .handler = help_cmd, .usage = NULL }, 78 }; 79 80 static image_t *images[MAX_IMAGES]; 81 static size_t nr_images; 82 static uuid_t uuid_null = { 0 }; 83 static int verbose; 84 85 static void vlog(int prio, const char *msg, va_list ap) 86 { 87 char *prefix[] = { "DEBUG", "WARN", "ERROR" }; 88 89 fprintf(stderr, "%s: ", prefix[prio]); 90 vfprintf(stderr, msg, ap); 91 fputc('\n', stderr); 92 } 93 94 static void log_dbgx(const char *msg, ...) 95 { 96 va_list ap; 97 98 va_start(ap, msg); 99 vlog(LOG_DBG, msg, ap); 100 va_end(ap); 101 } 102 103 static void log_warnx(const char *msg, ...) 104 { 105 va_list ap; 106 107 va_start(ap, msg); 108 vlog(LOG_WARN, msg, ap); 109 va_end(ap); 110 } 111 112 static void log_err(const char *msg, ...) 113 { 114 char buf[512]; 115 va_list ap; 116 117 va_start(ap, msg); 118 snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno)); 119 vlog(LOG_ERR, buf, ap); 120 va_end(ap); 121 exit(1); 122 } 123 124 static void log_errx(const char *msg, ...) 125 { 126 va_list ap; 127 128 va_start(ap, msg); 129 vlog(LOG_ERR, msg, ap); 130 va_end(ap); 131 exit(1); 132 } 133 134 static char *xstrdup(const char *s, const char *msg) 135 { 136 char *d; 137 138 d = strdup(s); 139 if (d == NULL) 140 log_errx("strdup: ", msg); 141 return d; 142 } 143 144 static void *xmalloc(size_t size, const char *msg) 145 { 146 void *d; 147 148 d = malloc(size); 149 if (d == NULL) 150 log_errx("malloc: ", msg); 151 return d; 152 } 153 154 static void add_image(image_t *image) 155 { 156 if (nr_images + 1 > MAX_IMAGES) 157 log_errx("Too many images"); 158 images[nr_images++] = image; 159 } 160 161 static void free_image(image_t *image) 162 { 163 free(image->buffer); 164 free(image); 165 } 166 167 static void replace_image(image_t *image_dst, image_t *image_src) 168 { 169 int i; 170 171 for (i = 0; i < nr_images; i++) { 172 if (images[i] == image_dst) { 173 free_image(images[i]); 174 images[i] = image_src; 175 break; 176 } 177 } 178 assert(i != nr_images); 179 } 180 181 static void remove_image(image_t *image) 182 { 183 int i; 184 185 for (i = 0; i < nr_images; i++) { 186 if (images[i] == image) { 187 free_image(images[i]); 188 images[i] = NULL; 189 break; 190 } 191 } 192 assert(i != nr_images); 193 194 /* Compact array. */ 195 memmove(&images[i], &images[i + 1], 196 (nr_images - i - 1) * sizeof(*images)); 197 nr_images--; 198 } 199 200 static void free_images(void) 201 { 202 int i; 203 204 for (i = 0; i < nr_images; i++) { 205 free_image(images[i]); 206 images[i] = NULL; 207 } 208 } 209 210 static toc_entry_t *lookup_entry_from_uuid(uuid_t *uuid) 211 { 212 toc_entry_t *toc_entry = toc_entries; 213 214 for (; toc_entry->cmdline_name != NULL; toc_entry++) 215 if (memcmp(&toc_entry->uuid, uuid, sizeof(uuid_t)) == 0) 216 return toc_entry; 217 return NULL; 218 } 219 220 static image_t *lookup_image_from_uuid(uuid_t *uuid) 221 { 222 image_t *image; 223 int i; 224 225 for (i = 0; i < nr_images; i++) { 226 image = images[i]; 227 if (memcmp(&image->uuid, uuid, sizeof(uuid_t)) == 0) 228 return image; 229 } 230 return NULL; 231 } 232 233 static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out) 234 { 235 struct stat st; 236 FILE *fp; 237 char *buf, *bufend; 238 fip_toc_header_t *toc_header; 239 fip_toc_entry_t *toc_entry; 240 image_t *image; 241 int terminated = 0; 242 243 fp = fopen(filename, "r"); 244 if (fp == NULL) 245 log_err("fopen %s", filename); 246 247 if (fstat(fileno(fp), &st) == -1) 248 log_err("fstat %s", filename); 249 250 buf = xmalloc(st.st_size, "failed to load file into memory"); 251 if (fread(buf, 1, st.st_size, fp) != st.st_size) 252 log_errx("Failed to read %s", filename); 253 bufend = buf + st.st_size; 254 fclose(fp); 255 256 if (st.st_size < sizeof(fip_toc_header_t)) 257 log_errx("FIP %s is truncated", filename); 258 259 toc_header = (fip_toc_header_t *)buf; 260 toc_entry = (fip_toc_entry_t *)(toc_header + 1); 261 262 if (toc_header->name != TOC_HEADER_NAME) 263 log_errx("%s is not a FIP file", filename); 264 265 /* Return the ToC header if the caller wants it. */ 266 if (toc_header_out != NULL) 267 *toc_header_out = *toc_header; 268 269 /* Walk through each ToC entry in the file. */ 270 while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) { 271 /* Found the ToC terminator, we are done. */ 272 if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) { 273 terminated = 1; 274 break; 275 } 276 277 /* 278 * Build a new image out of the ToC entry and add it to the 279 * table of images. 280 */ 281 image = xmalloc(sizeof(*image), 282 "failed to allocate memory for image"); 283 memcpy(&image->uuid, &toc_entry->uuid, sizeof(uuid_t)); 284 image->buffer = xmalloc(toc_entry->size, 285 "failed to allocate image buffer, is FIP file corrupted?"); 286 /* Overflow checks before memory copy. */ 287 if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address) 288 log_errx("FIP %s is corrupted", filename); 289 if (toc_entry->size + toc_entry->offset_address > st.st_size) 290 log_errx("FIP %s is corrupted", filename); 291 292 memcpy(image->buffer, buf + toc_entry->offset_address, 293 toc_entry->size); 294 image->size = toc_entry->size; 295 296 add_image(image); 297 298 toc_entry++; 299 } 300 301 if (terminated == 0) 302 log_errx("FIP %s does not have a ToC terminator entry", 303 filename); 304 free(buf); 305 return 0; 306 } 307 308 static image_t *read_image_from_file(const uuid_t *uuid, const char *filename) 309 { 310 struct stat st; 311 image_t *image; 312 FILE *fp; 313 314 assert(uuid != NULL); 315 316 fp = fopen(filename, "r"); 317 if (fp == NULL) 318 log_err("fopen %s", filename); 319 320 if (fstat(fileno(fp), &st) == -1) 321 log_errx("fstat %s", filename); 322 323 image = xmalloc(sizeof(*image), "failed to allocate memory for image"); 324 memcpy(&image->uuid, uuid, sizeof(uuid_t)); 325 image->buffer = xmalloc(st.st_size, "failed to allocate image buffer"); 326 if (fread(image->buffer, 1, st.st_size, fp) != st.st_size) 327 log_errx("Failed to read %s", filename); 328 image->size = st.st_size; 329 330 fclose(fp); 331 return image; 332 } 333 334 static int write_image_to_file(const image_t *image, const char *filename) 335 { 336 FILE *fp; 337 338 fp = fopen(filename, "w"); 339 if (fp == NULL) 340 log_err("fopen"); 341 if (fwrite(image->buffer, 1, image->size, fp) != image->size) 342 log_errx("Failed to write %s", filename); 343 fclose(fp); 344 return 0; 345 } 346 347 static int fill_common_opts(struct option *opts, int has_arg) 348 { 349 int i; 350 351 for (i = 0; toc_entries[i].cmdline_name != NULL; i++) { 352 opts[i].name = toc_entries[i].cmdline_name; 353 opts[i].has_arg = has_arg; 354 opts[i].flag = NULL; 355 opts[i].val = 0; 356 } 357 return i; 358 } 359 360 static void add_opt(struct option *opts, int idx, char *name, 361 int has_arg, int val) 362 { 363 opts[idx].name = name; 364 opts[idx].has_arg = has_arg; 365 opts[idx].flag = NULL; 366 opts[idx].val = val; 367 } 368 369 static void md_print(unsigned char *md, size_t len) 370 { 371 size_t i; 372 373 for (i = 0; i < len; i++) 374 printf("%02x", md[i]); 375 } 376 377 static int info_cmd(int argc, char *argv[]) 378 { 379 image_t *image; 380 uint64_t image_offset; 381 uint64_t image_size = 0; 382 fip_toc_header_t toc_header; 383 int i; 384 385 if (argc != 2) 386 info_usage(); 387 argc--, argv++; 388 389 parse_fip(argv[0], &toc_header); 390 391 if (verbose) { 392 log_dbgx("toc_header[name]: 0x%llX", 393 (unsigned long long)toc_header.name); 394 log_dbgx("toc_header[serial_number]: 0x%llX", 395 (unsigned long long)toc_header.serial_number); 396 log_dbgx("toc_header[flags]: 0x%llX", 397 (unsigned long long)toc_header.flags); 398 } 399 400 image_offset = sizeof(fip_toc_header_t) + 401 (sizeof(fip_toc_entry_t) * (nr_images + 1)); 402 403 for (i = 0; i < nr_images; i++) { 404 toc_entry_t *toc_entry; 405 406 image = images[i]; 407 toc_entry = lookup_entry_from_uuid(&image->uuid); 408 if (toc_entry != NULL) 409 printf("%s: ", toc_entry->name); 410 else 411 printf("Unknown entry: "); 412 image_size = image->size; 413 printf("offset=0x%llX, size=0x%llX", 414 (unsigned long long)image_offset, 415 (unsigned long long)image_size); 416 if (toc_entry != NULL) 417 printf(", cmdline=\"--%s\"", 418 toc_entry->cmdline_name); 419 if (verbose) { 420 unsigned char md[SHA256_DIGEST_LENGTH]; 421 422 SHA256(image->buffer, image_size, md); 423 printf(", sha256="); 424 md_print(md, sizeof(md)); 425 } 426 putchar('\n'); 427 image_offset += image_size; 428 } 429 430 free_images(); 431 return 0; 432 } 433 434 static void info_usage(void) 435 { 436 printf("fiptool info FIP_FILENAME\n"); 437 exit(1); 438 } 439 440 static int pack_images(char *filename, uint64_t toc_flags) 441 { 442 FILE *fp; 443 image_t *image; 444 fip_toc_header_t *toc_header; 445 fip_toc_entry_t *toc_entry; 446 char *buf; 447 uint64_t entry_offset, buf_size, payload_size; 448 int i; 449 450 /* Calculate total payload size and allocate scratch buffer. */ 451 payload_size = 0; 452 for (i = 0; i < nr_images; i++) 453 payload_size += images[i]->size; 454 455 buf_size = sizeof(fip_toc_header_t) + 456 sizeof(fip_toc_entry_t) * (nr_images + 1); 457 buf = calloc(1, buf_size); 458 if (buf == NULL) 459 log_err("calloc"); 460 461 /* Build up header and ToC entries from the image table. */ 462 toc_header = (fip_toc_header_t *)buf; 463 toc_header->name = TOC_HEADER_NAME; 464 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER; 465 toc_header->flags = toc_flags; 466 467 toc_entry = (fip_toc_entry_t *)(toc_header + 1); 468 469 entry_offset = buf_size; 470 for (i = 0; i < nr_images; i++) { 471 image = images[i]; 472 memcpy(&toc_entry->uuid, &image->uuid, sizeof(uuid_t)); 473 toc_entry->offset_address = entry_offset; 474 toc_entry->size = image->size; 475 toc_entry->flags = 0; 476 entry_offset += toc_entry->size; 477 toc_entry++; 478 } 479 480 /* Append a null uuid entry to mark the end of ToC entries. */ 481 memcpy(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)); 482 toc_entry->offset_address = entry_offset; 483 toc_entry->size = 0; 484 toc_entry->flags = 0; 485 486 /* Generate the FIP file. */ 487 fp = fopen(filename, "w"); 488 if (fp == NULL) 489 log_err("fopen %s", filename); 490 491 if (verbose) 492 log_dbgx("Metadata size: %zu bytes", buf_size); 493 494 if (fwrite(buf, 1, buf_size, fp) != buf_size) 495 log_errx("Failed to write image to %s", filename); 496 free(buf); 497 498 if (verbose) 499 log_dbgx("Payload size: %zu bytes", payload_size); 500 501 for (i = 0; i < nr_images; i++) { 502 image = images[i]; 503 if (fwrite(image->buffer, 1, image->size, fp) != image->size) 504 log_errx("Failed to write image to %s", filename); 505 } 506 507 fclose(fp); 508 return 0; 509 } 510 511 /* 512 * This function is shared between the create and update subcommands. 513 * The difference between the two subcommands is that when the FIP file 514 * is created, the parsing of an existing FIP is skipped. This results 515 * in update_fip() creating the new FIP file from scratch because the 516 * internal image table is not populated. 517 */ 518 static void update_fip(void) 519 { 520 toc_entry_t *toc_entry; 521 image_t *new_image, *old_image; 522 523 /* Add or replace images in the FIP file. */ 524 for (toc_entry = toc_entries; 525 toc_entry->cmdline_name != NULL; 526 toc_entry++) { 527 if (toc_entry->action != DO_PACK) 528 continue; 529 530 new_image = read_image_from_file(&toc_entry->uuid, 531 toc_entry->action_arg); 532 old_image = lookup_image_from_uuid(&toc_entry->uuid); 533 if (old_image != NULL) { 534 if (verbose) 535 log_dbgx("Replacing image %s.bin with %s", 536 toc_entry->cmdline_name, 537 toc_entry->action_arg); 538 replace_image(old_image, new_image); 539 } else { 540 if (verbose) 541 log_dbgx("Adding image %s", 542 toc_entry->action_arg); 543 add_image(new_image); 544 } 545 546 free(toc_entry->action_arg); 547 toc_entry->action_arg = NULL; 548 } 549 } 550 551 static void parse_plat_toc_flags(char *arg, unsigned long long *toc_flags) 552 { 553 unsigned long long flags; 554 char *endptr; 555 556 errno = 0; 557 flags = strtoull(arg, &endptr, 16); 558 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0) 559 log_errx("Invalid platform ToC flags: %s", arg); 560 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */ 561 *toc_flags |= flags << 32; 562 } 563 564 static int create_cmd(int argc, char *argv[]) 565 { 566 struct option opts[toc_entries_len + 1]; 567 unsigned long long toc_flags = 0; 568 int i; 569 570 if (argc < 2) 571 create_usage(); 572 573 i = fill_common_opts(opts, required_argument); 574 add_opt(opts, i, "plat-toc-flags", required_argument, 575 OPT_PLAT_TOC_FLAGS); 576 add_opt(opts, ++i, NULL, 0, 0); 577 578 while (1) { 579 int c, opt_index = 0; 580 581 c = getopt_long(argc, argv, "", opts, &opt_index); 582 if (c == -1) 583 break; 584 585 switch (c) { 586 case OPT_TOC_ENTRY: { 587 toc_entry_t *toc_entry; 588 589 toc_entry = &toc_entries[opt_index]; 590 toc_entry->action = DO_PACK; 591 toc_entry->action_arg = xstrdup(optarg, 592 "failed to allocate memory for argument"); 593 break; 594 } 595 case OPT_PLAT_TOC_FLAGS: 596 parse_plat_toc_flags(optarg, &toc_flags); 597 break; 598 default: 599 create_usage(); 600 } 601 } 602 argc -= optind; 603 argv += optind; 604 605 if (argc == 0) 606 create_usage(); 607 608 update_fip(); 609 610 pack_images(argv[0], toc_flags); 611 free_images(); 612 return 0; 613 } 614 615 static void create_usage(void) 616 { 617 toc_entry_t *toc_entry = toc_entries; 618 619 printf("fiptool create [--plat-toc-flags <value>] [opts] FIP_FILENAME\n"); 620 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field " 621 "occupying bits 32-47 in 64-bit ToC header.\n"); 622 fputc('\n', stderr); 623 printf("Specific images are packed with the following options:\n"); 624 for (; toc_entry->cmdline_name != NULL; toc_entry++) 625 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, 626 toc_entry->name); 627 exit(1); 628 } 629 630 static int update_cmd(int argc, char *argv[]) 631 { 632 struct option opts[toc_entries_len + 2]; 633 char outfile[FILENAME_MAX] = { 0 }; 634 fip_toc_header_t toc_header = { 0 }; 635 unsigned long long toc_flags = 0; 636 int pflag = 0; 637 int i; 638 639 if (argc < 2) 640 update_usage(); 641 642 i = fill_common_opts(opts, required_argument); 643 add_opt(opts, i, "out", required_argument, 'o'); 644 add_opt(opts, ++i, "plat-toc-flags", required_argument, 645 OPT_PLAT_TOC_FLAGS); 646 add_opt(opts, ++i, NULL, 0, 0); 647 648 while (1) { 649 int c, opt_index = 0; 650 651 c = getopt_long(argc, argv, "o:", opts, &opt_index); 652 if (c == -1) 653 break; 654 655 switch (c) { 656 case OPT_TOC_ENTRY: { 657 toc_entry_t *toc_entry; 658 659 toc_entry = &toc_entries[opt_index]; 660 toc_entry->action = DO_PACK; 661 toc_entry->action_arg = xstrdup(optarg, 662 "failed to allocate memory for argument"); 663 break; 664 } 665 case OPT_PLAT_TOC_FLAGS: { 666 parse_plat_toc_flags(optarg, &toc_flags); 667 pflag = 1; 668 break; 669 } 670 case 'o': 671 snprintf(outfile, sizeof(outfile), "%s", optarg); 672 break; 673 default: 674 update_usage(); 675 } 676 } 677 argc -= optind; 678 argv += optind; 679 680 if (argc == 0) 681 update_usage(); 682 683 if (outfile[0] == '\0') 684 snprintf(outfile, sizeof(outfile), "%s", argv[0]); 685 686 if (access(outfile, F_OK) == 0) 687 parse_fip(argv[0], &toc_header); 688 689 if (pflag) 690 toc_header.flags &= ~(0xffffULL << 32); 691 toc_flags = (toc_header.flags |= toc_flags); 692 693 update_fip(); 694 695 pack_images(outfile, toc_flags); 696 free_images(); 697 return 0; 698 } 699 700 static void update_usage(void) 701 { 702 toc_entry_t *toc_entry = toc_entries; 703 704 printf("fiptool update [--out FIP_FILENAME] " 705 "[--plat-toc-flags <value>] [opts] FIP_FILENAME\n"); 706 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n"); 707 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field " 708 "occupying bits 32-47 in 64-bit ToC header.\n"); 709 fputc('\n', stderr); 710 printf("Specific images are packed with the following options:\n"); 711 for (; toc_entry->cmdline_name != NULL; toc_entry++) 712 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, 713 toc_entry->name); 714 exit(1); 715 } 716 717 static int unpack_cmd(int argc, char *argv[]) 718 { 719 struct option opts[toc_entries_len + 3]; 720 char file[FILENAME_MAX], outdir[PATH_MAX] = { 0 }; 721 toc_entry_t *toc_entry; 722 int fflag = 0; 723 int unpack_all = 1; 724 int i; 725 726 if (argc < 2) 727 unpack_usage(); 728 729 i = fill_common_opts(opts, required_argument); 730 add_opt(opts, i, "force", no_argument, 'f'); 731 add_opt(opts, ++i, "out", required_argument, 'o'); 732 add_opt(opts, ++i, NULL, 0, 0); 733 734 while (1) { 735 int c, opt_index = 0; 736 737 c = getopt_long(argc, argv, "fo:", opts, &opt_index); 738 if (c == -1) 739 break; 740 741 switch (c) { 742 case OPT_TOC_ENTRY: 743 unpack_all = 0; 744 toc_entry = &toc_entries[opt_index]; 745 toc_entry->action = DO_UNPACK; 746 toc_entry->action_arg = xstrdup(optarg, 747 "failed to allocate memory for argument"); 748 break; 749 case 'f': 750 fflag = 1; 751 break; 752 case 'o': 753 snprintf(outdir, sizeof(outdir), "%s", optarg); 754 break; 755 default: 756 unpack_usage(); 757 } 758 } 759 argc -= optind; 760 argv += optind; 761 762 if (argc == 0) 763 unpack_usage(); 764 765 parse_fip(argv[0], NULL); 766 767 if (outdir[0] != '\0') 768 if (chdir(outdir) == -1) 769 log_err("chdir %s", outdir); 770 771 /* Unpack all specified images. */ 772 for (toc_entry = toc_entries; 773 toc_entry->cmdline_name != NULL; 774 toc_entry++) { 775 image_t *image; 776 777 if (!unpack_all && toc_entry->action != DO_UNPACK) 778 continue; 779 780 /* Build filename. */ 781 if (toc_entry->action_arg == NULL) 782 snprintf(file, sizeof(file), "%s.bin", 783 toc_entry->cmdline_name); 784 else 785 snprintf(file, sizeof(file), "%s", 786 toc_entry->action_arg); 787 788 image = lookup_image_from_uuid(&toc_entry->uuid); 789 if (image == NULL) { 790 if (!unpack_all) 791 log_warnx("Requested image %s is not in %s", 792 file, argv[0]); 793 free(toc_entry->action_arg); 794 toc_entry->action_arg = NULL; 795 continue; 796 } 797 798 if (access(file, F_OK) != 0 || fflag) { 799 if (verbose) 800 log_dbgx("Unpacking %s", file); 801 write_image_to_file(image, file); 802 } else { 803 log_warnx("File %s already exists, use --force to overwrite it", 804 file); 805 } 806 807 free(toc_entry->action_arg); 808 toc_entry->action_arg = NULL; 809 } 810 811 free_images(); 812 return 0; 813 } 814 815 static void unpack_usage(void) 816 { 817 toc_entry_t *toc_entry = toc_entries; 818 819 printf("fiptool unpack [--force] [--out <path>] [opts] FIP_FILENAME\n"); 820 printf(" --force\tIf the output file already exists, use --force to " 821 "overwrite it.\n"); 822 printf(" --out path\tSet the output directory path.\n"); 823 fputc('\n', stderr); 824 printf("Specific images are unpacked with the following options:\n"); 825 for (; toc_entry->cmdline_name != NULL; toc_entry++) 826 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, 827 toc_entry->name); 828 fputc('\n', stderr); 829 printf("If no options are provided, all images will be unpacked.\n"); 830 exit(1); 831 } 832 833 static int remove_cmd(int argc, char *argv[]) 834 { 835 struct option opts[toc_entries_len + 2]; 836 char outfile[FILENAME_MAX] = { 0 }; 837 fip_toc_header_t toc_header; 838 toc_entry_t *toc_entry; 839 int fflag = 0; 840 int i; 841 842 if (argc < 2) 843 remove_usage(); 844 845 i = fill_common_opts(opts, no_argument); 846 add_opt(opts, i, "force", no_argument, 'f'); 847 add_opt(opts, ++i, "out", required_argument, 'o'); 848 add_opt(opts, ++i, NULL, 0, 0); 849 850 while (1) { 851 int c, opt_index = 0; 852 853 c = getopt_long(argc, argv, "fo:", opts, &opt_index); 854 if (c == -1) 855 break; 856 857 switch (c) { 858 case OPT_TOC_ENTRY: 859 toc_entry = &toc_entries[opt_index]; 860 toc_entry->action = DO_REMOVE; 861 break; 862 case 'f': 863 fflag = 1; 864 break; 865 case 'o': 866 snprintf(outfile, sizeof(outfile), "%s", optarg); 867 break; 868 default: 869 remove_usage(); 870 } 871 } 872 argc -= optind; 873 argv += optind; 874 875 if (argc == 0) 876 remove_usage(); 877 878 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag) 879 log_errx("File %s already exists, use --force to overwrite it", 880 outfile); 881 882 if (outfile[0] == '\0') 883 snprintf(outfile, sizeof(outfile), "%s", argv[0]); 884 885 parse_fip(argv[0], &toc_header); 886 887 for (toc_entry = toc_entries; 888 toc_entry->cmdline_name != NULL; 889 toc_entry++) { 890 image_t *image; 891 892 if (toc_entry->action != DO_REMOVE) 893 continue; 894 image = lookup_image_from_uuid(&toc_entry->uuid); 895 if (image != NULL) { 896 if (verbose) 897 log_dbgx("Removing %s.bin", 898 toc_entry->cmdline_name); 899 remove_image(image); 900 } else { 901 log_warnx("Requested image %s.bin is not in %s", 902 toc_entry->cmdline_name, argv[0]); 903 } 904 } 905 906 pack_images(outfile, toc_header.flags); 907 free_images(); 908 return 0; 909 } 910 911 static void remove_usage(void) 912 { 913 toc_entry_t *toc_entry = toc_entries; 914 915 printf("fiptool remove [--force] [--out FIP_FILENAME] [opts] FIP_FILENAME\n"); 916 printf(" --force\t\tIf the output FIP file already exists, use --force to " 917 "overwrite it.\n"); 918 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n"); 919 fputc('\n', stderr); 920 printf("Specific images are removed with the following options:\n"); 921 for (; toc_entry->cmdline_name != NULL; toc_entry++) 922 printf(" --%-16s\t%s\n", toc_entry->cmdline_name, 923 toc_entry->name); 924 exit(1); 925 } 926 927 static int version_cmd(int argc, char *argv[]) 928 { 929 #ifdef VERSION 930 puts(VERSION); 931 #else 932 /* If built from fiptool directory, VERSION is not set. */ 933 puts("Unknown version"); 934 #endif 935 return 0; 936 } 937 938 static void version_usage(void) 939 { 940 printf("fiptool version\n"); 941 exit(1); 942 } 943 944 static int help_cmd(int argc, char *argv[]) 945 { 946 int i; 947 948 if (argc < 2) 949 usage(); 950 argc--, argv++; 951 952 for (i = 0; i < NELEM(cmds); i++) { 953 if (strcmp(cmds[i].name, argv[0]) == 0 && 954 cmds[i].usage != NULL) 955 cmds[i].usage(); 956 } 957 if (i == NELEM(cmds)) 958 printf("No help for subcommand '%s'\n", argv[0]); 959 return 0; 960 } 961 962 static void usage(void) 963 { 964 printf("usage: [--verbose] fiptool <command> [<args>]\n"); 965 printf("Global options supported:\n"); 966 printf(" --verbose\tEnable verbose output for all commands.\n"); 967 fputc('\n', stderr); 968 printf("Commands supported:\n"); 969 printf(" info\t\tList images contained in FIP.\n"); 970 printf(" create\tCreate a new FIP with the given images.\n"); 971 printf(" update\tUpdate an existing FIP with the given images.\n"); 972 printf(" unpack\tUnpack images from FIP.\n"); 973 printf(" remove\tRemove images from FIP.\n"); 974 printf(" version\tShow fiptool version.\n"); 975 printf(" help\t\tShow help for given command.\n"); 976 exit(1); 977 } 978 979 int main(int argc, char *argv[]) 980 { 981 int i, ret = 0; 982 983 while (1) { 984 int c, opt_index = 0; 985 static struct option opts[] = { 986 { "verbose", no_argument, NULL, 'v' }, 987 { NULL, no_argument, NULL, 0 } 988 }; 989 990 /* 991 * Set POSIX mode so getopt stops at the first non-option 992 * which is the subcommand. 993 */ 994 c = getopt_long(argc, argv, "+v", opts, &opt_index); 995 if (c == -1) 996 break; 997 998 switch (c) { 999 case 'v': 1000 verbose = 1; 1001 break; 1002 default: 1003 usage(); 1004 } 1005 } 1006 argc -= optind; 1007 argv += optind; 1008 /* Reset optind for subsequent getopt processing. */ 1009 optind = 0; 1010 1011 if (argc == 0) 1012 usage(); 1013 1014 for (i = 0; i < NELEM(cmds); i++) { 1015 if (strcmp(cmds[i].name, argv[0]) == 0) { 1016 ret = cmds[i].handler(argc, argv); 1017 break; 1018 } 1019 } 1020 if (i == NELEM(cmds)) 1021 usage(); 1022 return ret; 1023 } 1024