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