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