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 static void md_print(const unsigned char *md, size_t len) 464 { 465 size_t i; 466 467 for (i = 0; i < len; i++) 468 printf("%02x", md[i]); 469 } 470 471 static int info_cmd(int argc, char *argv[]) 472 { 473 image_desc_t *desc; 474 fip_toc_header_t toc_header; 475 476 if (argc != 2) 477 info_usage(EXIT_FAILURE); 478 argc--, argv++; 479 480 parse_fip(argv[0], &toc_header); 481 482 if (verbose) { 483 log_dbgx("toc_header[name]: 0x%llX", 484 (unsigned long long)toc_header.name); 485 log_dbgx("toc_header[serial_number]: 0x%llX", 486 (unsigned long long)toc_header.serial_number); 487 log_dbgx("toc_header[flags]: 0x%llX", 488 (unsigned long long)toc_header.flags); 489 } 490 491 for (desc = image_desc_head; desc != NULL; desc = desc->next) { 492 image_t *image = desc->image; 493 494 if (image == NULL) 495 continue; 496 printf("%s: offset=0x%llX, size=0x%llX, cmdline=\"--%s\"", 497 desc->name, 498 (unsigned long long)image->toc_e.offset_address, 499 (unsigned long long)image->toc_e.size, 500 desc->cmdline_name); 501 #ifndef _MSC_VER /* We don't have SHA256 for Visual Studio. */ 502 if (verbose) { 503 unsigned char md[SHA256_DIGEST_LENGTH]; 504 505 SHA256(image->buffer, image->toc_e.size, md); 506 printf(", sha256="); 507 md_print(md, sizeof(md)); 508 } 509 #endif 510 putchar('\n'); 511 } 512 513 return 0; 514 } 515 516 static void info_usage(int exit_status) 517 { 518 printf("fiptool info FIP_FILENAME\n"); 519 exit(exit_status); 520 } 521 522 static int pack_images(const char *filename, uint64_t toc_flags, unsigned long align) 523 { 524 FILE *fp; 525 image_desc_t *desc; 526 fip_toc_header_t *toc_header; 527 fip_toc_entry_t *toc_entry; 528 char *buf; 529 uint64_t entry_offset, buf_size, payload_size = 0, pad_size; 530 size_t nr_images = 0; 531 532 for (desc = image_desc_head; desc != NULL; desc = desc->next) 533 if (desc->image != NULL) 534 nr_images++; 535 536 buf_size = sizeof(fip_toc_header_t) + 537 sizeof(fip_toc_entry_t) * (nr_images + 1); 538 buf = calloc(1, buf_size); 539 if (buf == NULL) 540 log_err("calloc"); 541 542 /* Build up header and ToC entries from the image table. */ 543 toc_header = (fip_toc_header_t *)buf; 544 toc_header->name = TOC_HEADER_NAME; 545 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER; 546 toc_header->flags = toc_flags; 547 548 toc_entry = (fip_toc_entry_t *)(toc_header + 1); 549 550 entry_offset = buf_size; 551 for (desc = image_desc_head; desc != NULL; desc = desc->next) { 552 image_t *image = desc->image; 553 554 if (image == NULL || (image->toc_e.size == 0ULL)) 555 continue; 556 payload_size += image->toc_e.size; 557 entry_offset = (entry_offset + align - 1) & ~(align - 1); 558 image->toc_e.offset_address = entry_offset; 559 *toc_entry++ = image->toc_e; 560 entry_offset += image->toc_e.size; 561 } 562 563 /* 564 * Append a null uuid entry to mark the end of ToC entries. 565 * NOTE the offset address for the last toc_entry must match the fip 566 * size. 567 */ 568 memset(toc_entry, 0, sizeof(*toc_entry)); 569 toc_entry->offset_address = (entry_offset + align - 1) & ~(align - 1); 570 571 /* Generate the FIP file. */ 572 fp = fopen(filename, "wb"); 573 if (fp == NULL) 574 log_err("fopen %s", filename); 575 576 if (verbose) 577 log_dbgx("Metadata size: %zu bytes", buf_size); 578 579 xfwrite(buf, buf_size, fp, filename); 580 581 if (verbose) 582 log_dbgx("Payload size: %zu bytes", payload_size); 583 584 for (desc = image_desc_head; desc != NULL; desc = desc->next) { 585 image_t *image = desc->image; 586 587 if (image == NULL) 588 continue; 589 if (fseek(fp, image->toc_e.offset_address, SEEK_SET)) 590 log_errx("Failed to set file position"); 591 592 xfwrite(image->buffer, image->toc_e.size, fp, filename); 593 } 594 595 if (fseek(fp, entry_offset, SEEK_SET)) 596 log_errx("Failed to set file position"); 597 598 pad_size = toc_entry->offset_address - entry_offset; 599 while (pad_size--) 600 fputc(0x0, fp); 601 602 free(buf); 603 fclose(fp); 604 return 0; 605 } 606 607 /* 608 * This function is shared between the create and update subcommands. 609 * The difference between the two subcommands is that when the FIP file 610 * is created, the parsing of an existing FIP is skipped. This results 611 * in update_fip() creating the new FIP file from scratch because the 612 * internal image table is not populated. 613 */ 614 static void update_fip(void) 615 { 616 image_desc_t *desc; 617 618 /* Add or replace images in the FIP file. */ 619 for (desc = image_desc_head; desc != NULL; desc = desc->next) { 620 image_t *image; 621 622 if (desc->action != DO_PACK) 623 continue; 624 625 image = read_image_from_file(&desc->uuid, 626 desc->action_arg); 627 if (desc->image != NULL) { 628 if (verbose) { 629 log_dbgx("Replacing %s with %s", 630 desc->cmdline_name, 631 desc->action_arg); 632 } 633 free(desc->image); 634 desc->image = image; 635 } else { 636 if (verbose) 637 log_dbgx("Adding image %s", 638 desc->action_arg); 639 desc->image = image; 640 } 641 } 642 } 643 644 static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags) 645 { 646 unsigned long long flags; 647 char *endptr; 648 649 errno = 0; 650 flags = strtoull(arg, &endptr, 16); 651 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0) 652 log_errx("Invalid platform ToC flags: %s", arg); 653 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */ 654 *toc_flags |= flags << 32; 655 } 656 657 static int is_power_of_2(unsigned long x) 658 { 659 return x && !(x & (x - 1)); 660 } 661 662 static unsigned long get_image_align(char *arg) 663 { 664 char *endptr; 665 unsigned long align; 666 667 errno = 0; 668 align = strtoul(arg, &endptr, 0); 669 if (*endptr != '\0' || !is_power_of_2(align) || errno != 0) 670 log_errx("Invalid alignment: %s", arg); 671 672 return align; 673 } 674 675 static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len) 676 { 677 char *p; 678 679 for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) { 680 if (strncmp(p, "uuid=", strlen("uuid=")) == 0) { 681 p += strlen("uuid="); 682 uuid_from_str(uuid, p); 683 } else if (strncmp(p, "file=", strlen("file=")) == 0) { 684 p += strlen("file="); 685 snprintf(filename, len, "%s", p); 686 } 687 } 688 } 689 690 static int create_cmd(int argc, char *argv[]) 691 { 692 struct option *opts = NULL; 693 size_t nr_opts = 0; 694 unsigned long long toc_flags = 0; 695 unsigned long align = 1; 696 697 if (argc < 2) 698 create_usage(EXIT_FAILURE); 699 700 opts = fill_common_opts(opts, &nr_opts, required_argument); 701 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument, 702 OPT_PLAT_TOC_FLAGS); 703 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN); 704 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b'); 705 opts = add_opt(opts, &nr_opts, NULL, 0, 0); 706 707 while (1) { 708 int c, opt_index = 0; 709 710 c = getopt_long(argc, argv, "b:", opts, &opt_index); 711 if (c == -1) 712 break; 713 714 switch (c) { 715 case OPT_TOC_ENTRY: { 716 image_desc_t *desc; 717 718 desc = lookup_image_desc_from_opt(opts[opt_index].name); 719 set_image_desc_action(desc, DO_PACK, optarg); 720 break; 721 } 722 case OPT_PLAT_TOC_FLAGS: 723 parse_plat_toc_flags(optarg, &toc_flags); 724 break; 725 case OPT_ALIGN: 726 align = get_image_align(optarg); 727 break; 728 case 'b': { 729 char name[_UUID_STR_LEN + 1]; 730 char filename[PATH_MAX] = { 0 }; 731 uuid_t uuid = uuid_null; 732 image_desc_t *desc; 733 734 parse_blob_opt(optarg, &uuid, 735 filename, sizeof(filename)); 736 737 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 || 738 filename[0] == '\0') 739 create_usage(EXIT_FAILURE); 740 741 desc = lookup_image_desc_from_uuid(&uuid); 742 if (desc == NULL) { 743 uuid_to_str(name, sizeof(name), &uuid); 744 desc = new_image_desc(&uuid, name, "blob"); 745 add_image_desc(desc); 746 } 747 set_image_desc_action(desc, DO_PACK, filename); 748 break; 749 } 750 default: 751 create_usage(EXIT_FAILURE); 752 } 753 } 754 argc -= optind; 755 argv += optind; 756 free(opts); 757 758 if (argc == 0) 759 create_usage(EXIT_SUCCESS); 760 761 update_fip(); 762 763 pack_images(argv[0], toc_flags, align); 764 return 0; 765 } 766 767 static void create_usage(int exit_status) 768 { 769 toc_entry_t *toc_entry = toc_entries; 770 771 printf("fiptool create [opts] FIP_FILENAME\n"); 772 printf("\n"); 773 printf("Options:\n"); 774 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n"); 775 printf(" --blob uuid=...,file=...\tAdd an image with the given UUID pointed to by file.\n"); 776 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field occupying bits 32-47 in 64-bit ToC header.\n"); 777 printf("\n"); 778 printf("Specific images are packed with the following options:\n"); 779 for (; toc_entry->cmdline_name != NULL; toc_entry++) 780 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, 781 toc_entry->name); 782 #ifdef PLAT_DEF_FIP_UUID 783 toc_entry = plat_def_toc_entries; 784 for (; toc_entry->cmdline_name != NULL; toc_entry++) 785 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, 786 toc_entry->name); 787 #endif 788 exit(exit_status); 789 } 790 791 static int update_cmd(int argc, char *argv[]) 792 { 793 struct option *opts = NULL; 794 size_t nr_opts = 0; 795 char outfile[PATH_MAX] = { 0 }; 796 fip_toc_header_t toc_header = { 0 }; 797 unsigned long long toc_flags = 0; 798 unsigned long align = 1; 799 int pflag = 0; 800 801 if (argc < 2) 802 update_usage(EXIT_FAILURE); 803 804 opts = fill_common_opts(opts, &nr_opts, required_argument); 805 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN); 806 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b'); 807 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o'); 808 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument, 809 OPT_PLAT_TOC_FLAGS); 810 opts = add_opt(opts, &nr_opts, NULL, 0, 0); 811 812 while (1) { 813 int c, opt_index = 0; 814 815 c = getopt_long(argc, argv, "b:o:", opts, &opt_index); 816 if (c == -1) 817 break; 818 819 switch (c) { 820 case OPT_TOC_ENTRY: { 821 image_desc_t *desc; 822 823 desc = lookup_image_desc_from_opt(opts[opt_index].name); 824 set_image_desc_action(desc, DO_PACK, optarg); 825 break; 826 } 827 case OPT_PLAT_TOC_FLAGS: 828 parse_plat_toc_flags(optarg, &toc_flags); 829 pflag = 1; 830 break; 831 case 'b': { 832 char name[_UUID_STR_LEN + 1]; 833 char filename[PATH_MAX] = { 0 }; 834 uuid_t uuid = uuid_null; 835 image_desc_t *desc; 836 837 parse_blob_opt(optarg, &uuid, 838 filename, sizeof(filename)); 839 840 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 || 841 filename[0] == '\0') 842 update_usage(EXIT_FAILURE); 843 844 desc = lookup_image_desc_from_uuid(&uuid); 845 if (desc == NULL) { 846 uuid_to_str(name, sizeof(name), &uuid); 847 desc = new_image_desc(&uuid, name, "blob"); 848 add_image_desc(desc); 849 } 850 set_image_desc_action(desc, DO_PACK, filename); 851 break; 852 } 853 case OPT_ALIGN: 854 align = get_image_align(optarg); 855 break; 856 case 'o': 857 snprintf(outfile, sizeof(outfile), "%s", optarg); 858 break; 859 default: 860 update_usage(EXIT_FAILURE); 861 } 862 } 863 argc -= optind; 864 argv += optind; 865 free(opts); 866 867 if (argc == 0) 868 update_usage(EXIT_SUCCESS); 869 870 if (outfile[0] == '\0') 871 snprintf(outfile, sizeof(outfile), "%s", argv[0]); 872 873 if (access(argv[0], F_OK) == 0) 874 parse_fip(argv[0], &toc_header); 875 876 if (pflag) 877 toc_header.flags &= ~(0xffffULL << 32); 878 toc_flags = (toc_header.flags |= toc_flags); 879 880 update_fip(); 881 882 pack_images(outfile, toc_flags, align); 883 return 0; 884 } 885 886 static void update_usage(int exit_status) 887 { 888 toc_entry_t *toc_entry = toc_entries; 889 890 printf("fiptool update [opts] FIP_FILENAME\n"); 891 printf("\n"); 892 printf("Options:\n"); 893 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n"); 894 printf(" --blob uuid=...,file=...\tAdd or update an image with the given UUID pointed to by file.\n"); 895 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n"); 896 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field occupying bits 32-47 in 64-bit ToC header.\n"); 897 printf("\n"); 898 printf("Specific images are packed with the following options:\n"); 899 for (; toc_entry->cmdline_name != NULL; toc_entry++) 900 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, 901 toc_entry->name); 902 #ifdef PLAT_DEF_FIP_UUID 903 toc_entry = plat_def_toc_entries; 904 for (; toc_entry->cmdline_name != NULL; toc_entry++) 905 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, 906 toc_entry->name); 907 #endif 908 exit(exit_status); 909 } 910 911 static int unpack_cmd(int argc, char *argv[]) 912 { 913 struct option *opts = NULL; 914 size_t nr_opts = 0; 915 char outdir[PATH_MAX] = { 0 }; 916 image_desc_t *desc; 917 int fflag = 0; 918 int unpack_all = 1; 919 920 if (argc < 2) 921 unpack_usage(EXIT_FAILURE); 922 923 opts = fill_common_opts(opts, &nr_opts, required_argument); 924 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b'); 925 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f'); 926 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o'); 927 opts = add_opt(opts, &nr_opts, NULL, 0, 0); 928 929 while (1) { 930 int c, opt_index = 0; 931 932 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index); 933 if (c == -1) 934 break; 935 936 switch (c) { 937 case OPT_TOC_ENTRY: { 938 image_desc_t *desc; 939 940 desc = lookup_image_desc_from_opt(opts[opt_index].name); 941 set_image_desc_action(desc, DO_UNPACK, optarg); 942 unpack_all = 0; 943 break; 944 } 945 case 'b': { 946 char name[_UUID_STR_LEN + 1]; 947 char filename[PATH_MAX] = { 0 }; 948 uuid_t uuid = uuid_null; 949 image_desc_t *desc; 950 951 parse_blob_opt(optarg, &uuid, 952 filename, sizeof(filename)); 953 954 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 || 955 filename[0] == '\0') 956 unpack_usage(EXIT_FAILURE); 957 958 desc = lookup_image_desc_from_uuid(&uuid); 959 if (desc == NULL) { 960 uuid_to_str(name, sizeof(name), &uuid); 961 desc = new_image_desc(&uuid, name, "blob"); 962 add_image_desc(desc); 963 } 964 set_image_desc_action(desc, DO_UNPACK, filename); 965 unpack_all = 0; 966 break; 967 } 968 case 'f': 969 fflag = 1; 970 break; 971 case 'o': 972 snprintf(outdir, sizeof(outdir), "%s", optarg); 973 break; 974 default: 975 unpack_usage(EXIT_FAILURE); 976 } 977 } 978 argc -= optind; 979 argv += optind; 980 free(opts); 981 982 if (argc == 0) 983 unpack_usage(EXIT_SUCCESS); 984 985 parse_fip(argv[0], NULL); 986 987 if (outdir[0] != '\0') 988 if (chdir(outdir) == -1) 989 log_err("chdir %s", outdir); 990 991 /* Unpack all specified images. */ 992 for (desc = image_desc_head; desc != NULL; desc = desc->next) { 993 char file[PATH_MAX]; 994 image_t *image = desc->image; 995 996 if (!unpack_all && desc->action != DO_UNPACK) 997 continue; 998 999 /* Build filename. */ 1000 if (desc->action_arg == NULL) 1001 snprintf(file, sizeof(file), "%s.bin", 1002 desc->cmdline_name); 1003 else 1004 snprintf(file, sizeof(file), "%s", 1005 desc->action_arg); 1006 1007 if (image == NULL) { 1008 if (!unpack_all) 1009 log_warnx("%s does not exist in %s", 1010 file, argv[0]); 1011 continue; 1012 } 1013 1014 if (access(file, F_OK) != 0 || fflag) { 1015 if (verbose) 1016 log_dbgx("Unpacking %s", file); 1017 write_image_to_file(image, file); 1018 } else { 1019 log_warnx("File %s already exists, use --force to overwrite it", 1020 file); 1021 } 1022 } 1023 1024 return 0; 1025 } 1026 1027 static void unpack_usage(int exit_status) 1028 { 1029 toc_entry_t *toc_entry = toc_entries; 1030 1031 printf("fiptool unpack [opts] FIP_FILENAME\n"); 1032 printf("\n"); 1033 printf("Options:\n"); 1034 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID to file.\n"); 1035 printf(" --force\t\t\tIf the output file already exists, use --force to overwrite it.\n"); 1036 printf(" --out path\t\t\tSet the output directory path.\n"); 1037 printf("\n"); 1038 printf("Specific images are unpacked with the following options:\n"); 1039 for (; toc_entry->cmdline_name != NULL; toc_entry++) 1040 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, 1041 toc_entry->name); 1042 #ifdef PLAT_DEF_FIP_UUID 1043 toc_entry = plat_def_toc_entries; 1044 for (; toc_entry->cmdline_name != NULL; toc_entry++) 1045 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name, 1046 toc_entry->name); 1047 #endif 1048 printf("\n"); 1049 printf("If no options are provided, all images will be unpacked.\n"); 1050 exit(exit_status); 1051 } 1052 1053 static int remove_cmd(int argc, char *argv[]) 1054 { 1055 struct option *opts = NULL; 1056 size_t nr_opts = 0; 1057 char outfile[PATH_MAX] = { 0 }; 1058 fip_toc_header_t toc_header; 1059 image_desc_t *desc; 1060 unsigned long align = 1; 1061 int fflag = 0; 1062 1063 if (argc < 2) 1064 remove_usage(EXIT_FAILURE); 1065 1066 opts = fill_common_opts(opts, &nr_opts, no_argument); 1067 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN); 1068 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b'); 1069 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f'); 1070 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o'); 1071 opts = add_opt(opts, &nr_opts, NULL, 0, 0); 1072 1073 while (1) { 1074 int c, opt_index = 0; 1075 1076 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index); 1077 if (c == -1) 1078 break; 1079 1080 switch (c) { 1081 case OPT_TOC_ENTRY: { 1082 image_desc_t *desc; 1083 1084 desc = lookup_image_desc_from_opt(opts[opt_index].name); 1085 set_image_desc_action(desc, DO_REMOVE, NULL); 1086 break; 1087 } 1088 case OPT_ALIGN: 1089 align = get_image_align(optarg); 1090 break; 1091 case 'b': { 1092 char name[_UUID_STR_LEN + 1], filename[PATH_MAX]; 1093 uuid_t uuid = uuid_null; 1094 image_desc_t *desc; 1095 1096 parse_blob_opt(optarg, &uuid, 1097 filename, sizeof(filename)); 1098 1099 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0) 1100 remove_usage(EXIT_FAILURE); 1101 1102 desc = lookup_image_desc_from_uuid(&uuid); 1103 if (desc == NULL) { 1104 uuid_to_str(name, sizeof(name), &uuid); 1105 desc = new_image_desc(&uuid, name, "blob"); 1106 add_image_desc(desc); 1107 } 1108 set_image_desc_action(desc, DO_REMOVE, NULL); 1109 break; 1110 } 1111 case 'f': 1112 fflag = 1; 1113 break; 1114 case 'o': 1115 snprintf(outfile, sizeof(outfile), "%s", optarg); 1116 break; 1117 default: 1118 remove_usage(EXIT_FAILURE); 1119 } 1120 } 1121 argc -= optind; 1122 argv += optind; 1123 free(opts); 1124 1125 if (argc == 0) 1126 remove_usage(EXIT_SUCCESS); 1127 1128 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag) 1129 log_errx("File %s already exists, use --force to overwrite it", 1130 outfile); 1131 1132 if (outfile[0] == '\0') 1133 snprintf(outfile, sizeof(outfile), "%s", argv[0]); 1134 1135 parse_fip(argv[0], &toc_header); 1136 1137 for (desc = image_desc_head; desc != NULL; desc = desc->next) { 1138 if (desc->action != DO_REMOVE) 1139 continue; 1140 1141 if (desc->image != NULL) { 1142 if (verbose) 1143 log_dbgx("Removing %s", 1144 desc->cmdline_name); 1145 free(desc->image); 1146 desc->image = NULL; 1147 } else { 1148 log_warnx("%s does not exist in %s", 1149 desc->cmdline_name, argv[0]); 1150 } 1151 } 1152 1153 pack_images(outfile, toc_header.flags, align); 1154 return 0; 1155 } 1156 1157 static void remove_usage(int exit_status) 1158 { 1159 toc_entry_t *toc_entry = toc_entries; 1160 1161 printf("fiptool remove [opts] FIP_FILENAME\n"); 1162 printf("\n"); 1163 printf("Options:\n"); 1164 printf(" --align <value>\tEach image is aligned to <value> (default: 1).\n"); 1165 printf(" --blob uuid=...\tRemove an image with the given UUID.\n"); 1166 printf(" --force\t\tIf the output FIP file already exists, use --force to overwrite it.\n"); 1167 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n"); 1168 printf("\n"); 1169 printf("Specific images are removed with the following options:\n"); 1170 for (; toc_entry->cmdline_name != NULL; toc_entry++) 1171 printf(" --%-16s\t%s\n", toc_entry->cmdline_name, 1172 toc_entry->name); 1173 #ifdef PLAT_DEF_FIP_UUID 1174 toc_entry = plat_def_toc_entries; 1175 for (; toc_entry->cmdline_name != NULL; toc_entry++) 1176 printf(" --%-16s\t%s\n", toc_entry->cmdline_name, 1177 toc_entry->name); 1178 #endif 1179 exit(exit_status); 1180 } 1181 1182 static int version_cmd(int argc, char *argv[]) 1183 { 1184 #ifdef VERSION 1185 puts(VERSION); 1186 #else 1187 /* If built from fiptool directory, VERSION is not set. */ 1188 puts("Unknown version"); 1189 #endif 1190 return 0; 1191 } 1192 1193 static void version_usage(int exit_status) 1194 { 1195 printf("fiptool version\n"); 1196 exit(exit_status); 1197 } 1198 1199 static int help_cmd(int argc, char *argv[]) 1200 { 1201 int i; 1202 1203 if (argc < 2) 1204 usage(); 1205 argc--, argv++; 1206 1207 for (i = 0; i < NELEM(cmds); i++) { 1208 if (strcmp(cmds[i].name, argv[0]) == 0 && 1209 cmds[i].usage != NULL) 1210 cmds[i].usage(EXIT_SUCCESS); 1211 } 1212 if (i == NELEM(cmds)) 1213 printf("No help for subcommand '%s'\n", argv[0]); 1214 return 0; 1215 } 1216 1217 static void usage(void) 1218 { 1219 printf("usage: fiptool [--verbose] <command> [<args>]\n"); 1220 printf("Global options supported:\n"); 1221 printf(" --verbose\tEnable verbose output for all commands.\n"); 1222 printf("\n"); 1223 printf("Commands supported:\n"); 1224 printf(" info\t\tList images contained in FIP.\n"); 1225 printf(" create\tCreate a new FIP with the given images.\n"); 1226 printf(" update\tUpdate an existing FIP with the given images.\n"); 1227 printf(" unpack\tUnpack images from FIP.\n"); 1228 printf(" remove\tRemove images from FIP.\n"); 1229 printf(" version\tShow fiptool version.\n"); 1230 printf(" help\t\tShow help for given command.\n"); 1231 exit(EXIT_SUCCESS); 1232 } 1233 1234 int main(int argc, char *argv[]) 1235 { 1236 int i, ret = 0; 1237 1238 while (1) { 1239 int c, opt_index = 0; 1240 static struct option opts[] = { 1241 { "verbose", no_argument, NULL, 'v' }, 1242 { NULL, no_argument, NULL, 0 } 1243 }; 1244 1245 /* 1246 * Set POSIX mode so getopt stops at the first non-option 1247 * which is the subcommand. 1248 */ 1249 c = getopt_long(argc, argv, "+v", opts, &opt_index); 1250 if (c == -1) 1251 break; 1252 1253 switch (c) { 1254 case 'v': 1255 verbose = 1; 1256 break; 1257 default: 1258 usage(); 1259 } 1260 } 1261 argc -= optind; 1262 argv += optind; 1263 /* Reset optind for subsequent getopt processing. */ 1264 optind = 0; 1265 1266 if (argc == 0) 1267 usage(); 1268 1269 fill_image_descs(); 1270 for (i = 0; i < NELEM(cmds); i++) { 1271 if (strcmp(cmds[i].name, argv[0]) == 0) { 1272 ret = cmds[i].handler(argc, argv); 1273 break; 1274 } 1275 } 1276 if (i == NELEM(cmds)) 1277 usage(); 1278 free_image_descs(); 1279 return ret; 1280 } 1281