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