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