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