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