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