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