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