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