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