xref: /rk3399_ARM-atf/tools/fiptool/fiptool.c (revision 7941816a3428f015a9f4a6c5dc978b4fca568f4b)
1 /*
2  * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of ARM nor the names of its contributors may be used
15  * to endorse or promote products derived from this software without specific
16  * prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 
34 #include <assert.h>
35 #include <errno.h>
36 #include <getopt.h>
37 #include <limits.h>
38 #include <stdarg.h>
39 #include <stdint.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 #include <openssl/sha.h>
46 
47 #include "fiptool.h"
48 #include "firmware_image_package.h"
49 #include "tbbr_config.h"
50 
51 #define OPT_TOC_ENTRY 0
52 #define OPT_PLAT_TOC_FLAGS 1
53 
54 static int info_cmd(int argc, char *argv[]);
55 static void info_usage(void);
56 static int create_cmd(int argc, char *argv[]);
57 static void create_usage(void);
58 static int update_cmd(int argc, char *argv[]);
59 static void update_usage(void);
60 static int unpack_cmd(int argc, char *argv[]);
61 static void unpack_usage(void);
62 static int remove_cmd(int argc, char *argv[]);
63 static void remove_usage(void);
64 static int version_cmd(int argc, char *argv[]);
65 static void version_usage(void);
66 static int help_cmd(int argc, char *argv[]);
67 static void usage(void);
68 
69 /* Available subcommands. */
70 static cmd_t cmds[] = {
71 	{ .name = "info",    .handler = info_cmd,    .usage = info_usage    },
72 	{ .name = "create",  .handler = create_cmd,  .usage = create_usage  },
73 	{ .name = "update",  .handler = update_cmd,  .usage = update_usage  },
74 	{ .name = "unpack",  .handler = unpack_cmd,  .usage = unpack_usage  },
75 	{ .name = "remove",  .handler = remove_cmd,  .usage = remove_usage  },
76 	{ .name = "version", .handler = version_cmd, .usage = version_usage },
77 	{ .name = "help",    .handler = help_cmd,    .usage = NULL          },
78 };
79 
80 static image_t *images[MAX_IMAGES];
81 static size_t nr_images;
82 static uuid_t uuid_null = { 0 };
83 static int verbose;
84 
85 static void vlog(int prio, const char *msg, va_list ap)
86 {
87 	char *prefix[] = { "DEBUG", "WARN", "ERROR" };
88 
89 	fprintf(stderr, "%s: ", prefix[prio]);
90 	vfprintf(stderr, msg, ap);
91 	fputc('\n', stderr);
92 }
93 
94 static void log_dbgx(const char *msg, ...)
95 {
96 	va_list ap;
97 
98 	va_start(ap, msg);
99 	vlog(LOG_DBG, msg, ap);
100 	va_end(ap);
101 }
102 
103 static void log_warnx(const char *msg, ...)
104 {
105 	va_list ap;
106 
107 	va_start(ap, msg);
108 	vlog(LOG_WARN, msg, ap);
109 	va_end(ap);
110 }
111 
112 static void log_err(const char *msg, ...)
113 {
114 	char buf[512];
115 	va_list ap;
116 
117 	va_start(ap, msg);
118 	snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
119 	vlog(LOG_ERR, buf, ap);
120 	va_end(ap);
121 	exit(1);
122 }
123 
124 static void log_errx(const char *msg, ...)
125 {
126 	va_list ap;
127 
128 	va_start(ap, msg);
129 	vlog(LOG_ERR, msg, ap);
130 	va_end(ap);
131 	exit(1);
132 }
133 
134 static char *xstrdup(const char *s, const char *msg)
135 {
136 	char *d;
137 
138 	d = strdup(s);
139 	if (d == NULL)
140 		log_errx("strdup: ", msg);
141 	return d;
142 }
143 
144 static void *xmalloc(size_t size, const char *msg)
145 {
146 	void *d;
147 
148 	d = malloc(size);
149 	if (d == NULL)
150 		log_errx("malloc: ", msg);
151 	return d;
152 }
153 
154 static void add_image(image_t *image)
155 {
156 	if (nr_images + 1 > MAX_IMAGES)
157 		log_errx("Too many images");
158 	images[nr_images++] = image;
159 }
160 
161 static void free_image(image_t *image)
162 {
163 	free(image->buffer);
164 	free(image);
165 }
166 
167 static void replace_image(image_t *image_dst, image_t *image_src)
168 {
169 	int i;
170 
171 	for (i = 0; i < nr_images; i++) {
172 		if (images[i] == image_dst) {
173 			free_image(images[i]);
174 			images[i] = image_src;
175 			break;
176 		}
177 	}
178 	assert(i != nr_images);
179 }
180 
181 static void remove_image(image_t *image)
182 {
183 	int i;
184 
185 	for (i = 0; i < nr_images; i++) {
186 		if (images[i] == image) {
187 			free_image(images[i]);
188 			images[i] = NULL;
189 			break;
190 		}
191 	}
192 	assert(i != nr_images);
193 
194 	/* Compact array. */
195 	memmove(&images[i], &images[i + 1],
196 	    (nr_images - i - 1) * sizeof(*images));
197 	nr_images--;
198 }
199 
200 static void free_images(void)
201 {
202 	int i;
203 
204 	for (i = 0; i < nr_images; i++) {
205 		free_image(images[i]);
206 		images[i] = NULL;
207 	}
208 }
209 
210 static toc_entry_t *lookup_entry_from_uuid(uuid_t *uuid)
211 {
212 	toc_entry_t *toc_entry = toc_entries;
213 
214 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
215 		if (memcmp(&toc_entry->uuid, uuid, sizeof(uuid_t)) == 0)
216 			return toc_entry;
217 	return NULL;
218 }
219 
220 static image_t *lookup_image_from_uuid(uuid_t *uuid)
221 {
222 	image_t *image;
223 	int i;
224 
225 	for (i = 0; i < nr_images; i++) {
226 		image = images[i];
227 		if (memcmp(&image->uuid, uuid, sizeof(uuid_t)) == 0)
228 			return image;
229 	}
230 	return NULL;
231 }
232 
233 static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
234 {
235 	struct stat st;
236 	FILE *fp;
237 	char *buf, *bufend;
238 	fip_toc_header_t *toc_header;
239 	fip_toc_entry_t *toc_entry;
240 	image_t *image;
241 	int terminated = 0;
242 
243 	fp = fopen(filename, "r");
244 	if (fp == NULL)
245 		log_err("fopen %s", filename);
246 
247 	if (fstat(fileno(fp), &st) == -1)
248 		log_err("fstat %s", filename);
249 
250 	buf = xmalloc(st.st_size, "failed to load file into memory");
251 	if (fread(buf, 1, st.st_size, fp) != st.st_size)
252 		log_errx("Failed to read %s", filename);
253 	bufend = buf + st.st_size;
254 	fclose(fp);
255 
256 	if (st.st_size < sizeof(fip_toc_header_t))
257 		log_errx("FIP %s is truncated", filename);
258 
259 	toc_header = (fip_toc_header_t *)buf;
260 	toc_entry = (fip_toc_entry_t *)(toc_header + 1);
261 
262 	if (toc_header->name != TOC_HEADER_NAME)
263 		log_errx("%s is not a FIP file", filename);
264 
265 	/* Return the ToC header if the caller wants it. */
266 	if (toc_header_out != NULL)
267 		*toc_header_out = *toc_header;
268 
269 	/* Walk through each ToC entry in the file. */
270 	while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
271 		/* Found the ToC terminator, we are done. */
272 		if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
273 			terminated = 1;
274 			break;
275 		}
276 
277 		/*
278 		 * Build a new image out of the ToC entry and add it to the
279 		 * table of images.
280 		 */
281 		image = xmalloc(sizeof(*image),
282 		    "failed to allocate memory for image");
283 		memcpy(&image->uuid, &toc_entry->uuid, sizeof(uuid_t));
284 		image->buffer = xmalloc(toc_entry->size,
285 		    "failed to allocate image buffer, is FIP file corrupted?");
286 		/* Overflow checks before memory copy. */
287 		if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
288 			log_errx("FIP %s is corrupted", filename);
289 		if (toc_entry->size + toc_entry->offset_address > st.st_size)
290 			log_errx("FIP %s is corrupted", filename);
291 
292 		memcpy(image->buffer, buf + toc_entry->offset_address,
293 		    toc_entry->size);
294 		image->size = toc_entry->size;
295 
296 		add_image(image);
297 
298 		toc_entry++;
299 	}
300 
301 	if (terminated == 0)
302 		log_errx("FIP %s does not have a ToC terminator entry",
303 		    filename);
304 	free(buf);
305 	return 0;
306 }
307 
308 static image_t *read_image_from_file(const uuid_t *uuid, const char *filename)
309 {
310 	struct stat st;
311 	image_t *image;
312 	FILE *fp;
313 
314 	assert(uuid != NULL);
315 
316 	fp = fopen(filename, "r");
317 	if (fp == NULL)
318 		log_err("fopen %s", filename);
319 
320 	if (fstat(fileno(fp), &st) == -1)
321 		log_errx("fstat %s", filename);
322 
323 	image = xmalloc(sizeof(*image), "failed to allocate memory for image");
324 	memcpy(&image->uuid, uuid, sizeof(uuid_t));
325 	image->buffer = xmalloc(st.st_size, "failed to allocate image buffer");
326 	if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
327 		log_errx("Failed to read %s", filename);
328 	image->size = st.st_size;
329 
330 	fclose(fp);
331 	return image;
332 }
333 
334 static int write_image_to_file(const image_t *image, const char *filename)
335 {
336 	FILE *fp;
337 
338 	fp = fopen(filename, "w");
339 	if (fp == NULL)
340 		log_err("fopen");
341 	if (fwrite(image->buffer, 1, image->size, fp) != image->size)
342 		log_errx("Failed to write %s", filename);
343 	fclose(fp);
344 	return 0;
345 }
346 
347 static int fill_common_opts(struct option *opts, int has_arg)
348 {
349 	int i;
350 
351 	for (i = 0; toc_entries[i].cmdline_name != NULL; i++) {
352 		opts[i].name = toc_entries[i].cmdline_name;
353 		opts[i].has_arg = has_arg;
354 		opts[i].flag = NULL;
355 		opts[i].val = 0;
356 	}
357 	return i;
358 }
359 
360 static void add_opt(struct option *opts, int idx, char *name,
361     int has_arg, int val)
362 {
363 	opts[idx].name = name;
364 	opts[idx].has_arg = has_arg;
365 	opts[idx].flag = NULL;
366 	opts[idx].val = val;
367 }
368 
369 static void md_print(unsigned char *md, size_t len)
370 {
371 	size_t i;
372 
373 	for (i = 0; i < len; i++)
374 		printf("%02x", md[i]);
375 }
376 
377 static int info_cmd(int argc, char *argv[])
378 {
379 	image_t *image;
380 	uint64_t image_offset;
381 	uint64_t image_size = 0;
382 	fip_toc_header_t toc_header;
383 	int i;
384 
385 	if (argc != 2)
386 		info_usage();
387 	argc--, argv++;
388 
389 	parse_fip(argv[0], &toc_header);
390 
391 	if (verbose) {
392 		log_dbgx("toc_header[name]: 0x%llX",
393 		    (unsigned long long)toc_header.name);
394 		log_dbgx("toc_header[serial_number]: 0x%llX",
395 		    (unsigned long long)toc_header.serial_number);
396 		log_dbgx("toc_header[flags]: 0x%llX",
397 		    (unsigned long long)toc_header.flags);
398 	}
399 
400 	image_offset = sizeof(fip_toc_header_t) +
401 	    (sizeof(fip_toc_entry_t) * (nr_images + 1));
402 
403 	for (i = 0; i < nr_images; i++) {
404 		toc_entry_t *toc_entry;
405 
406 		image = images[i];
407 		toc_entry = lookup_entry_from_uuid(&image->uuid);
408 		if (toc_entry != NULL)
409 			printf("%s: ", toc_entry->name);
410 		else
411 			printf("Unknown entry: ");
412 		image_size = image->size;
413 		printf("offset=0x%llX, size=0x%llX",
414 		    (unsigned long long)image_offset,
415 		    (unsigned long long)image_size);
416 		if (toc_entry != NULL)
417 			printf(", cmdline=\"--%s\"",
418 			    toc_entry->cmdline_name);
419 		if (verbose) {
420 			unsigned char md[SHA256_DIGEST_LENGTH];
421 
422 			SHA256(image->buffer, image_size, md);
423 			printf(", sha256=");
424 			md_print(md, sizeof(md));
425 		}
426 		putchar('\n');
427 		image_offset += image_size;
428 	}
429 
430 	free_images();
431 	return 0;
432 }
433 
434 static void info_usage(void)
435 {
436 	printf("fiptool info FIP_FILENAME\n");
437 	exit(1);
438 }
439 
440 static int pack_images(char *filename, uint64_t toc_flags)
441 {
442 	FILE *fp;
443 	image_t *image;
444 	fip_toc_header_t *toc_header;
445 	fip_toc_entry_t *toc_entry;
446 	char *buf;
447 	uint64_t entry_offset, buf_size, payload_size;
448 	int i;
449 
450 	/* Calculate total payload size and allocate scratch buffer. */
451 	payload_size = 0;
452 	for (i = 0; i < nr_images; i++)
453 		payload_size += images[i]->size;
454 
455 	buf_size = sizeof(fip_toc_header_t) +
456 	    sizeof(fip_toc_entry_t) * (nr_images + 1);
457 	buf = calloc(1, buf_size);
458 	if (buf == NULL)
459 		log_err("calloc");
460 
461 	/* Build up header and ToC entries from the image table. */
462 	toc_header = (fip_toc_header_t *)buf;
463 	toc_header->name = TOC_HEADER_NAME;
464 	toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
465 	toc_header->flags = toc_flags;
466 
467 	toc_entry = (fip_toc_entry_t *)(toc_header + 1);
468 
469 	entry_offset = buf_size;
470 	for (i = 0; i < nr_images; i++) {
471 		image = images[i];
472 		memcpy(&toc_entry->uuid, &image->uuid, sizeof(uuid_t));
473 		toc_entry->offset_address = entry_offset;
474 		toc_entry->size = image->size;
475 		toc_entry->flags = 0;
476 		entry_offset += toc_entry->size;
477 		toc_entry++;
478 	}
479 
480 	/* Append a null uuid entry to mark the end of ToC entries. */
481 	memcpy(&toc_entry->uuid, &uuid_null, sizeof(uuid_t));
482 	toc_entry->offset_address = entry_offset;
483 	toc_entry->size = 0;
484 	toc_entry->flags = 0;
485 
486 	/* Generate the FIP file. */
487 	fp = fopen(filename, "w");
488 	if (fp == NULL)
489 		log_err("fopen %s", filename);
490 
491 	if (verbose)
492 		log_dbgx("Metadata size: %zu bytes", buf_size);
493 
494 	if (fwrite(buf, 1, buf_size, fp) != buf_size)
495 		log_errx("Failed to write image to %s", filename);
496 	free(buf);
497 
498 	if (verbose)
499 		log_dbgx("Payload size: %zu bytes", payload_size);
500 
501 	for (i = 0; i < nr_images; i++) {
502 		image = images[i];
503 		if (fwrite(image->buffer, 1, image->size, fp) != image->size)
504 			log_errx("Failed to write image to %s", filename);
505 	}
506 
507 	fclose(fp);
508 	return 0;
509 }
510 
511 /*
512  * This function is shared between the create and update subcommands.
513  * The difference between the two subcommands is that when the FIP file
514  * is created, the parsing of an existing FIP is skipped.  This results
515  * in update_fip() creating the new FIP file from scratch because the
516  * internal image table is not populated.
517  */
518 static void update_fip(void)
519 {
520 	toc_entry_t *toc_entry;
521 	image_t *new_image, *old_image;
522 
523 	/* Add or replace images in the FIP file. */
524 	for (toc_entry = toc_entries;
525 	     toc_entry->cmdline_name != NULL;
526 	     toc_entry++) {
527 		if (toc_entry->action != DO_PACK)
528 			continue;
529 
530 		new_image = read_image_from_file(&toc_entry->uuid,
531 		    toc_entry->action_arg);
532 		old_image = lookup_image_from_uuid(&toc_entry->uuid);
533 		if (old_image != NULL) {
534 			if (verbose)
535 				log_dbgx("Replacing image %s.bin with %s",
536 				    toc_entry->cmdline_name,
537 				    toc_entry->action_arg);
538 			replace_image(old_image, new_image);
539 		} else {
540 			if (verbose)
541 				log_dbgx("Adding image %s",
542 				    toc_entry->action_arg);
543 			add_image(new_image);
544 		}
545 
546 		free(toc_entry->action_arg);
547 		toc_entry->action_arg = NULL;
548 	}
549 }
550 
551 static void parse_plat_toc_flags(char *arg, unsigned long long *toc_flags)
552 {
553 	unsigned long long flags;
554 	char *endptr;
555 
556 	errno = 0;
557 	flags = strtoull(arg, &endptr, 16);
558 	if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
559 		log_errx("Invalid platform ToC flags: %s", arg);
560 	/* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
561 	*toc_flags |= flags << 32;
562 }
563 
564 static int create_cmd(int argc, char *argv[])
565 {
566 	struct option opts[toc_entries_len + 1];
567 	unsigned long long toc_flags = 0;
568 	int i;
569 
570 	if (argc < 2)
571 		create_usage();
572 
573 	i = fill_common_opts(opts, required_argument);
574 	add_opt(opts, i, "plat-toc-flags", required_argument,
575 	    OPT_PLAT_TOC_FLAGS);
576 	add_opt(opts, ++i, NULL, 0, 0);
577 
578 	while (1) {
579 		int c, opt_index = 0;
580 
581 		c = getopt_long(argc, argv, "", opts, &opt_index);
582 		if (c == -1)
583 			break;
584 
585 		switch (c) {
586 		case OPT_TOC_ENTRY: {
587 			toc_entry_t *toc_entry;
588 
589 			toc_entry = &toc_entries[opt_index];
590 			toc_entry->action = DO_PACK;
591 			toc_entry->action_arg = xstrdup(optarg,
592 			    "failed to allocate memory for argument");
593 			break;
594 		}
595 		case OPT_PLAT_TOC_FLAGS:
596 			parse_plat_toc_flags(optarg, &toc_flags);
597 			break;
598 		default:
599 			create_usage();
600 		}
601 	}
602 	argc -= optind;
603 	argv += optind;
604 
605 	if (argc == 0)
606 		create_usage();
607 
608 	update_fip();
609 
610 	pack_images(argv[0], toc_flags);
611 	free_images();
612 	return 0;
613 }
614 
615 static void create_usage(void)
616 {
617 	toc_entry_t *toc_entry = toc_entries;
618 
619 	printf("fiptool create [--plat-toc-flags <value>] [opts] FIP_FILENAME\n");
620 	printf("  --plat-toc-flags <value>\t16-bit platform specific flag field "
621 	    "occupying bits 32-47 in 64-bit ToC header.\n");
622 	fputc('\n', stderr);
623 	printf("Specific images are packed with the following options:\n");
624 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
625 		printf("  --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
626 		    toc_entry->name);
627 	exit(1);
628 }
629 
630 static int update_cmd(int argc, char *argv[])
631 {
632 	struct option opts[toc_entries_len + 2];
633 	char outfile[FILENAME_MAX] = { 0 };
634 	fip_toc_header_t toc_header = { 0 };
635 	unsigned long long toc_flags = 0;
636 	int pflag = 0;
637 	int i;
638 
639 	if (argc < 2)
640 		update_usage();
641 
642 	i = fill_common_opts(opts, required_argument);
643 	add_opt(opts, i, "out", required_argument, 'o');
644 	add_opt(opts, ++i, "plat-toc-flags", required_argument,
645 	    OPT_PLAT_TOC_FLAGS);
646 	add_opt(opts, ++i, NULL, 0, 0);
647 
648 	while (1) {
649 		int c, opt_index = 0;
650 
651 		c = getopt_long(argc, argv, "o:", opts, &opt_index);
652 		if (c == -1)
653 			break;
654 
655 		switch (c) {
656 		case OPT_TOC_ENTRY: {
657 			toc_entry_t *toc_entry;
658 
659 			toc_entry = &toc_entries[opt_index];
660 			toc_entry->action = DO_PACK;
661 			toc_entry->action_arg = xstrdup(optarg,
662 			    "failed to allocate memory for argument");
663 			break;
664 		}
665 		case OPT_PLAT_TOC_FLAGS: {
666 			parse_plat_toc_flags(optarg, &toc_flags);
667 			pflag = 1;
668 			break;
669 		}
670 		case 'o':
671 			snprintf(outfile, sizeof(outfile), "%s", optarg);
672 			break;
673 		default:
674 			update_usage();
675 		}
676 	}
677 	argc -= optind;
678 	argv += optind;
679 
680 	if (argc == 0)
681 		update_usage();
682 
683 	if (outfile[0] == '\0')
684 		snprintf(outfile, sizeof(outfile), "%s", argv[0]);
685 
686 	if (access(outfile, F_OK) == 0)
687 		parse_fip(argv[0], &toc_header);
688 
689 	if (pflag)
690 		toc_header.flags &= ~(0xffffULL << 32);
691 	toc_flags = (toc_header.flags |= toc_flags);
692 
693 	update_fip();
694 
695 	pack_images(outfile, toc_flags);
696 	free_images();
697 	return 0;
698 }
699 
700 static void update_usage(void)
701 {
702 	toc_entry_t *toc_entry = toc_entries;
703 
704 	printf("fiptool update [--out FIP_FILENAME] "
705 	    "[--plat-toc-flags <value>] [opts] FIP_FILENAME\n");
706 	printf("  --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
707 	printf("  --plat-toc-flags <value>\t16-bit platform specific flag field "
708 	    "occupying bits 32-47 in 64-bit ToC header.\n");
709 	fputc('\n', stderr);
710 	printf("Specific images are packed with the following options:\n");
711 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
712 		printf("  --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
713 		    toc_entry->name);
714 	exit(1);
715 }
716 
717 static int unpack_cmd(int argc, char *argv[])
718 {
719 	struct option opts[toc_entries_len + 3];
720 	char file[FILENAME_MAX], outdir[PATH_MAX] = { 0 };
721 	toc_entry_t *toc_entry;
722 	int fflag = 0;
723 	int unpack_all = 1;
724 	int i;
725 
726 	if (argc < 2)
727 		unpack_usage();
728 
729 	i = fill_common_opts(opts, required_argument);
730 	add_opt(opts, i, "force", no_argument, 'f');
731 	add_opt(opts, ++i, "out", required_argument, 'o');
732 	add_opt(opts, ++i, NULL, 0, 0);
733 
734 	while (1) {
735 		int c, opt_index = 0;
736 
737 		c = getopt_long(argc, argv, "fo:", opts, &opt_index);
738 		if (c == -1)
739 			break;
740 
741 		switch (c) {
742 		case OPT_TOC_ENTRY:
743 			unpack_all = 0;
744 			toc_entry = &toc_entries[opt_index];
745 			toc_entry->action = DO_UNPACK;
746 			toc_entry->action_arg = xstrdup(optarg,
747 			    "failed to allocate memory for argument");
748 			break;
749 		case 'f':
750 			fflag = 1;
751 			break;
752 		case 'o':
753 			snprintf(outdir, sizeof(outdir), "%s", optarg);
754 			break;
755 		default:
756 			unpack_usage();
757 		}
758 	}
759 	argc -= optind;
760 	argv += optind;
761 
762 	if (argc == 0)
763 		unpack_usage();
764 
765 	parse_fip(argv[0], NULL);
766 
767 	if (outdir[0] != '\0')
768 		if (chdir(outdir) == -1)
769 			log_err("chdir %s", outdir);
770 
771 	/* Unpack all specified images. */
772 	for (toc_entry = toc_entries;
773 	     toc_entry->cmdline_name != NULL;
774 	     toc_entry++) {
775 		image_t *image;
776 
777 		if (!unpack_all && toc_entry->action != DO_UNPACK)
778 			continue;
779 
780 		/* Build filename. */
781 		if (toc_entry->action_arg == NULL)
782 			snprintf(file, sizeof(file), "%s.bin",
783 			    toc_entry->cmdline_name);
784 		else
785 			snprintf(file, sizeof(file), "%s",
786 			    toc_entry->action_arg);
787 
788 		image = lookup_image_from_uuid(&toc_entry->uuid);
789 		if (image == NULL) {
790 			if (!unpack_all)
791 				log_warnx("Requested image %s is not in %s",
792 				    file, argv[0]);
793 			free(toc_entry->action_arg);
794 			toc_entry->action_arg = NULL;
795 			continue;
796 		}
797 
798 		if (access(file, F_OK) != 0 || fflag) {
799 			if (verbose)
800 				log_dbgx("Unpacking %s", file);
801 			write_image_to_file(image, file);
802 		} else {
803 			log_warnx("File %s already exists, use --force to overwrite it",
804 			    file);
805 		}
806 
807 		free(toc_entry->action_arg);
808 		toc_entry->action_arg = NULL;
809 	}
810 
811 	free_images();
812 	return 0;
813 }
814 
815 static void unpack_usage(void)
816 {
817 	toc_entry_t *toc_entry = toc_entries;
818 
819 	printf("fiptool unpack [--force] [--out <path>] [opts] FIP_FILENAME\n");
820 	printf("  --force\tIf the output file already exists, use --force to "
821 	    "overwrite it.\n");
822 	printf("  --out path\tSet the output directory path.\n");
823 	fputc('\n', stderr);
824 	printf("Specific images are unpacked with the following options:\n");
825 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
826 		printf("  --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
827 		    toc_entry->name);
828 	fputc('\n', stderr);
829 	printf("If no options are provided, all images will be unpacked.\n");
830 	exit(1);
831 }
832 
833 static int remove_cmd(int argc, char *argv[])
834 {
835 	struct option opts[toc_entries_len + 2];
836 	char outfile[FILENAME_MAX] = { 0 };
837 	fip_toc_header_t toc_header;
838 	toc_entry_t *toc_entry;
839 	int fflag = 0;
840 	int i;
841 
842 	if (argc < 2)
843 		remove_usage();
844 
845 	i = fill_common_opts(opts, no_argument);
846 	add_opt(opts, i, "force", no_argument, 'f');
847 	add_opt(opts, ++i, "out", required_argument, 'o');
848 	add_opt(opts, ++i, NULL, 0, 0);
849 
850 	while (1) {
851 		int c, opt_index = 0;
852 
853 		c = getopt_long(argc, argv, "fo:", opts, &opt_index);
854 		if (c == -1)
855 			break;
856 
857 		switch (c) {
858 		case OPT_TOC_ENTRY:
859 			toc_entry = &toc_entries[opt_index];
860 			toc_entry->action = DO_REMOVE;
861 			break;
862 		case 'f':
863 			fflag = 1;
864 			break;
865 		case 'o':
866 			snprintf(outfile, sizeof(outfile), "%s", optarg);
867 			break;
868 		default:
869 			remove_usage();
870 		}
871 	}
872 	argc -= optind;
873 	argv += optind;
874 
875 	if (argc == 0)
876 		remove_usage();
877 
878 	if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
879 		log_errx("File %s already exists, use --force to overwrite it",
880 		    outfile);
881 
882 	if (outfile[0] == '\0')
883 		snprintf(outfile, sizeof(outfile), "%s", argv[0]);
884 
885 	parse_fip(argv[0], &toc_header);
886 
887 	for (toc_entry = toc_entries;
888 	     toc_entry->cmdline_name != NULL;
889 	     toc_entry++) {
890 		image_t *image;
891 
892 		if (toc_entry->action != DO_REMOVE)
893 			continue;
894 		image = lookup_image_from_uuid(&toc_entry->uuid);
895 		if (image != NULL) {
896 			if (verbose)
897 				log_dbgx("Removing %s.bin",
898 				    toc_entry->cmdline_name);
899 			remove_image(image);
900 		} else {
901 			log_warnx("Requested image %s.bin is not in %s",
902 			    toc_entry->cmdline_name, argv[0]);
903 		}
904 	}
905 
906 	pack_images(outfile, toc_header.flags);
907 	free_images();
908 	return 0;
909 }
910 
911 static void remove_usage(void)
912 {
913 	toc_entry_t *toc_entry = toc_entries;
914 
915 	printf("fiptool remove [--force] [--out FIP_FILENAME] [opts] FIP_FILENAME\n");
916 	printf("  --force\t\tIf the output FIP file already exists, use --force to "
917 	    "overwrite it.\n");
918 	printf("  --out FIP_FILENAME\tSet an alternative output FIP file.\n");
919 	fputc('\n', stderr);
920 	printf("Specific images are removed with the following options:\n");
921 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
922 		printf("  --%-16s\t%s\n", toc_entry->cmdline_name,
923 		    toc_entry->name);
924 	exit(1);
925 }
926 
927 static int version_cmd(int argc, char *argv[])
928 {
929 #ifdef VERSION
930 	puts(VERSION);
931 #else
932 	/* If built from fiptool directory, VERSION is not set. */
933 	puts("Unknown version");
934 #endif
935 	return 0;
936 }
937 
938 static void version_usage(void)
939 {
940 	printf("fiptool version\n");
941 	exit(1);
942 }
943 
944 static int help_cmd(int argc, char *argv[])
945 {
946 	int i;
947 
948 	if (argc < 2)
949 		usage();
950 	argc--, argv++;
951 
952 	for (i = 0; i < NELEM(cmds); i++) {
953 		if (strcmp(cmds[i].name, argv[0]) == 0 &&
954 		    cmds[i].usage != NULL)
955 			cmds[i].usage();
956 	}
957 	if (i == NELEM(cmds))
958 		printf("No help for subcommand '%s'\n", argv[0]);
959 	return 0;
960 }
961 
962 static void usage(void)
963 {
964 	printf("usage: [--verbose] fiptool <command> [<args>]\n");
965 	printf("Global options supported:\n");
966 	printf("  --verbose\tEnable verbose output for all commands.\n");
967 	fputc('\n', stderr);
968 	printf("Commands supported:\n");
969 	printf("  info\t\tList images contained in FIP.\n");
970 	printf("  create\tCreate a new FIP with the given images.\n");
971 	printf("  update\tUpdate an existing FIP with the given images.\n");
972 	printf("  unpack\tUnpack images from FIP.\n");
973 	printf("  remove\tRemove images from FIP.\n");
974 	printf("  version\tShow fiptool version.\n");
975 	printf("  help\t\tShow help for given command.\n");
976 	exit(1);
977 }
978 
979 int main(int argc, char *argv[])
980 {
981 	int i, ret = 0;
982 
983 	while (1) {
984 		int c, opt_index = 0;
985 		static struct option opts[] = {
986 			{ "verbose", no_argument, NULL, 'v' },
987 			{ NULL, no_argument, NULL, 0 }
988 		};
989 
990 		/*
991 		 * Set POSIX mode so getopt stops at the first non-option
992 		 * which is the subcommand.
993 		 */
994 		c = getopt_long(argc, argv, "+v", opts, &opt_index);
995 		if (c == -1)
996 			break;
997 
998 		switch (c) {
999 		case 'v':
1000 			verbose = 1;
1001 			break;
1002 		default:
1003 			usage();
1004 		}
1005 	}
1006 	argc -= optind;
1007 	argv += optind;
1008 	/* Reset optind for subsequent getopt processing. */
1009 	optind = 0;
1010 
1011 	if (argc == 0)
1012 		usage();
1013 
1014 	for (i = 0; i < NELEM(cmds); i++) {
1015 		if (strcmp(cmds[i].name, argv[0]) == 0) {
1016 			ret = cmds[i].handler(argc, argv);
1017 			break;
1018 		}
1019 	}
1020 	if (i == NELEM(cmds))
1021 		usage();
1022 	return ret;
1023 }
1024