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