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