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