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