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