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