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 " 826 "pointed to by file.\n"); 827 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field " 828 "occupying bits 32-47 in 64-bit ToC header.\n"); 829 fputc('\n', stderr); 830 printf("Specific images are packed with the following options:\n"); 831 for (; toc_entry->cmdline_name != NULL; toc_entry++) 832 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, 833 toc_entry->name); 834 exit(1); 835 } 836 837 static int update_cmd(int argc, char *argv[]) 838 { 839 struct option *opts = NULL; 840 size_t nr_opts = 0; 841 char outfile[PATH_MAX] = { 0 }; 842 fip_toc_header_t toc_header = { 0 }; 843 unsigned long long toc_flags = 0; 844 unsigned long align = 1; 845 int pflag = 0; 846 847 if (argc < 2) 848 update_usage(); 849 850 opts = fill_common_opts(opts, &nr_opts, required_argument); 851 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN); 852 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b'); 853 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o'); 854 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument, 855 OPT_PLAT_TOC_FLAGS); 856 opts = add_opt(opts, &nr_opts, NULL, 0, 0); 857 858 while (1) { 859 int c, opt_index = 0; 860 861 c = getopt_long(argc, argv, "b:o:", opts, &opt_index); 862 if (c == -1) 863 break; 864 865 switch (c) { 866 case OPT_TOC_ENTRY: { 867 image_desc_t *desc; 868 869 desc = lookup_image_desc_from_opt(opts[opt_index].name); 870 set_image_desc_action(desc, DO_PACK, optarg); 871 break; 872 } 873 case OPT_PLAT_TOC_FLAGS: 874 parse_plat_toc_flags(optarg, &toc_flags); 875 pflag = 1; 876 break; 877 case 'b': { 878 char name[_UUID_STR_LEN + 1]; 879 char filename[PATH_MAX] = { 0 }; 880 uuid_t uuid = { 0 }; 881 image_desc_t *desc; 882 883 parse_blob_opt(optarg, &uuid, 884 filename, sizeof(filename)); 885 886 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 || 887 filename[0] == '\0') 888 update_usage(); 889 890 desc = lookup_image_desc_from_uuid(&uuid); 891 if (desc == NULL) { 892 uuid_to_str(name, sizeof(name), &uuid); 893 desc = new_image_desc(&uuid, name, "blob"); 894 add_image_desc(desc); 895 } 896 set_image_desc_action(desc, DO_PACK, filename); 897 break; 898 } 899 case OPT_ALIGN: 900 align = get_image_align(optarg); 901 break; 902 case 'o': 903 snprintf(outfile, sizeof(outfile), "%s", optarg); 904 break; 905 default: 906 update_usage(); 907 } 908 } 909 argc -= optind; 910 argv += optind; 911 free(opts); 912 913 if (argc == 0) 914 update_usage(); 915 916 if (outfile[0] == '\0') 917 snprintf(outfile, sizeof(outfile), "%s", argv[0]); 918 919 if (access(argv[0], F_OK) == 0) 920 parse_fip(argv[0], &toc_header); 921 922 if (pflag) 923 toc_header.flags &= ~(0xffffULL << 32); 924 toc_flags = (toc_header.flags |= toc_flags); 925 926 update_fip(); 927 928 pack_images(outfile, toc_flags, align); 929 free_images(); 930 return 0; 931 } 932 933 static void update_usage(void) 934 { 935 toc_entry_t *toc_entry = toc_entries; 936 937 printf("fiptool update [opts] FIP_FILENAME\n"); 938 printf("\n"); 939 printf("Options:\n"); 940 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n"); 941 printf(" --blob uuid=...,file=...\tAdd or update an image " 942 "with the given UUID pointed to by file.\n"); 943 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n"); 944 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field " 945 "occupying bits 32-47 in 64-bit ToC header.\n"); 946 fputc('\n', stderr); 947 printf("Specific images are packed with the following options:\n"); 948 for (; toc_entry->cmdline_name != NULL; toc_entry++) 949 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, 950 toc_entry->name); 951 exit(1); 952 } 953 954 static int unpack_cmd(int argc, char *argv[]) 955 { 956 struct option *opts = NULL; 957 size_t nr_opts = 0; 958 char outdir[PATH_MAX] = { 0 }; 959 image_desc_t *desc; 960 int fflag = 0; 961 int unpack_all = 1; 962 963 if (argc < 2) 964 unpack_usage(); 965 966 opts = fill_common_opts(opts, &nr_opts, required_argument); 967 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b'); 968 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f'); 969 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o'); 970 opts = add_opt(opts, &nr_opts, NULL, 0, 0); 971 972 while (1) { 973 int c, opt_index = 0; 974 975 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index); 976 if (c == -1) 977 break; 978 979 switch (c) { 980 case OPT_TOC_ENTRY: { 981 image_desc_t *desc; 982 983 desc = lookup_image_desc_from_opt(opts[opt_index].name); 984 set_image_desc_action(desc, DO_UNPACK, optarg); 985 unpack_all = 0; 986 break; 987 } 988 case 'b': { 989 char name[_UUID_STR_LEN + 1]; 990 char filename[PATH_MAX] = { 0 }; 991 uuid_t uuid = { 0 }; 992 image_desc_t *desc; 993 994 parse_blob_opt(optarg, &uuid, 995 filename, sizeof(filename)); 996 997 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 || 998 filename[0] == '\0') 999 unpack_usage(); 1000 1001 desc = lookup_image_desc_from_uuid(&uuid); 1002 if (desc == NULL) { 1003 uuid_to_str(name, sizeof(name), &uuid); 1004 desc = new_image_desc(&uuid, name, "blob"); 1005 add_image_desc(desc); 1006 } 1007 set_image_desc_action(desc, DO_UNPACK, filename); 1008 unpack_all = 0; 1009 break; 1010 } 1011 case 'f': 1012 fflag = 1; 1013 break; 1014 case 'o': 1015 snprintf(outdir, sizeof(outdir), "%s", optarg); 1016 break; 1017 default: 1018 unpack_usage(); 1019 } 1020 } 1021 argc -= optind; 1022 argv += optind; 1023 free(opts); 1024 1025 if (argc == 0) 1026 unpack_usage(); 1027 1028 parse_fip(argv[0], NULL); 1029 1030 if (outdir[0] != '\0') 1031 if (chdir(outdir) == -1) 1032 log_err("chdir %s", outdir); 1033 1034 /* Unpack all specified images. */ 1035 for (desc = image_desc_head; desc != NULL; desc = desc->next) { 1036 char file[PATH_MAX]; 1037 image_t *image; 1038 1039 if (!unpack_all && desc->action != DO_UNPACK) 1040 continue; 1041 1042 /* Build filename. */ 1043 if (desc->action_arg == NULL) 1044 snprintf(file, sizeof(file), "%s.bin", 1045 desc->cmdline_name); 1046 else 1047 snprintf(file, sizeof(file), "%s", 1048 desc->action_arg); 1049 1050 image = lookup_image_from_uuid(&desc->uuid); 1051 if (image == NULL) { 1052 if (!unpack_all) 1053 log_warnx("%s does not exist in %s", 1054 file, argv[0]); 1055 continue; 1056 } 1057 1058 if (access(file, F_OK) != 0 || fflag) { 1059 if (verbose) 1060 log_dbgx("Unpacking %s", file); 1061 write_image_to_file(image, file); 1062 } else { 1063 log_warnx("File %s already exists, use --force to overwrite it", 1064 file); 1065 } 1066 } 1067 1068 free_images(); 1069 return 0; 1070 } 1071 1072 static void unpack_usage(void) 1073 { 1074 toc_entry_t *toc_entry = toc_entries; 1075 1076 printf("fiptool unpack [opts] FIP_FILENAME\n"); 1077 printf("\n"); 1078 printf("Options:\n"); 1079 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID " 1080 "to file.\n"); 1081 printf(" --force\t\t\tIf the output file already exists, use --force to " 1082 "overwrite it.\n"); 1083 printf(" --out path\t\t\tSet the output directory path.\n"); 1084 fputc('\n', stderr); 1085 printf("Specific images are unpacked with the following options:\n"); 1086 for (; toc_entry->cmdline_name != NULL; toc_entry++) 1087 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, 1088 toc_entry->name); 1089 fputc('\n', stderr); 1090 printf("If no options are provided, all images will be unpacked.\n"); 1091 exit(1); 1092 } 1093 1094 static int remove_cmd(int argc, char *argv[]) 1095 { 1096 struct option *opts = NULL; 1097 size_t nr_opts = 0; 1098 char outfile[PATH_MAX] = { 0 }; 1099 fip_toc_header_t toc_header; 1100 image_desc_t *desc; 1101 unsigned long align = 1; 1102 int fflag = 0; 1103 1104 if (argc < 2) 1105 remove_usage(); 1106 1107 opts = fill_common_opts(opts, &nr_opts, no_argument); 1108 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN); 1109 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b'); 1110 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f'); 1111 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o'); 1112 opts = add_opt(opts, &nr_opts, NULL, 0, 0); 1113 1114 while (1) { 1115 int c, opt_index = 0; 1116 1117 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index); 1118 if (c == -1) 1119 break; 1120 1121 switch (c) { 1122 case OPT_TOC_ENTRY: { 1123 image_desc_t *desc; 1124 1125 desc = lookup_image_desc_from_opt(opts[opt_index].name); 1126 set_image_desc_action(desc, DO_REMOVE, NULL); 1127 break; 1128 } 1129 case OPT_ALIGN: 1130 align = get_image_align(optarg); 1131 break; 1132 case 'b': { 1133 char name[_UUID_STR_LEN + 1], filename[PATH_MAX]; 1134 uuid_t uuid = { 0 }; 1135 image_desc_t *desc; 1136 1137 parse_blob_opt(optarg, &uuid, 1138 filename, sizeof(filename)); 1139 1140 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0) 1141 remove_usage(); 1142 1143 desc = lookup_image_desc_from_uuid(&uuid); 1144 if (desc == NULL) { 1145 uuid_to_str(name, sizeof(name), &uuid); 1146 desc = new_image_desc(&uuid, name, "blob"); 1147 add_image_desc(desc); 1148 } 1149 set_image_desc_action(desc, DO_REMOVE, NULL); 1150 break; 1151 } 1152 case 'f': 1153 fflag = 1; 1154 break; 1155 case 'o': 1156 snprintf(outfile, sizeof(outfile), "%s", optarg); 1157 break; 1158 default: 1159 remove_usage(); 1160 } 1161 } 1162 argc -= optind; 1163 argv += optind; 1164 free(opts); 1165 1166 if (argc == 0) 1167 remove_usage(); 1168 1169 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag) 1170 log_errx("File %s already exists, use --force to overwrite it", 1171 outfile); 1172 1173 if (outfile[0] == '\0') 1174 snprintf(outfile, sizeof(outfile), "%s", argv[0]); 1175 1176 parse_fip(argv[0], &toc_header); 1177 1178 for (desc = image_desc_head; desc != NULL; desc = desc->next) { 1179 image_t *image; 1180 1181 if (desc->action != DO_REMOVE) 1182 continue; 1183 1184 image = lookup_image_from_uuid(&desc->uuid); 1185 if (image != NULL) { 1186 if (verbose) 1187 log_dbgx("Removing %s", 1188 desc->cmdline_name); 1189 remove_image(image); 1190 } else { 1191 log_warnx("%s does not exist in %s", 1192 desc->cmdline_name, argv[0]); 1193 } 1194 } 1195 1196 pack_images(outfile, toc_header.flags, align); 1197 free_images(); 1198 return 0; 1199 } 1200 1201 static void remove_usage(void) 1202 { 1203 toc_entry_t *toc_entry = toc_entries; 1204 1205 printf("fiptool remove [opts] FIP_FILENAME\n"); 1206 printf("\n"); 1207 printf("Options:\n"); 1208 printf(" --align <value>\tEach image is aligned to <value> (default: 1).\n"); 1209 printf(" --blob uuid=...\tRemove an image with the given UUID.\n"); 1210 printf(" --force\t\tIf the output FIP file already exists, use --force to " 1211 "overwrite it.\n"); 1212 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n"); 1213 fputc('\n', stderr); 1214 printf("Specific images are removed with the following options:\n"); 1215 for (; toc_entry->cmdline_name != NULL; toc_entry++) 1216 printf(" --%-16s\t%s\n", toc_entry->cmdline_name, 1217 toc_entry->name); 1218 exit(1); 1219 } 1220 1221 static int version_cmd(int argc, char *argv[]) 1222 { 1223 #ifdef VERSION 1224 puts(VERSION); 1225 #else 1226 /* If built from fiptool directory, VERSION is not set. */ 1227 puts("Unknown version"); 1228 #endif 1229 return 0; 1230 } 1231 1232 static void version_usage(void) 1233 { 1234 printf("fiptool version\n"); 1235 exit(1); 1236 } 1237 1238 static int help_cmd(int argc, char *argv[]) 1239 { 1240 int i; 1241 1242 if (argc < 2) 1243 usage(); 1244 argc--, argv++; 1245 1246 for (i = 0; i < NELEM(cmds); i++) { 1247 if (strcmp(cmds[i].name, argv[0]) == 0 && 1248 cmds[i].usage != NULL) 1249 cmds[i].usage(); 1250 } 1251 if (i == NELEM(cmds)) 1252 printf("No help for subcommand '%s'\n", argv[0]); 1253 return 0; 1254 } 1255 1256 static void usage(void) 1257 { 1258 printf("usage: fiptool [--verbose] <command> [<args>]\n"); 1259 printf("Global options supported:\n"); 1260 printf(" --verbose\tEnable verbose output for all commands.\n"); 1261 fputc('\n', stderr); 1262 printf("Commands supported:\n"); 1263 printf(" info\t\tList images contained in FIP.\n"); 1264 printf(" create\tCreate a new FIP with the given images.\n"); 1265 printf(" update\tUpdate an existing FIP with the given images.\n"); 1266 printf(" unpack\tUnpack images from FIP.\n"); 1267 printf(" remove\tRemove images from FIP.\n"); 1268 printf(" version\tShow fiptool version.\n"); 1269 printf(" help\t\tShow help for given command.\n"); 1270 exit(1); 1271 } 1272 1273 int main(int argc, char *argv[]) 1274 { 1275 int i, ret = 0; 1276 1277 while (1) { 1278 int c, opt_index = 0; 1279 static struct option opts[] = { 1280 { "verbose", no_argument, NULL, 'v' }, 1281 { NULL, no_argument, NULL, 0 } 1282 }; 1283 1284 /* 1285 * Set POSIX mode so getopt stops at the first non-option 1286 * which is the subcommand. 1287 */ 1288 c = getopt_long(argc, argv, "+v", opts, &opt_index); 1289 if (c == -1) 1290 break; 1291 1292 switch (c) { 1293 case 'v': 1294 verbose = 1; 1295 break; 1296 default: 1297 usage(); 1298 } 1299 } 1300 argc -= optind; 1301 argv += optind; 1302 /* Reset optind for subsequent getopt processing. */ 1303 optind = 0; 1304 1305 if (argc == 0) 1306 usage(); 1307 1308 fill_image_descs(); 1309 for (i = 0; i < NELEM(cmds); i++) { 1310 if (strcmp(cmds[i].name, argv[0]) == 0) { 1311 ret = cmds[i].handler(argc, argv); 1312 break; 1313 } 1314 } 1315 if (i == NELEM(cmds)) 1316 usage(); 1317 free_image_descs(); 1318 return ret; 1319 } 1320