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