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