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