xref: /rk3399_ARM-atf/tools/fiptool/fiptool.c (revision ccbfd01d95b9b35acb3e2ca5f25379ce8fa0ed1c)
1819281eeSdp-arm /*
2*ccbfd01dSManish V Badarkhe  * Copyright (c) 2016-2024, Arm Limited and Contributors. All rights reserved.
3819281eeSdp-arm  *
482cb2c1aSdp-arm  * SPDX-License-Identifier: BSD-3-Clause
5819281eeSdp-arm  */
6819281eeSdp-arm 
7*ccbfd01dSManish V Badarkhe #ifdef __linux__
806e69f7cSAntonio Borneo #include <sys/mount.h>
906e69f7cSAntonio Borneo #endif
10*ccbfd01dSManish V Badarkhe 
11819281eeSdp-arm #include <sys/types.h>
12819281eeSdp-arm #include <sys/stat.h>
13819281eeSdp-arm 
14819281eeSdp-arm #include <assert.h>
15819281eeSdp-arm #include <errno.h>
16819281eeSdp-arm #include <limits.h>
17819281eeSdp-arm #include <stdarg.h>
18819281eeSdp-arm #include <stdint.h>
19819281eeSdp-arm #include <stdio.h>
20819281eeSdp-arm #include <stdlib.h>
21819281eeSdp-arm #include <string.h>
222a6c1a8fSMasahiro Yamada 
23819281eeSdp-arm #include "fiptool.h"
24819281eeSdp-arm #include "tbbr_config.h"
25819281eeSdp-arm 
26819281eeSdp-arm #define OPT_TOC_ENTRY 0
27819281eeSdp-arm #define OPT_PLAT_TOC_FLAGS 1
281c75d5dfSMasahiro Yamada #define OPT_ALIGN 2
29819281eeSdp-arm 
30819281eeSdp-arm static int info_cmd(int argc, char *argv[]);
314e500525SLeonardo Sandoval static void info_usage(int);
32819281eeSdp-arm static int create_cmd(int argc, char *argv[]);
334e500525SLeonardo Sandoval static void create_usage(int);
34819281eeSdp-arm static int update_cmd(int argc, char *argv[]);
354e500525SLeonardo Sandoval static void update_usage(int);
36819281eeSdp-arm static int unpack_cmd(int argc, char *argv[]);
374e500525SLeonardo Sandoval static void unpack_usage(int);
38819281eeSdp-arm static int remove_cmd(int argc, char *argv[]);
394e500525SLeonardo Sandoval static void remove_usage(int);
40819281eeSdp-arm static int version_cmd(int argc, char *argv[]);
414e500525SLeonardo Sandoval static void version_usage(int);
42819281eeSdp-arm static int help_cmd(int argc, char *argv[]);
43819281eeSdp-arm static void usage(void);
44819281eeSdp-arm 
45819281eeSdp-arm /* Available subcommands. */
46819281eeSdp-arm static cmd_t cmds[] = {
47819281eeSdp-arm 	{ .name = "info",    .handler = info_cmd,    .usage = info_usage    },
48819281eeSdp-arm 	{ .name = "create",  .handler = create_cmd,  .usage = create_usage  },
49819281eeSdp-arm 	{ .name = "update",  .handler = update_cmd,  .usage = update_usage  },
50819281eeSdp-arm 	{ .name = "unpack",  .handler = unpack_cmd,  .usage = unpack_usage  },
51819281eeSdp-arm 	{ .name = "remove",  .handler = remove_cmd,  .usage = remove_usage  },
52819281eeSdp-arm 	{ .name = "version", .handler = version_cmd, .usage = version_usage },
53819281eeSdp-arm 	{ .name = "help",    .handler = help_cmd,    .usage = NULL          },
54819281eeSdp-arm };
55819281eeSdp-arm 
56e0f083a0Sdp-arm static image_desc_t *image_desc_head;
57e0f083a0Sdp-arm static size_t nr_image_descs;
5803364865SRoberto Vargas static const uuid_t uuid_null;
59819281eeSdp-arm static int verbose;
60819281eeSdp-arm 
6160b499feSdp-arm static void vlog(int prio, const char *msg, va_list ap)
62819281eeSdp-arm {
63819281eeSdp-arm 	char *prefix[] = { "DEBUG", "WARN", "ERROR" };
64819281eeSdp-arm 
65819281eeSdp-arm 	fprintf(stderr, "%s: ", prefix[prio]);
66819281eeSdp-arm 	vfprintf(stderr, msg, ap);
67819281eeSdp-arm 	fputc('\n', stderr);
68819281eeSdp-arm }
69819281eeSdp-arm 
7060b499feSdp-arm static void log_dbgx(const char *msg, ...)
71819281eeSdp-arm {
72819281eeSdp-arm 	va_list ap;
73819281eeSdp-arm 
74819281eeSdp-arm 	va_start(ap, msg);
75819281eeSdp-arm 	vlog(LOG_DBG, msg, ap);
76819281eeSdp-arm 	va_end(ap);
77819281eeSdp-arm }
78819281eeSdp-arm 
7960b499feSdp-arm static void log_warnx(const char *msg, ...)
80819281eeSdp-arm {
81819281eeSdp-arm 	va_list ap;
82819281eeSdp-arm 
83819281eeSdp-arm 	va_start(ap, msg);
84819281eeSdp-arm 	vlog(LOG_WARN, msg, ap);
85819281eeSdp-arm 	va_end(ap);
86819281eeSdp-arm }
87819281eeSdp-arm 
8860b499feSdp-arm static void log_err(const char *msg, ...)
89819281eeSdp-arm {
90819281eeSdp-arm 	char buf[512];
91819281eeSdp-arm 	va_list ap;
92819281eeSdp-arm 
93819281eeSdp-arm 	va_start(ap, msg);
94819281eeSdp-arm 	snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
95819281eeSdp-arm 	vlog(LOG_ERR, buf, ap);
96819281eeSdp-arm 	va_end(ap);
97819281eeSdp-arm 	exit(1);
98819281eeSdp-arm }
99819281eeSdp-arm 
10060b499feSdp-arm static void log_errx(const char *msg, ...)
101819281eeSdp-arm {
102819281eeSdp-arm 	va_list ap;
103819281eeSdp-arm 
104819281eeSdp-arm 	va_start(ap, msg);
105819281eeSdp-arm 	vlog(LOG_ERR, msg, ap);
106819281eeSdp-arm 	va_end(ap);
107819281eeSdp-arm 	exit(1);
108819281eeSdp-arm }
109819281eeSdp-arm 
110a22f6285Sdp-arm static char *xstrdup(const char *s, const char *msg)
111a22f6285Sdp-arm {
112a22f6285Sdp-arm 	char *d;
113a22f6285Sdp-arm 
114a22f6285Sdp-arm 	d = strdup(s);
115a22f6285Sdp-arm 	if (d == NULL)
1169fc9ff1fSdp-arm 		log_errx("strdup: %s", msg);
117a22f6285Sdp-arm 	return d;
118a22f6285Sdp-arm }
119a22f6285Sdp-arm 
120a22f6285Sdp-arm static void *xmalloc(size_t size, const char *msg)
121a22f6285Sdp-arm {
122a22f6285Sdp-arm 	void *d;
123a22f6285Sdp-arm 
124a22f6285Sdp-arm 	d = malloc(size);
125a22f6285Sdp-arm 	if (d == NULL)
1269fc9ff1fSdp-arm 		log_errx("malloc: %s", msg);
127a22f6285Sdp-arm 	return d;
128a22f6285Sdp-arm }
129a22f6285Sdp-arm 
130696ccba6SMasahiro Yamada static void *xzalloc(size_t size, const char *msg)
131696ccba6SMasahiro Yamada {
132696ccba6SMasahiro Yamada 	return memset(xmalloc(size, msg), 0, size);
133696ccba6SMasahiro Yamada }
134696ccba6SMasahiro Yamada 
135a1da83f5SMasahiro Yamada static void xfwrite(void *buf, size_t size, FILE *fp, const char *filename)
136a1da83f5SMasahiro Yamada {
137a1da83f5SMasahiro Yamada 	if (fwrite(buf, 1, size, fp) != size)
138a1da83f5SMasahiro Yamada 		log_errx("Failed to write %s", filename);
139a1da83f5SMasahiro Yamada }
140a1da83f5SMasahiro Yamada 
141e0f083a0Sdp-arm static image_desc_t *new_image_desc(const uuid_t *uuid,
142e0f083a0Sdp-arm     const char *name, const char *cmdline_name)
143e0f083a0Sdp-arm {
144e0f083a0Sdp-arm 	image_desc_t *desc;
145e0f083a0Sdp-arm 
146696ccba6SMasahiro Yamada 	desc = xzalloc(sizeof(*desc),
147e0f083a0Sdp-arm 	    "failed to allocate memory for image descriptor");
148e0f083a0Sdp-arm 	memcpy(&desc->uuid, uuid, sizeof(uuid_t));
149e0f083a0Sdp-arm 	desc->name = xstrdup(name,
150e0f083a0Sdp-arm 	    "failed to allocate memory for image name");
151e0f083a0Sdp-arm 	desc->cmdline_name = xstrdup(cmdline_name,
152e0f083a0Sdp-arm 	    "failed to allocate memory for image command line name");
153e0f083a0Sdp-arm 	desc->action = DO_UNSPEC;
154e0f083a0Sdp-arm 	return desc;
155e0f083a0Sdp-arm }
156e0f083a0Sdp-arm 
157d02fcebeSdp-arm static void set_image_desc_action(image_desc_t *desc, int action,
158d02fcebeSdp-arm     const char *arg)
159d02fcebeSdp-arm {
160d02fcebeSdp-arm 	assert(desc != NULL);
161d02fcebeSdp-arm 
16296851114SEvan Lloyd 	if (desc->action_arg != (char *)DO_UNSPEC)
163d02fcebeSdp-arm 		free(desc->action_arg);
164d02fcebeSdp-arm 	desc->action = action;
165d02fcebeSdp-arm 	desc->action_arg = NULL;
166d02fcebeSdp-arm 	if (arg != NULL)
167d02fcebeSdp-arm 		desc->action_arg = xstrdup(arg,
168d02fcebeSdp-arm 		    "failed to allocate memory for argument");
169d02fcebeSdp-arm }
170d02fcebeSdp-arm 
171e0f083a0Sdp-arm static void free_image_desc(image_desc_t *desc)
172e0f083a0Sdp-arm {
173e0f083a0Sdp-arm 	free(desc->name);
174e0f083a0Sdp-arm 	free(desc->cmdline_name);
175e0f083a0Sdp-arm 	free(desc->action_arg);
1768d283231SJonathan Wright 	if (desc->image) {
1778d283231SJonathan Wright 		free(desc->image->buffer);
178b9589fe5Sdp-arm 		free(desc->image);
1798d283231SJonathan Wright 	}
180e0f083a0Sdp-arm 	free(desc);
181e0f083a0Sdp-arm }
182e0f083a0Sdp-arm 
183e0f083a0Sdp-arm static void add_image_desc(image_desc_t *desc)
184e0f083a0Sdp-arm {
18511c0a4ffSMasahiro Yamada 	image_desc_t **p = &image_desc_head;
18611c0a4ffSMasahiro Yamada 
18711c0a4ffSMasahiro Yamada 	while (*p)
18811c0a4ffSMasahiro Yamada 		p = &(*p)->next;
18911c0a4ffSMasahiro Yamada 
190e9e0d287SMasahiro Yamada 	assert(*p == NULL);
19111c0a4ffSMasahiro Yamada 	*p = desc;
192e0f083a0Sdp-arm 	nr_image_descs++;
193e0f083a0Sdp-arm }
194e0f083a0Sdp-arm 
195e0f083a0Sdp-arm static void free_image_descs(void)
196e0f083a0Sdp-arm {
197e0f083a0Sdp-arm 	image_desc_t *desc = image_desc_head, *tmp;
198e0f083a0Sdp-arm 
199e0f083a0Sdp-arm 	while (desc != NULL) {
200e0f083a0Sdp-arm 		tmp = desc->next;
201e0f083a0Sdp-arm 		free_image_desc(desc);
202e0f083a0Sdp-arm 		desc = tmp;
203e0f083a0Sdp-arm 		nr_image_descs--;
204e0f083a0Sdp-arm 	}
205e0f083a0Sdp-arm 	assert(nr_image_descs == 0);
206e0f083a0Sdp-arm }
207e0f083a0Sdp-arm 
208e0f083a0Sdp-arm static void fill_image_descs(void)
209e0f083a0Sdp-arm {
210e0f083a0Sdp-arm 	toc_entry_t *toc_entry;
211e0f083a0Sdp-arm 
212e0f083a0Sdp-arm 	for (toc_entry = toc_entries;
213e0f083a0Sdp-arm 	     toc_entry->cmdline_name != NULL;
214e0f083a0Sdp-arm 	     toc_entry++) {
215e0f083a0Sdp-arm 		image_desc_t *desc;
216e0f083a0Sdp-arm 
217e0f083a0Sdp-arm 		desc = new_image_desc(&toc_entry->uuid,
218e0f083a0Sdp-arm 		    toc_entry->name,
219e0f083a0Sdp-arm 		    toc_entry->cmdline_name);
220e0f083a0Sdp-arm 		add_image_desc(desc);
221e0f083a0Sdp-arm 	}
2223527d6d2SPankaj Gupta #ifdef PLAT_DEF_FIP_UUID
2233527d6d2SPankaj Gupta 	for (toc_entry = plat_def_toc_entries;
2243527d6d2SPankaj Gupta 	     toc_entry->cmdline_name != NULL;
2253527d6d2SPankaj Gupta 	     toc_entry++) {
2263527d6d2SPankaj Gupta 		image_desc_t *desc;
2273527d6d2SPankaj Gupta 
2283527d6d2SPankaj Gupta 		desc = new_image_desc(&toc_entry->uuid,
2293527d6d2SPankaj Gupta 		    toc_entry->name,
2303527d6d2SPankaj Gupta 		    toc_entry->cmdline_name);
2313527d6d2SPankaj Gupta 		add_image_desc(desc);
2323527d6d2SPankaj Gupta 	}
2333527d6d2SPankaj Gupta #endif
234e0f083a0Sdp-arm }
235e0f083a0Sdp-arm 
236e0f083a0Sdp-arm static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid)
237819281eeSdp-arm {
238e0f083a0Sdp-arm 	image_desc_t *desc;
239819281eeSdp-arm 
240e0f083a0Sdp-arm 	for (desc = image_desc_head; desc != NULL; desc = desc->next)
241e0f083a0Sdp-arm 		if (memcmp(&desc->uuid, uuid, sizeof(uuid_t)) == 0)
242e0f083a0Sdp-arm 			return desc;
243819281eeSdp-arm 	return NULL;
244819281eeSdp-arm }
245819281eeSdp-arm 
246e0f083a0Sdp-arm static image_desc_t *lookup_image_desc_from_opt(const char *opt)
247e0f083a0Sdp-arm {
248e0f083a0Sdp-arm 	image_desc_t *desc;
249e0f083a0Sdp-arm 
250e0f083a0Sdp-arm 	for (desc = image_desc_head; desc != NULL; desc = desc->next)
251e0f083a0Sdp-arm 		if (strcmp(desc->cmdline_name, opt) == 0)
252e0f083a0Sdp-arm 			return desc;
253e0f083a0Sdp-arm 	return NULL;
254e0f083a0Sdp-arm }
255e0f083a0Sdp-arm 
256fcab6bbeSdp-arm static void uuid_to_str(char *s, size_t len, const uuid_t *u)
257fcab6bbeSdp-arm {
258fcab6bbeSdp-arm 	assert(len >= (_UUID_STR_LEN + 1));
259fcab6bbeSdp-arm 
26003364865SRoberto Vargas 	snprintf(s, len,
26103364865SRoberto Vargas 	    "%02X%02X%02X%02X-%02X%02X-%02X%02X-%04X-%04X%04X%04X",
26203364865SRoberto Vargas 	    u->time_low[0], u->time_low[1], u->time_low[2], u->time_low[3],
26303364865SRoberto Vargas 	    u->time_mid[0], u->time_mid[1],
26403364865SRoberto Vargas 	    u->time_hi_and_version[0], u->time_hi_and_version[1],
26503364865SRoberto Vargas 	    (u->clock_seq_hi_and_reserved << 8) | u->clock_seq_low,
26603364865SRoberto Vargas 	    (u->node[0] << 8) | u->node[1],
26703364865SRoberto Vargas 	    (u->node[2] << 8) | u->node[3],
26803364865SRoberto Vargas 	    (u->node[4] << 8) | u->node[5]);
269fcab6bbeSdp-arm }
270fcab6bbeSdp-arm 
271fcab6bbeSdp-arm static void uuid_from_str(uuid_t *u, const char *s)
272fcab6bbeSdp-arm {
273fcab6bbeSdp-arm 	int n;
274fcab6bbeSdp-arm 
275fcab6bbeSdp-arm 	if (s == NULL)
276fcab6bbeSdp-arm 		log_errx("UUID cannot be NULL");
277fcab6bbeSdp-arm 	if (strlen(s) != _UUID_STR_LEN)
278fcab6bbeSdp-arm 		log_errx("Invalid UUID: %s", s);
279fcab6bbeSdp-arm 
280fcab6bbeSdp-arm 	n = sscanf(s,
28103364865SRoberto Vargas 	    "%2hhx%2hhx%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
28203364865SRoberto Vargas 	    &u->time_low[0], &u->time_low[1], &u->time_low[2], &u->time_low[3],
28303364865SRoberto Vargas 	    &u->time_mid[0], &u->time_mid[1],
28403364865SRoberto Vargas 	    &u->time_hi_and_version[0], &u->time_hi_and_version[1],
28503364865SRoberto Vargas 	    &u->clock_seq_hi_and_reserved, &u->clock_seq_low,
28603364865SRoberto Vargas 	    &u->node[0], &u->node[1],
28703364865SRoberto Vargas 	    &u->node[2], &u->node[3],
28803364865SRoberto Vargas 	    &u->node[4], &u->node[5]);
289fcab6bbeSdp-arm 	/*
290e56b8dc8SAndre Przywara 	 * Given the format specifier above, we expect 16 items to be scanned
291fcab6bbeSdp-arm 	 * for a properly formatted UUID.
292fcab6bbeSdp-arm 	 */
293e56b8dc8SAndre Przywara 	if (n != 16)
294fcab6bbeSdp-arm 		log_errx("Invalid UUID: %s", s);
295fcab6bbeSdp-arm }
296fcab6bbeSdp-arm 
29760b499feSdp-arm static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
298819281eeSdp-arm {
29996851114SEvan Lloyd 	struct BLD_PLAT_STAT st;
300819281eeSdp-arm 	FILE *fp;
301819281eeSdp-arm 	char *buf, *bufend;
302819281eeSdp-arm 	fip_toc_header_t *toc_header;
303819281eeSdp-arm 	fip_toc_entry_t *toc_entry;
304819281eeSdp-arm 	int terminated = 0;
30506e69f7cSAntonio Borneo 	size_t st_size;
306819281eeSdp-arm 
30755745deaSEvan Lloyd 	fp = fopen(filename, "rb");
308819281eeSdp-arm 	if (fp == NULL)
309819281eeSdp-arm 		log_err("fopen %s", filename);
310819281eeSdp-arm 
311819281eeSdp-arm 	if (fstat(fileno(fp), &st) == -1)
312819281eeSdp-arm 		log_err("fstat %s", filename);
313819281eeSdp-arm 
31406e69f7cSAntonio Borneo 	st_size = st.st_size;
31506e69f7cSAntonio Borneo 
31606e69f7cSAntonio Borneo #ifdef BLKGETSIZE64
31706e69f7cSAntonio Borneo 	if ((st.st_mode & S_IFBLK) != 0)
31806e69f7cSAntonio Borneo 		if (ioctl(fileno(fp), BLKGETSIZE64, &st_size) == -1)
31906e69f7cSAntonio Borneo 			log_err("ioctl %s", filename);
32006e69f7cSAntonio Borneo #endif
32106e69f7cSAntonio Borneo 
32206e69f7cSAntonio Borneo 	buf = xmalloc(st_size, "failed to load file into memory");
32306e69f7cSAntonio Borneo 	if (fread(buf, 1, st_size, fp) != st_size)
324819281eeSdp-arm 		log_errx("Failed to read %s", filename);
32506e69f7cSAntonio Borneo 	bufend = buf + st_size;
326819281eeSdp-arm 	fclose(fp);
327819281eeSdp-arm 
32806e69f7cSAntonio Borneo 	if (st_size < sizeof(fip_toc_header_t))
329819281eeSdp-arm 		log_errx("FIP %s is truncated", filename);
330819281eeSdp-arm 
331819281eeSdp-arm 	toc_header = (fip_toc_header_t *)buf;
332819281eeSdp-arm 	toc_entry = (fip_toc_entry_t *)(toc_header + 1);
333819281eeSdp-arm 
334819281eeSdp-arm 	if (toc_header->name != TOC_HEADER_NAME)
335819281eeSdp-arm 		log_errx("%s is not a FIP file", filename);
336819281eeSdp-arm 
337819281eeSdp-arm 	/* Return the ToC header if the caller wants it. */
338819281eeSdp-arm 	if (toc_header_out != NULL)
339819281eeSdp-arm 		*toc_header_out = *toc_header;
340819281eeSdp-arm 
341819281eeSdp-arm 	/* Walk through each ToC entry in the file. */
342819281eeSdp-arm 	while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
343fcab6bbeSdp-arm 		image_t *image;
344fcab6bbeSdp-arm 		image_desc_t *desc;
345fcab6bbeSdp-arm 
346819281eeSdp-arm 		/* Found the ToC terminator, we are done. */
347819281eeSdp-arm 		if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
348819281eeSdp-arm 			terminated = 1;
349819281eeSdp-arm 			break;
350819281eeSdp-arm 		}
351819281eeSdp-arm 
352819281eeSdp-arm 		/*
353819281eeSdp-arm 		 * Build a new image out of the ToC entry and add it to the
354819281eeSdp-arm 		 * table of images.
355819281eeSdp-arm 		 */
35611c0a4ffSMasahiro Yamada 		image = xzalloc(sizeof(*image),
357a22f6285Sdp-arm 		    "failed to allocate memory for image");
35865caa3d0SMasahiro Yamada 		image->toc_e = *toc_entry;
359a22f6285Sdp-arm 		image->buffer = xmalloc(toc_entry->size,
360a22f6285Sdp-arm 		    "failed to allocate image buffer, is FIP file corrupted?");
361819281eeSdp-arm 		/* Overflow checks before memory copy. */
362819281eeSdp-arm 		if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
36306e69f7cSAntonio Borneo 			log_errx("FIP %s is corrupted: entry size exceeds 64 bit address space",
36406e69f7cSAntonio Borneo 				filename);
36506e69f7cSAntonio Borneo 		if (toc_entry->size + toc_entry->offset_address > st_size)
36606e69f7cSAntonio Borneo 			log_errx("FIP %s is corrupted: entry size exceeds FIP file size",
36706e69f7cSAntonio Borneo 				filename);
368819281eeSdp-arm 
369819281eeSdp-arm 		memcpy(image->buffer, buf + toc_entry->offset_address,
370819281eeSdp-arm 		    toc_entry->size);
371819281eeSdp-arm 
372fcab6bbeSdp-arm 		/* If this is an unknown image, create a descriptor for it. */
37365caa3d0SMasahiro Yamada 		desc = lookup_image_desc_from_uuid(&toc_entry->uuid);
374fcab6bbeSdp-arm 		if (desc == NULL) {
375fcab6bbeSdp-arm 			char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
376fcab6bbeSdp-arm 
37765caa3d0SMasahiro Yamada 			uuid_to_str(name, sizeof(name), &toc_entry->uuid);
378fcab6bbeSdp-arm 			snprintf(filename, sizeof(filename), "%s%s",
379fcab6bbeSdp-arm 			    name, ".bin");
38065caa3d0SMasahiro Yamada 			desc = new_image_desc(&toc_entry->uuid, name, "blob");
381fcab6bbeSdp-arm 			desc->action = DO_UNPACK;
382fcab6bbeSdp-arm 			desc->action_arg = xstrdup(filename,
383fcab6bbeSdp-arm 			    "failed to allocate memory for blob filename");
384fcab6bbeSdp-arm 			add_image_desc(desc);
385fcab6bbeSdp-arm 		}
386fcab6bbeSdp-arm 
387b9589fe5Sdp-arm 		assert(desc->image == NULL);
388b9589fe5Sdp-arm 		desc->image = image;
389819281eeSdp-arm 
390819281eeSdp-arm 		toc_entry++;
391819281eeSdp-arm 	}
392819281eeSdp-arm 
393819281eeSdp-arm 	if (terminated == 0)
394819281eeSdp-arm 		log_errx("FIP %s does not have a ToC terminator entry",
395819281eeSdp-arm 		    filename);
396819281eeSdp-arm 	free(buf);
397819281eeSdp-arm 	return 0;
398819281eeSdp-arm }
399819281eeSdp-arm 
40060b499feSdp-arm static image_t *read_image_from_file(const uuid_t *uuid, const char *filename)
401819281eeSdp-arm {
40296851114SEvan Lloyd 	struct BLD_PLAT_STAT st;
403819281eeSdp-arm 	image_t *image;
404819281eeSdp-arm 	FILE *fp;
405819281eeSdp-arm 
406b04efcceSdp-arm 	assert(uuid != NULL);
40796851114SEvan Lloyd 	assert(filename != NULL);
408b04efcceSdp-arm 
40955745deaSEvan Lloyd 	fp = fopen(filename, "rb");
410819281eeSdp-arm 	if (fp == NULL)
411819281eeSdp-arm 		log_err("fopen %s", filename);
412819281eeSdp-arm 
413819281eeSdp-arm 	if (fstat(fileno(fp), &st) == -1)
414819281eeSdp-arm 		log_errx("fstat %s", filename);
415819281eeSdp-arm 
41611c0a4ffSMasahiro Yamada 	image = xzalloc(sizeof(*image), "failed to allocate memory for image");
41765caa3d0SMasahiro Yamada 	image->toc_e.uuid = *uuid;
418a22f6285Sdp-arm 	image->buffer = xmalloc(st.st_size, "failed to allocate image buffer");
419819281eeSdp-arm 	if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
420819281eeSdp-arm 		log_errx("Failed to read %s", filename);
42165caa3d0SMasahiro Yamada 	image->toc_e.size = st.st_size;
422819281eeSdp-arm 
423819281eeSdp-arm 	fclose(fp);
424819281eeSdp-arm 	return image;
425819281eeSdp-arm }
426819281eeSdp-arm 
42760b499feSdp-arm static int write_image_to_file(const image_t *image, const char *filename)
428819281eeSdp-arm {
429819281eeSdp-arm 	FILE *fp;
430819281eeSdp-arm 
43155745deaSEvan Lloyd 	fp = fopen(filename, "wb");
432819281eeSdp-arm 	if (fp == NULL)
433819281eeSdp-arm 		log_err("fopen");
43465caa3d0SMasahiro Yamada 	xfwrite(image->buffer, image->toc_e.size, fp, filename);
435819281eeSdp-arm 	fclose(fp);
436819281eeSdp-arm 	return 0;
437819281eeSdp-arm }
438819281eeSdp-arm 
439e0f083a0Sdp-arm static struct option *add_opt(struct option *opts, size_t *nr_opts,
440e0f083a0Sdp-arm     const char *name, int has_arg, int val)
441819281eeSdp-arm {
442e0f083a0Sdp-arm 	opts = realloc(opts, (*nr_opts + 1) * sizeof(*opts));
443e0f083a0Sdp-arm 	if (opts == NULL)
444e0f083a0Sdp-arm 		log_err("realloc");
445e0f083a0Sdp-arm 	opts[*nr_opts].name = name;
446e0f083a0Sdp-arm 	opts[*nr_opts].has_arg = has_arg;
447e0f083a0Sdp-arm 	opts[*nr_opts].flag = NULL;
448e0f083a0Sdp-arm 	opts[*nr_opts].val = val;
449e0f083a0Sdp-arm 	++*nr_opts;
450e0f083a0Sdp-arm 	return opts;
451819281eeSdp-arm }
452819281eeSdp-arm 
453e0f083a0Sdp-arm static struct option *fill_common_opts(struct option *opts, size_t *nr_opts,
454e0f083a0Sdp-arm     int has_arg)
455819281eeSdp-arm {
456e0f083a0Sdp-arm 	image_desc_t *desc;
457e0f083a0Sdp-arm 
458e0f083a0Sdp-arm 	for (desc = image_desc_head; desc != NULL; desc = desc->next)
459e0f083a0Sdp-arm 		opts = add_opt(opts, nr_opts, desc->cmdline_name, has_arg,
460e0f083a0Sdp-arm 		    OPT_TOC_ENTRY);
461e0f083a0Sdp-arm 	return opts;
462819281eeSdp-arm }
463819281eeSdp-arm 
4644d4fec28SOlivier Deprez #if !STATIC
465e0f083a0Sdp-arm static void md_print(const unsigned char *md, size_t len)
4669df69ba3Sdp-arm {
4679df69ba3Sdp-arm 	size_t i;
4689df69ba3Sdp-arm 
4699df69ba3Sdp-arm 	for (i = 0; i < len; i++)
4709df69ba3Sdp-arm 		printf("%02x", md[i]);
4719df69ba3Sdp-arm }
4724d4fec28SOlivier Deprez #endif
4739df69ba3Sdp-arm 
474819281eeSdp-arm static int info_cmd(int argc, char *argv[])
475819281eeSdp-arm {
476b9589fe5Sdp-arm 	image_desc_t *desc;
477819281eeSdp-arm 	fip_toc_header_t toc_header;
478819281eeSdp-arm 
479819281eeSdp-arm 	if (argc != 2)
4804e500525SLeonardo Sandoval 		info_usage(EXIT_FAILURE);
481819281eeSdp-arm 	argc--, argv++;
482819281eeSdp-arm 
483819281eeSdp-arm 	parse_fip(argv[0], &toc_header);
484819281eeSdp-arm 
485819281eeSdp-arm 	if (verbose) {
486819281eeSdp-arm 		log_dbgx("toc_header[name]: 0x%llX",
487819281eeSdp-arm 		    (unsigned long long)toc_header.name);
488819281eeSdp-arm 		log_dbgx("toc_header[serial_number]: 0x%llX",
489819281eeSdp-arm 		    (unsigned long long)toc_header.serial_number);
490819281eeSdp-arm 		log_dbgx("toc_header[flags]: 0x%llX",
491819281eeSdp-arm 		    (unsigned long long)toc_header.flags);
492819281eeSdp-arm 	}
493819281eeSdp-arm 
494b9589fe5Sdp-arm 	for (desc = image_desc_head; desc != NULL; desc = desc->next) {
495b9589fe5Sdp-arm 		image_t *image = desc->image;
496b04efcceSdp-arm 
497b9589fe5Sdp-arm 		if (image == NULL)
498b9589fe5Sdp-arm 			continue;
49965caa3d0SMasahiro Yamada 		printf("%s: offset=0x%llX, size=0x%llX, cmdline=\"--%s\"",
50065caa3d0SMasahiro Yamada 		       desc->name,
50165caa3d0SMasahiro Yamada 		       (unsigned long long)image->toc_e.offset_address,
50265caa3d0SMasahiro Yamada 		       (unsigned long long)image->toc_e.size,
503e0f083a0Sdp-arm 		       desc->cmdline_name);
5044d4fec28SOlivier Deprez 
5054d4fec28SOlivier Deprez 		/*
5064d4fec28SOlivier Deprez 		 * Omit this informative code portion for:
5074d4fec28SOlivier Deprez 		 * Visual Studio missing SHA256.
5084d4fec28SOlivier Deprez 		 * Statically linked builds.
5094d4fec28SOlivier Deprez 		 */
5104d4fec28SOlivier Deprez #if !defined(_MSC_VER) && !STATIC
5119df69ba3Sdp-arm 		if (verbose) {
5129df69ba3Sdp-arm 			unsigned char md[SHA256_DIGEST_LENGTH];
5139df69ba3Sdp-arm 
51465caa3d0SMasahiro Yamada 			SHA256(image->buffer, image->toc_e.size, md);
5159df69ba3Sdp-arm 			printf(", sha256=");
5169df69ba3Sdp-arm 			md_print(md, sizeof(md));
5179df69ba3Sdp-arm 		}
51896851114SEvan Lloyd #endif
519819281eeSdp-arm 		putchar('\n');
520819281eeSdp-arm 	}
521819281eeSdp-arm 
522819281eeSdp-arm 	return 0;
523819281eeSdp-arm }
524819281eeSdp-arm 
5254e500525SLeonardo Sandoval static void info_usage(int exit_status)
526819281eeSdp-arm {
527819281eeSdp-arm 	printf("fiptool info FIP_FILENAME\n");
5284e500525SLeonardo Sandoval 	exit(exit_status);
529819281eeSdp-arm }
530819281eeSdp-arm 
5311c75d5dfSMasahiro Yamada static int pack_images(const char *filename, uint64_t toc_flags, unsigned long align)
532819281eeSdp-arm {
533819281eeSdp-arm 	FILE *fp;
534b9589fe5Sdp-arm 	image_desc_t *desc;
535819281eeSdp-arm 	fip_toc_header_t *toc_header;
536819281eeSdp-arm 	fip_toc_entry_t *toc_entry;
537819281eeSdp-arm 	char *buf;
538880b9e8bSRoberto Vargas 	uint64_t entry_offset, buf_size, payload_size = 0, pad_size;
539b9589fe5Sdp-arm 	size_t nr_images = 0;
540b9589fe5Sdp-arm 
541b9589fe5Sdp-arm 	for (desc = image_desc_head; desc != NULL; desc = desc->next)
542b9589fe5Sdp-arm 		if (desc->image != NULL)
543b9589fe5Sdp-arm 			nr_images++;
544819281eeSdp-arm 
545819281eeSdp-arm 	buf_size = sizeof(fip_toc_header_t) +
546819281eeSdp-arm 	    sizeof(fip_toc_entry_t) * (nr_images + 1);
547819281eeSdp-arm 	buf = calloc(1, buf_size);
548819281eeSdp-arm 	if (buf == NULL)
549819281eeSdp-arm 		log_err("calloc");
550819281eeSdp-arm 
551819281eeSdp-arm 	/* Build up header and ToC entries from the image table. */
552819281eeSdp-arm 	toc_header = (fip_toc_header_t *)buf;
553819281eeSdp-arm 	toc_header->name = TOC_HEADER_NAME;
554819281eeSdp-arm 	toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
555819281eeSdp-arm 	toc_header->flags = toc_flags;
556819281eeSdp-arm 
557819281eeSdp-arm 	toc_entry = (fip_toc_entry_t *)(toc_header + 1);
558819281eeSdp-arm 
559819281eeSdp-arm 	entry_offset = buf_size;
560b9589fe5Sdp-arm 	for (desc = image_desc_head; desc != NULL; desc = desc->next) {
561b9589fe5Sdp-arm 		image_t *image = desc->image;
562b9589fe5Sdp-arm 
563ab556c9cSManish V Badarkhe 		if (image == NULL || (image->toc_e.size == 0ULL))
564b9589fe5Sdp-arm 			continue;
56565caa3d0SMasahiro Yamada 		payload_size += image->toc_e.size;
5661c75d5dfSMasahiro Yamada 		entry_offset = (entry_offset + align - 1) & ~(align - 1);
56765caa3d0SMasahiro Yamada 		image->toc_e.offset_address = entry_offset;
56865caa3d0SMasahiro Yamada 		*toc_entry++ = image->toc_e;
56965caa3d0SMasahiro Yamada 		entry_offset += image->toc_e.size;
570819281eeSdp-arm 	}
571819281eeSdp-arm 
572880b9e8bSRoberto Vargas 	/*
573880b9e8bSRoberto Vargas 	 * Append a null uuid entry to mark the end of ToC entries.
574880b9e8bSRoberto Vargas 	 * NOTE the offset address for the last toc_entry must match the fip
575880b9e8bSRoberto Vargas 	 * size.
576880b9e8bSRoberto Vargas 	 */
57765caa3d0SMasahiro Yamada 	memset(toc_entry, 0, sizeof(*toc_entry));
578880b9e8bSRoberto Vargas 	toc_entry->offset_address = (entry_offset + align - 1) & ~(align - 1);
579819281eeSdp-arm 
580819281eeSdp-arm 	/* Generate the FIP file. */
58155745deaSEvan Lloyd 	fp = fopen(filename, "wb");
582819281eeSdp-arm 	if (fp == NULL)
583819281eeSdp-arm 		log_err("fopen %s", filename);
584819281eeSdp-arm 
585819281eeSdp-arm 	if (verbose)
586819281eeSdp-arm 		log_dbgx("Metadata size: %zu bytes", buf_size);
587819281eeSdp-arm 
588a1da83f5SMasahiro Yamada 	xfwrite(buf, buf_size, fp, filename);
589819281eeSdp-arm 
590819281eeSdp-arm 	if (verbose)
591819281eeSdp-arm 		log_dbgx("Payload size: %zu bytes", payload_size);
592819281eeSdp-arm 
593b9589fe5Sdp-arm 	for (desc = image_desc_head; desc != NULL; desc = desc->next) {
594b9589fe5Sdp-arm 		image_t *image = desc->image;
595b9589fe5Sdp-arm 
596b9589fe5Sdp-arm 		if (image == NULL)
597b9589fe5Sdp-arm 			continue;
5981c75d5dfSMasahiro Yamada 		if (fseek(fp, image->toc_e.offset_address, SEEK_SET))
5991c75d5dfSMasahiro Yamada 			log_errx("Failed to set file position");
6001c75d5dfSMasahiro Yamada 
60165caa3d0SMasahiro Yamada 		xfwrite(image->buffer, image->toc_e.size, fp, filename);
6021c75d5dfSMasahiro Yamada 	}
603819281eeSdp-arm 
604880b9e8bSRoberto Vargas 	if (fseek(fp, entry_offset, SEEK_SET))
605880b9e8bSRoberto Vargas 		log_errx("Failed to set file position");
606880b9e8bSRoberto Vargas 
607880b9e8bSRoberto Vargas 	pad_size = toc_entry->offset_address - entry_offset;
608880b9e8bSRoberto Vargas 	while (pad_size--)
609880b9e8bSRoberto Vargas 		fputc(0x0, fp);
610880b9e8bSRoberto Vargas 
6118e4cdd22SAndreas Färber 	free(buf);
612819281eeSdp-arm 	fclose(fp);
613819281eeSdp-arm 	return 0;
614819281eeSdp-arm }
615819281eeSdp-arm 
616819281eeSdp-arm /*
617819281eeSdp-arm  * This function is shared between the create and update subcommands.
618819281eeSdp-arm  * The difference between the two subcommands is that when the FIP file
619819281eeSdp-arm  * is created, the parsing of an existing FIP is skipped.  This results
620819281eeSdp-arm  * in update_fip() creating the new FIP file from scratch because the
621819281eeSdp-arm  * internal image table is not populated.
622819281eeSdp-arm  */
623819281eeSdp-arm static void update_fip(void)
624819281eeSdp-arm {
625e0f083a0Sdp-arm 	image_desc_t *desc;
626819281eeSdp-arm 
627819281eeSdp-arm 	/* Add or replace images in the FIP file. */
628e0f083a0Sdp-arm 	for (desc = image_desc_head; desc != NULL; desc = desc->next) {
629b9589fe5Sdp-arm 		image_t *image;
630fcab6bbeSdp-arm 
631e0f083a0Sdp-arm 		if (desc->action != DO_PACK)
632819281eeSdp-arm 			continue;
633819281eeSdp-arm 
634b9589fe5Sdp-arm 		image = read_image_from_file(&desc->uuid,
635e0f083a0Sdp-arm 		    desc->action_arg);
636b9589fe5Sdp-arm 		if (desc->image != NULL) {
637e0f083a0Sdp-arm 			if (verbose) {
638fcab6bbeSdp-arm 				log_dbgx("Replacing %s with %s",
639e0f083a0Sdp-arm 				    desc->cmdline_name,
640e0f083a0Sdp-arm 				    desc->action_arg);
641e0f083a0Sdp-arm 			}
642b9589fe5Sdp-arm 			free(desc->image);
643b9589fe5Sdp-arm 			desc->image = image;
644819281eeSdp-arm 		} else {
645819281eeSdp-arm 			if (verbose)
646819281eeSdp-arm 				log_dbgx("Adding image %s",
647e0f083a0Sdp-arm 				    desc->action_arg);
648b9589fe5Sdp-arm 			desc->image = image;
649819281eeSdp-arm 		}
650819281eeSdp-arm 	}
651819281eeSdp-arm }
652819281eeSdp-arm 
653e0f083a0Sdp-arm static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
654819281eeSdp-arm {
655819281eeSdp-arm 	unsigned long long flags;
656819281eeSdp-arm 	char *endptr;
657819281eeSdp-arm 
658819281eeSdp-arm 	errno = 0;
659819281eeSdp-arm 	flags = strtoull(arg, &endptr, 16);
660819281eeSdp-arm 	if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
661819281eeSdp-arm 		log_errx("Invalid platform ToC flags: %s", arg);
662819281eeSdp-arm 	/* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
663819281eeSdp-arm 	*toc_flags |= flags << 32;
664819281eeSdp-arm }
665819281eeSdp-arm 
6661c75d5dfSMasahiro Yamada static int is_power_of_2(unsigned long x)
6671c75d5dfSMasahiro Yamada {
6681c75d5dfSMasahiro Yamada 	return x && !(x & (x - 1));
6691c75d5dfSMasahiro Yamada }
6701c75d5dfSMasahiro Yamada 
6711c75d5dfSMasahiro Yamada static unsigned long get_image_align(char *arg)
6721c75d5dfSMasahiro Yamada {
6731c75d5dfSMasahiro Yamada 	char *endptr;
6741c75d5dfSMasahiro Yamada 	unsigned long align;
6751c75d5dfSMasahiro Yamada 
6761c75d5dfSMasahiro Yamada 	errno = 0;
677fb5f7949SAndreas Färber 	align = strtoul(arg, &endptr, 0);
6781c75d5dfSMasahiro Yamada 	if (*endptr != '\0' || !is_power_of_2(align) || errno != 0)
6791c75d5dfSMasahiro Yamada 		log_errx("Invalid alignment: %s", arg);
6801c75d5dfSMasahiro Yamada 
6811c75d5dfSMasahiro Yamada 	return align;
6821c75d5dfSMasahiro Yamada }
6831c75d5dfSMasahiro Yamada 
684fcab6bbeSdp-arm static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
685fcab6bbeSdp-arm {
686fcab6bbeSdp-arm 	char *p;
687fcab6bbeSdp-arm 
688fcab6bbeSdp-arm 	for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
689fcab6bbeSdp-arm 		if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
690fcab6bbeSdp-arm 			p += strlen("uuid=");
691fcab6bbeSdp-arm 			uuid_from_str(uuid, p);
692fcab6bbeSdp-arm 		} else if (strncmp(p, "file=", strlen("file=")) == 0) {
693fcab6bbeSdp-arm 			p += strlen("file=");
694fcab6bbeSdp-arm 			snprintf(filename, len, "%s", p);
695fcab6bbeSdp-arm 		}
696fcab6bbeSdp-arm 	}
697fcab6bbeSdp-arm }
698fcab6bbeSdp-arm 
699819281eeSdp-arm static int create_cmd(int argc, char *argv[])
700819281eeSdp-arm {
701e0f083a0Sdp-arm 	struct option *opts = NULL;
702e0f083a0Sdp-arm 	size_t nr_opts = 0;
703819281eeSdp-arm 	unsigned long long toc_flags = 0;
7041c75d5dfSMasahiro Yamada 	unsigned long align = 1;
705819281eeSdp-arm 
706819281eeSdp-arm 	if (argc < 2)
7074e500525SLeonardo Sandoval 		create_usage(EXIT_FAILURE);
708819281eeSdp-arm 
709e0f083a0Sdp-arm 	opts = fill_common_opts(opts, &nr_opts, required_argument);
710e0f083a0Sdp-arm 	opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
711819281eeSdp-arm 	    OPT_PLAT_TOC_FLAGS);
7121c75d5dfSMasahiro Yamada 	opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
713fcab6bbeSdp-arm 	opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
714e0f083a0Sdp-arm 	opts = add_opt(opts, &nr_opts, NULL, 0, 0);
715819281eeSdp-arm 
716819281eeSdp-arm 	while (1) {
71720f87e78Sdp-arm 		int c, opt_index = 0;
718819281eeSdp-arm 
719fcab6bbeSdp-arm 		c = getopt_long(argc, argv, "b:", opts, &opt_index);
720819281eeSdp-arm 		if (c == -1)
721819281eeSdp-arm 			break;
722819281eeSdp-arm 
723819281eeSdp-arm 		switch (c) {
724819281eeSdp-arm 		case OPT_TOC_ENTRY: {
725e0f083a0Sdp-arm 			image_desc_t *desc;
726819281eeSdp-arm 
727e0f083a0Sdp-arm 			desc = lookup_image_desc_from_opt(opts[opt_index].name);
728d02fcebeSdp-arm 			set_image_desc_action(desc, DO_PACK, optarg);
729819281eeSdp-arm 			break;
730819281eeSdp-arm 		}
731819281eeSdp-arm 		case OPT_PLAT_TOC_FLAGS:
732819281eeSdp-arm 			parse_plat_toc_flags(optarg, &toc_flags);
733819281eeSdp-arm 			break;
7341c75d5dfSMasahiro Yamada 		case OPT_ALIGN:
7351c75d5dfSMasahiro Yamada 			align = get_image_align(optarg);
7361c75d5dfSMasahiro Yamada 			break;
737fcab6bbeSdp-arm 		case 'b': {
738fcab6bbeSdp-arm 			char name[_UUID_STR_LEN + 1];
739fcab6bbeSdp-arm 			char filename[PATH_MAX] = { 0 };
74003364865SRoberto Vargas 			uuid_t uuid = uuid_null;
741fcab6bbeSdp-arm 			image_desc_t *desc;
742fcab6bbeSdp-arm 
743fcab6bbeSdp-arm 			parse_blob_opt(optarg, &uuid,
744fcab6bbeSdp-arm 			    filename, sizeof(filename));
745fcab6bbeSdp-arm 
746fcab6bbeSdp-arm 			if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
747fcab6bbeSdp-arm 			    filename[0] == '\0')
7484e500525SLeonardo Sandoval 				create_usage(EXIT_FAILURE);
749fcab6bbeSdp-arm 
750fcab6bbeSdp-arm 			desc = lookup_image_desc_from_uuid(&uuid);
751d02fcebeSdp-arm 			if (desc == NULL) {
752fcab6bbeSdp-arm 				uuid_to_str(name, sizeof(name), &uuid);
753fcab6bbeSdp-arm 				desc = new_image_desc(&uuid, name, "blob");
754fcab6bbeSdp-arm 				add_image_desc(desc);
755fcab6bbeSdp-arm 			}
756d02fcebeSdp-arm 			set_image_desc_action(desc, DO_PACK, filename);
757fcab6bbeSdp-arm 			break;
758fcab6bbeSdp-arm 		}
759819281eeSdp-arm 		default:
7604e500525SLeonardo Sandoval 			create_usage(EXIT_FAILURE);
761819281eeSdp-arm 		}
762819281eeSdp-arm 	}
763819281eeSdp-arm 	argc -= optind;
764819281eeSdp-arm 	argv += optind;
765e0f083a0Sdp-arm 	free(opts);
766819281eeSdp-arm 
767819281eeSdp-arm 	if (argc == 0)
7684e500525SLeonardo Sandoval 		create_usage(EXIT_SUCCESS);
769819281eeSdp-arm 
770819281eeSdp-arm 	update_fip();
771819281eeSdp-arm 
7721c75d5dfSMasahiro Yamada 	pack_images(argv[0], toc_flags, align);
773819281eeSdp-arm 	return 0;
774819281eeSdp-arm }
775819281eeSdp-arm 
7764e500525SLeonardo Sandoval static void create_usage(int exit_status)
777819281eeSdp-arm {
778819281eeSdp-arm 	toc_entry_t *toc_entry = toc_entries;
779819281eeSdp-arm 
780ee079320SMasahiro Yamada 	printf("fiptool create [opts] FIP_FILENAME\n");
781ee079320SMasahiro Yamada 	printf("\n");
782ee079320SMasahiro Yamada 	printf("Options:\n");
7831c75d5dfSMasahiro Yamada 	printf("  --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
784802b42a0SMasahiro Yamada 	printf("  --blob uuid=...,file=...\tAdd an image with the given UUID pointed to by file.\n");
785802b42a0SMasahiro Yamada 	printf("  --plat-toc-flags <value>\t16-bit platform specific flag field occupying bits 32-47 in 64-bit ToC header.\n");
78695d2b268SMasahiro Yamada 	printf("\n");
787819281eeSdp-arm 	printf("Specific images are packed with the following options:\n");
788819281eeSdp-arm 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
789819281eeSdp-arm 		printf("  --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
790819281eeSdp-arm 		    toc_entry->name);
7913527d6d2SPankaj Gupta #ifdef PLAT_DEF_FIP_UUID
7923527d6d2SPankaj Gupta 	toc_entry = plat_def_toc_entries;
7933527d6d2SPankaj Gupta 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
7943527d6d2SPankaj Gupta 		printf("  --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
7953527d6d2SPankaj Gupta 		    toc_entry->name);
7963527d6d2SPankaj Gupta #endif
7974e500525SLeonardo Sandoval 	exit(exit_status);
798819281eeSdp-arm }
799819281eeSdp-arm 
800819281eeSdp-arm static int update_cmd(int argc, char *argv[])
801819281eeSdp-arm {
802e0f083a0Sdp-arm 	struct option *opts = NULL;
803e0f083a0Sdp-arm 	size_t nr_opts = 0;
804fcab6bbeSdp-arm 	char outfile[PATH_MAX] = { 0 };
805819281eeSdp-arm 	fip_toc_header_t toc_header = { 0 };
806819281eeSdp-arm 	unsigned long long toc_flags = 0;
8071c75d5dfSMasahiro Yamada 	unsigned long align = 1;
808819281eeSdp-arm 	int pflag = 0;
809819281eeSdp-arm 
810819281eeSdp-arm 	if (argc < 2)
8114e500525SLeonardo Sandoval 		update_usage(EXIT_FAILURE);
812819281eeSdp-arm 
813e0f083a0Sdp-arm 	opts = fill_common_opts(opts, &nr_opts, required_argument);
8141c75d5dfSMasahiro Yamada 	opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
815fcab6bbeSdp-arm 	opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
816e0f083a0Sdp-arm 	opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
817e0f083a0Sdp-arm 	opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
818819281eeSdp-arm 	    OPT_PLAT_TOC_FLAGS);
819e0f083a0Sdp-arm 	opts = add_opt(opts, &nr_opts, NULL, 0, 0);
820819281eeSdp-arm 
821819281eeSdp-arm 	while (1) {
82220f87e78Sdp-arm 		int c, opt_index = 0;
823819281eeSdp-arm 
824fcab6bbeSdp-arm 		c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
825819281eeSdp-arm 		if (c == -1)
826819281eeSdp-arm 			break;
827819281eeSdp-arm 
828819281eeSdp-arm 		switch (c) {
829819281eeSdp-arm 		case OPT_TOC_ENTRY: {
830e0f083a0Sdp-arm 			image_desc_t *desc;
831819281eeSdp-arm 
832e0f083a0Sdp-arm 			desc = lookup_image_desc_from_opt(opts[opt_index].name);
833d02fcebeSdp-arm 			set_image_desc_action(desc, DO_PACK, optarg);
834819281eeSdp-arm 			break;
835819281eeSdp-arm 		}
836e0f083a0Sdp-arm 		case OPT_PLAT_TOC_FLAGS:
837819281eeSdp-arm 			parse_plat_toc_flags(optarg, &toc_flags);
838819281eeSdp-arm 			pflag = 1;
839819281eeSdp-arm 			break;
840fcab6bbeSdp-arm 		case 'b': {
841fcab6bbeSdp-arm 			char name[_UUID_STR_LEN + 1];
842fcab6bbeSdp-arm 			char filename[PATH_MAX] = { 0 };
84303364865SRoberto Vargas 			uuid_t uuid = uuid_null;
844fcab6bbeSdp-arm 			image_desc_t *desc;
845fcab6bbeSdp-arm 
846fcab6bbeSdp-arm 			parse_blob_opt(optarg, &uuid,
847fcab6bbeSdp-arm 			    filename, sizeof(filename));
848fcab6bbeSdp-arm 
849fcab6bbeSdp-arm 			if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
850fcab6bbeSdp-arm 			    filename[0] == '\0')
8514e500525SLeonardo Sandoval 				update_usage(EXIT_FAILURE);
852fcab6bbeSdp-arm 
853fcab6bbeSdp-arm 			desc = lookup_image_desc_from_uuid(&uuid);
854d02fcebeSdp-arm 			if (desc == NULL) {
855fcab6bbeSdp-arm 				uuid_to_str(name, sizeof(name), &uuid);
856fcab6bbeSdp-arm 				desc = new_image_desc(&uuid, name, "blob");
857fcab6bbeSdp-arm 				add_image_desc(desc);
858fcab6bbeSdp-arm 			}
859d02fcebeSdp-arm 			set_image_desc_action(desc, DO_PACK, filename);
860fcab6bbeSdp-arm 			break;
861fcab6bbeSdp-arm 		}
8621c75d5dfSMasahiro Yamada 		case OPT_ALIGN:
8631c75d5dfSMasahiro Yamada 			align = get_image_align(optarg);
8641c75d5dfSMasahiro Yamada 			break;
865819281eeSdp-arm 		case 'o':
866819281eeSdp-arm 			snprintf(outfile, sizeof(outfile), "%s", optarg);
867819281eeSdp-arm 			break;
868819281eeSdp-arm 		default:
8694e500525SLeonardo Sandoval 			update_usage(EXIT_FAILURE);
870819281eeSdp-arm 		}
871819281eeSdp-arm 	}
872819281eeSdp-arm 	argc -= optind;
873819281eeSdp-arm 	argv += optind;
874e0f083a0Sdp-arm 	free(opts);
875819281eeSdp-arm 
876819281eeSdp-arm 	if (argc == 0)
8774e500525SLeonardo Sandoval 		update_usage(EXIT_SUCCESS);
878819281eeSdp-arm 
879819281eeSdp-arm 	if (outfile[0] == '\0')
880819281eeSdp-arm 		snprintf(outfile, sizeof(outfile), "%s", argv[0]);
881819281eeSdp-arm 
88240866aafSMasahiro Yamada 	if (access(argv[0], F_OK) == 0)
883819281eeSdp-arm 		parse_fip(argv[0], &toc_header);
884819281eeSdp-arm 
885819281eeSdp-arm 	if (pflag)
886819281eeSdp-arm 		toc_header.flags &= ~(0xffffULL << 32);
887819281eeSdp-arm 	toc_flags = (toc_header.flags |= toc_flags);
888819281eeSdp-arm 
889819281eeSdp-arm 	update_fip();
890819281eeSdp-arm 
8911c75d5dfSMasahiro Yamada 	pack_images(outfile, toc_flags, align);
892819281eeSdp-arm 	return 0;
893819281eeSdp-arm }
894819281eeSdp-arm 
8954e500525SLeonardo Sandoval static void update_usage(int exit_status)
896819281eeSdp-arm {
897819281eeSdp-arm 	toc_entry_t *toc_entry = toc_entries;
898819281eeSdp-arm 
899ee079320SMasahiro Yamada 	printf("fiptool update [opts] FIP_FILENAME\n");
900ee079320SMasahiro Yamada 	printf("\n");
901ee079320SMasahiro Yamada 	printf("Options:\n");
9021c75d5dfSMasahiro Yamada 	printf("  --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
903802b42a0SMasahiro Yamada 	printf("  --blob uuid=...,file=...\tAdd or update an image with the given UUID pointed to by file.\n");
904819281eeSdp-arm 	printf("  --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
905802b42a0SMasahiro Yamada 	printf("  --plat-toc-flags <value>\t16-bit platform specific flag field occupying bits 32-47 in 64-bit ToC header.\n");
90695d2b268SMasahiro Yamada 	printf("\n");
907819281eeSdp-arm 	printf("Specific images are packed with the following options:\n");
908819281eeSdp-arm 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
909819281eeSdp-arm 		printf("  --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
910819281eeSdp-arm 		    toc_entry->name);
9113527d6d2SPankaj Gupta #ifdef PLAT_DEF_FIP_UUID
9123527d6d2SPankaj Gupta 	toc_entry = plat_def_toc_entries;
9133527d6d2SPankaj Gupta 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
9143527d6d2SPankaj Gupta 		printf("  --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
9153527d6d2SPankaj Gupta 		    toc_entry->name);
9163527d6d2SPankaj Gupta #endif
9174e500525SLeonardo Sandoval 	exit(exit_status);
918819281eeSdp-arm }
919819281eeSdp-arm 
920819281eeSdp-arm static int unpack_cmd(int argc, char *argv[])
921819281eeSdp-arm {
922e0f083a0Sdp-arm 	struct option *opts = NULL;
923e0f083a0Sdp-arm 	size_t nr_opts = 0;
924fcab6bbeSdp-arm 	char outdir[PATH_MAX] = { 0 };
925e0f083a0Sdp-arm 	image_desc_t *desc;
926819281eeSdp-arm 	int fflag = 0;
927819281eeSdp-arm 	int unpack_all = 1;
928819281eeSdp-arm 
929819281eeSdp-arm 	if (argc < 2)
9304e500525SLeonardo Sandoval 		unpack_usage(EXIT_FAILURE);
931819281eeSdp-arm 
932e0f083a0Sdp-arm 	opts = fill_common_opts(opts, &nr_opts, required_argument);
933fcab6bbeSdp-arm 	opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
934e0f083a0Sdp-arm 	opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
935e0f083a0Sdp-arm 	opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
936e0f083a0Sdp-arm 	opts = add_opt(opts, &nr_opts, NULL, 0, 0);
937819281eeSdp-arm 
938819281eeSdp-arm 	while (1) {
93920f87e78Sdp-arm 		int c, opt_index = 0;
940819281eeSdp-arm 
941fcab6bbeSdp-arm 		c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
942819281eeSdp-arm 		if (c == -1)
943819281eeSdp-arm 			break;
944819281eeSdp-arm 
945819281eeSdp-arm 		switch (c) {
946e0f083a0Sdp-arm 		case OPT_TOC_ENTRY: {
947e0f083a0Sdp-arm 			image_desc_t *desc;
948e0f083a0Sdp-arm 
949e0f083a0Sdp-arm 			desc = lookup_image_desc_from_opt(opts[opt_index].name);
950d02fcebeSdp-arm 			set_image_desc_action(desc, DO_UNPACK, optarg);
951e0f083a0Sdp-arm 			unpack_all = 0;
952819281eeSdp-arm 			break;
953e0f083a0Sdp-arm 		}
954fcab6bbeSdp-arm 		case 'b': {
955fcab6bbeSdp-arm 			char name[_UUID_STR_LEN + 1];
956fcab6bbeSdp-arm 			char filename[PATH_MAX] = { 0 };
95703364865SRoberto Vargas 			uuid_t uuid = uuid_null;
958fcab6bbeSdp-arm 			image_desc_t *desc;
959fcab6bbeSdp-arm 
960fcab6bbeSdp-arm 			parse_blob_opt(optarg, &uuid,
961fcab6bbeSdp-arm 			    filename, sizeof(filename));
962fcab6bbeSdp-arm 
963fcab6bbeSdp-arm 			if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
964fcab6bbeSdp-arm 			    filename[0] == '\0')
9654e500525SLeonardo Sandoval 				unpack_usage(EXIT_FAILURE);
966fcab6bbeSdp-arm 
967fcab6bbeSdp-arm 			desc = lookup_image_desc_from_uuid(&uuid);
968d02fcebeSdp-arm 			if (desc == NULL) {
969fcab6bbeSdp-arm 				uuid_to_str(name, sizeof(name), &uuid);
970fcab6bbeSdp-arm 				desc = new_image_desc(&uuid, name, "blob");
971fcab6bbeSdp-arm 				add_image_desc(desc);
972fcab6bbeSdp-arm 			}
973d02fcebeSdp-arm 			set_image_desc_action(desc, DO_UNPACK, filename);
974fcab6bbeSdp-arm 			unpack_all = 0;
975fcab6bbeSdp-arm 			break;
976fcab6bbeSdp-arm 		}
977819281eeSdp-arm 		case 'f':
978819281eeSdp-arm 			fflag = 1;
979819281eeSdp-arm 			break;
980819281eeSdp-arm 		case 'o':
981819281eeSdp-arm 			snprintf(outdir, sizeof(outdir), "%s", optarg);
982819281eeSdp-arm 			break;
983819281eeSdp-arm 		default:
9844e500525SLeonardo Sandoval 			unpack_usage(EXIT_FAILURE);
985819281eeSdp-arm 		}
986819281eeSdp-arm 	}
987819281eeSdp-arm 	argc -= optind;
988819281eeSdp-arm 	argv += optind;
989e0f083a0Sdp-arm 	free(opts);
990819281eeSdp-arm 
991819281eeSdp-arm 	if (argc == 0)
9924e500525SLeonardo Sandoval 		unpack_usage(EXIT_SUCCESS);
993819281eeSdp-arm 
994819281eeSdp-arm 	parse_fip(argv[0], NULL);
995819281eeSdp-arm 
996819281eeSdp-arm 	if (outdir[0] != '\0')
997819281eeSdp-arm 		if (chdir(outdir) == -1)
998819281eeSdp-arm 			log_err("chdir %s", outdir);
999819281eeSdp-arm 
1000819281eeSdp-arm 	/* Unpack all specified images. */
1001e0f083a0Sdp-arm 	for (desc = image_desc_head; desc != NULL; desc = desc->next) {
1002fcab6bbeSdp-arm 		char file[PATH_MAX];
1003b9589fe5Sdp-arm 		image_t *image = desc->image;
1004b04efcceSdp-arm 
1005e0f083a0Sdp-arm 		if (!unpack_all && desc->action != DO_UNPACK)
1006819281eeSdp-arm 			continue;
1007819281eeSdp-arm 
1008819281eeSdp-arm 		/* Build filename. */
1009e0f083a0Sdp-arm 		if (desc->action_arg == NULL)
1010819281eeSdp-arm 			snprintf(file, sizeof(file), "%s.bin",
1011e0f083a0Sdp-arm 			    desc->cmdline_name);
1012819281eeSdp-arm 		else
1013819281eeSdp-arm 			snprintf(file, sizeof(file), "%s",
1014e0f083a0Sdp-arm 			    desc->action_arg);
1015819281eeSdp-arm 
1016b04efcceSdp-arm 		if (image == NULL) {
1017b04efcceSdp-arm 			if (!unpack_all)
1018fcab6bbeSdp-arm 				log_warnx("%s does not exist in %s",
1019819281eeSdp-arm 				    file, argv[0]);
1020819281eeSdp-arm 			continue;
1021819281eeSdp-arm 		}
1022819281eeSdp-arm 
1023819281eeSdp-arm 		if (access(file, F_OK) != 0 || fflag) {
1024819281eeSdp-arm 			if (verbose)
1025819281eeSdp-arm 				log_dbgx("Unpacking %s", file);
1026b04efcceSdp-arm 			write_image_to_file(image, file);
1027819281eeSdp-arm 		} else {
1028819281eeSdp-arm 			log_warnx("File %s already exists, use --force to overwrite it",
1029819281eeSdp-arm 			    file);
1030819281eeSdp-arm 		}
1031819281eeSdp-arm 	}
1032819281eeSdp-arm 
1033819281eeSdp-arm 	return 0;
1034819281eeSdp-arm }
1035819281eeSdp-arm 
10364e500525SLeonardo Sandoval static void unpack_usage(int exit_status)
1037819281eeSdp-arm {
1038819281eeSdp-arm 	toc_entry_t *toc_entry = toc_entries;
1039819281eeSdp-arm 
1040ee079320SMasahiro Yamada 	printf("fiptool unpack [opts] FIP_FILENAME\n");
1041ee079320SMasahiro Yamada 	printf("\n");
1042ee079320SMasahiro Yamada 	printf("Options:\n");
1043802b42a0SMasahiro Yamada 	printf("  --blob uuid=...,file=...\tUnpack an image with the given UUID to file.\n");
1044802b42a0SMasahiro Yamada 	printf("  --force\t\t\tIf the output file already exists, use --force to overwrite it.\n");
1045fcab6bbeSdp-arm 	printf("  --out path\t\t\tSet the output directory path.\n");
104695d2b268SMasahiro Yamada 	printf("\n");
1047819281eeSdp-arm 	printf("Specific images are unpacked with the following options:\n");
1048819281eeSdp-arm 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
1049819281eeSdp-arm 		printf("  --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
1050819281eeSdp-arm 		    toc_entry->name);
10513527d6d2SPankaj Gupta #ifdef PLAT_DEF_FIP_UUID
10523527d6d2SPankaj Gupta 	toc_entry = plat_def_toc_entries;
10533527d6d2SPankaj Gupta 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
10543527d6d2SPankaj Gupta 		printf("  --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
10553527d6d2SPankaj Gupta 		    toc_entry->name);
10563527d6d2SPankaj Gupta #endif
105795d2b268SMasahiro Yamada 	printf("\n");
1058819281eeSdp-arm 	printf("If no options are provided, all images will be unpacked.\n");
10594e500525SLeonardo Sandoval 	exit(exit_status);
1060819281eeSdp-arm }
1061819281eeSdp-arm 
1062819281eeSdp-arm static int remove_cmd(int argc, char *argv[])
1063819281eeSdp-arm {
1064e0f083a0Sdp-arm 	struct option *opts = NULL;
1065e0f083a0Sdp-arm 	size_t nr_opts = 0;
1066fcab6bbeSdp-arm 	char outfile[PATH_MAX] = { 0 };
1067819281eeSdp-arm 	fip_toc_header_t toc_header;
1068e0f083a0Sdp-arm 	image_desc_t *desc;
10691c75d5dfSMasahiro Yamada 	unsigned long align = 1;
1070819281eeSdp-arm 	int fflag = 0;
1071819281eeSdp-arm 
1072819281eeSdp-arm 	if (argc < 2)
10734e500525SLeonardo Sandoval 		remove_usage(EXIT_FAILURE);
1074819281eeSdp-arm 
1075e0f083a0Sdp-arm 	opts = fill_common_opts(opts, &nr_opts, no_argument);
10761c75d5dfSMasahiro Yamada 	opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
1077fcab6bbeSdp-arm 	opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
1078e0f083a0Sdp-arm 	opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
1079e0f083a0Sdp-arm 	opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
1080e0f083a0Sdp-arm 	opts = add_opt(opts, &nr_opts, NULL, 0, 0);
1081819281eeSdp-arm 
1082819281eeSdp-arm 	while (1) {
108320f87e78Sdp-arm 		int c, opt_index = 0;
1084819281eeSdp-arm 
1085fcab6bbeSdp-arm 		c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
1086819281eeSdp-arm 		if (c == -1)
1087819281eeSdp-arm 			break;
1088819281eeSdp-arm 
1089819281eeSdp-arm 		switch (c) {
1090e0f083a0Sdp-arm 		case OPT_TOC_ENTRY: {
1091e0f083a0Sdp-arm 			image_desc_t *desc;
1092e0f083a0Sdp-arm 
1093e0f083a0Sdp-arm 			desc = lookup_image_desc_from_opt(opts[opt_index].name);
1094d02fcebeSdp-arm 			set_image_desc_action(desc, DO_REMOVE, NULL);
1095819281eeSdp-arm 			break;
1096e0f083a0Sdp-arm 		}
10971c75d5dfSMasahiro Yamada 		case OPT_ALIGN:
10981c75d5dfSMasahiro Yamada 			align = get_image_align(optarg);
10991c75d5dfSMasahiro Yamada 			break;
1100fcab6bbeSdp-arm 		case 'b': {
1101fcab6bbeSdp-arm 			char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
110203364865SRoberto Vargas 			uuid_t uuid = uuid_null;
1103fcab6bbeSdp-arm 			image_desc_t *desc;
1104fcab6bbeSdp-arm 
1105fcab6bbeSdp-arm 			parse_blob_opt(optarg, &uuid,
1106fcab6bbeSdp-arm 			    filename, sizeof(filename));
1107fcab6bbeSdp-arm 
1108fcab6bbeSdp-arm 			if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
11094e500525SLeonardo Sandoval 				remove_usage(EXIT_FAILURE);
1110fcab6bbeSdp-arm 
1111fcab6bbeSdp-arm 			desc = lookup_image_desc_from_uuid(&uuid);
1112d02fcebeSdp-arm 			if (desc == NULL) {
1113fcab6bbeSdp-arm 				uuid_to_str(name, sizeof(name), &uuid);
1114fcab6bbeSdp-arm 				desc = new_image_desc(&uuid, name, "blob");
1115fcab6bbeSdp-arm 				add_image_desc(desc);
1116fcab6bbeSdp-arm 			}
1117d02fcebeSdp-arm 			set_image_desc_action(desc, DO_REMOVE, NULL);
1118fcab6bbeSdp-arm 			break;
1119fcab6bbeSdp-arm 		}
1120819281eeSdp-arm 		case 'f':
1121819281eeSdp-arm 			fflag = 1;
1122819281eeSdp-arm 			break;
1123819281eeSdp-arm 		case 'o':
1124819281eeSdp-arm 			snprintf(outfile, sizeof(outfile), "%s", optarg);
1125819281eeSdp-arm 			break;
1126819281eeSdp-arm 		default:
11274e500525SLeonardo Sandoval 			remove_usage(EXIT_FAILURE);
1128819281eeSdp-arm 		}
1129819281eeSdp-arm 	}
1130819281eeSdp-arm 	argc -= optind;
1131819281eeSdp-arm 	argv += optind;
1132e0f083a0Sdp-arm 	free(opts);
1133819281eeSdp-arm 
1134819281eeSdp-arm 	if (argc == 0)
11354e500525SLeonardo Sandoval 		remove_usage(EXIT_SUCCESS);
1136819281eeSdp-arm 
1137819281eeSdp-arm 	if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
1138819281eeSdp-arm 		log_errx("File %s already exists, use --force to overwrite it",
1139819281eeSdp-arm 		    outfile);
1140819281eeSdp-arm 
1141819281eeSdp-arm 	if (outfile[0] == '\0')
1142819281eeSdp-arm 		snprintf(outfile, sizeof(outfile), "%s", argv[0]);
1143819281eeSdp-arm 
1144819281eeSdp-arm 	parse_fip(argv[0], &toc_header);
1145819281eeSdp-arm 
1146e0f083a0Sdp-arm 	for (desc = image_desc_head; desc != NULL; desc = desc->next) {
1147e0f083a0Sdp-arm 		if (desc->action != DO_REMOVE)
1148819281eeSdp-arm 			continue;
1149fcab6bbeSdp-arm 
1150b9589fe5Sdp-arm 		if (desc->image != NULL) {
1151819281eeSdp-arm 			if (verbose)
1152fcab6bbeSdp-arm 				log_dbgx("Removing %s",
1153e0f083a0Sdp-arm 				    desc->cmdline_name);
1154b9589fe5Sdp-arm 			free(desc->image);
1155b9589fe5Sdp-arm 			desc->image = NULL;
1156819281eeSdp-arm 		} else {
1157fcab6bbeSdp-arm 			log_warnx("%s does not exist in %s",
1158e0f083a0Sdp-arm 			    desc->cmdline_name, argv[0]);
1159819281eeSdp-arm 		}
1160819281eeSdp-arm 	}
1161819281eeSdp-arm 
11621c75d5dfSMasahiro Yamada 	pack_images(outfile, toc_header.flags, align);
1163819281eeSdp-arm 	return 0;
1164819281eeSdp-arm }
1165819281eeSdp-arm 
11664e500525SLeonardo Sandoval static void remove_usage(int exit_status)
1167819281eeSdp-arm {
1168819281eeSdp-arm 	toc_entry_t *toc_entry = toc_entries;
1169819281eeSdp-arm 
1170ee079320SMasahiro Yamada 	printf("fiptool remove [opts] FIP_FILENAME\n");
1171ee079320SMasahiro Yamada 	printf("\n");
1172ee079320SMasahiro Yamada 	printf("Options:\n");
11731c75d5dfSMasahiro Yamada 	printf("  --align <value>\tEach image is aligned to <value> (default: 1).\n");
1174fcab6bbeSdp-arm 	printf("  --blob uuid=...\tRemove an image with the given UUID.\n");
1175802b42a0SMasahiro Yamada 	printf("  --force\t\tIf the output FIP file already exists, use --force to overwrite it.\n");
1176819281eeSdp-arm 	printf("  --out FIP_FILENAME\tSet an alternative output FIP file.\n");
117795d2b268SMasahiro Yamada 	printf("\n");
1178819281eeSdp-arm 	printf("Specific images are removed with the following options:\n");
1179819281eeSdp-arm 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
1180819281eeSdp-arm 		printf("  --%-16s\t%s\n", toc_entry->cmdline_name,
1181819281eeSdp-arm 		    toc_entry->name);
11823527d6d2SPankaj Gupta #ifdef PLAT_DEF_FIP_UUID
11833527d6d2SPankaj Gupta 	toc_entry = plat_def_toc_entries;
11843527d6d2SPankaj Gupta 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
11853527d6d2SPankaj Gupta 		printf("  --%-16s\t%s\n", toc_entry->cmdline_name,
11863527d6d2SPankaj Gupta 		    toc_entry->name);
11873527d6d2SPankaj Gupta #endif
11884e500525SLeonardo Sandoval 	exit(exit_status);
1189819281eeSdp-arm }
1190819281eeSdp-arm 
1191819281eeSdp-arm static int version_cmd(int argc, char *argv[])
1192819281eeSdp-arm {
1193819281eeSdp-arm #ifdef VERSION
1194819281eeSdp-arm 	puts(VERSION);
1195819281eeSdp-arm #else
1196819281eeSdp-arm 	/* If built from fiptool directory, VERSION is not set. */
1197819281eeSdp-arm 	puts("Unknown version");
1198819281eeSdp-arm #endif
1199819281eeSdp-arm 	return 0;
1200819281eeSdp-arm }
1201819281eeSdp-arm 
12024e500525SLeonardo Sandoval static void version_usage(int exit_status)
1203819281eeSdp-arm {
1204819281eeSdp-arm 	printf("fiptool version\n");
12054e500525SLeonardo Sandoval 	exit(exit_status);
1206819281eeSdp-arm }
1207819281eeSdp-arm 
1208819281eeSdp-arm static int help_cmd(int argc, char *argv[])
1209819281eeSdp-arm {
1210819281eeSdp-arm 	int i;
1211819281eeSdp-arm 
1212819281eeSdp-arm 	if (argc < 2)
1213819281eeSdp-arm 		usage();
1214819281eeSdp-arm 	argc--, argv++;
1215819281eeSdp-arm 
1216819281eeSdp-arm 	for (i = 0; i < NELEM(cmds); i++) {
1217819281eeSdp-arm 		if (strcmp(cmds[i].name, argv[0]) == 0 &&
121885ee2778Sdp-arm 		    cmds[i].usage != NULL)
12194e500525SLeonardo Sandoval 			cmds[i].usage(EXIT_SUCCESS);
1220819281eeSdp-arm 	}
1221819281eeSdp-arm 	if (i == NELEM(cmds))
1222819281eeSdp-arm 		printf("No help for subcommand '%s'\n", argv[0]);
1223819281eeSdp-arm 	return 0;
1224819281eeSdp-arm }
1225819281eeSdp-arm 
1226819281eeSdp-arm static void usage(void)
1227819281eeSdp-arm {
12284f96a498SMasahiro Yamada 	printf("usage: fiptool [--verbose] <command> [<args>]\n");
1229819281eeSdp-arm 	printf("Global options supported:\n");
1230819281eeSdp-arm 	printf("  --verbose\tEnable verbose output for all commands.\n");
123195d2b268SMasahiro Yamada 	printf("\n");
1232819281eeSdp-arm 	printf("Commands supported:\n");
1233819281eeSdp-arm 	printf("  info\t\tList images contained in FIP.\n");
1234819281eeSdp-arm 	printf("  create\tCreate a new FIP with the given images.\n");
1235819281eeSdp-arm 	printf("  update\tUpdate an existing FIP with the given images.\n");
1236819281eeSdp-arm 	printf("  unpack\tUnpack images from FIP.\n");
1237819281eeSdp-arm 	printf("  remove\tRemove images from FIP.\n");
1238819281eeSdp-arm 	printf("  version\tShow fiptool version.\n");
1239819281eeSdp-arm 	printf("  help\t\tShow help for given command.\n");
12404e500525SLeonardo Sandoval 	exit(EXIT_SUCCESS);
1241819281eeSdp-arm }
1242819281eeSdp-arm 
1243819281eeSdp-arm int main(int argc, char *argv[])
1244819281eeSdp-arm {
1245819281eeSdp-arm 	int i, ret = 0;
1246819281eeSdp-arm 
1247cc672bb2Sdp-arm 	while (1) {
1248cc672bb2Sdp-arm 		int c, opt_index = 0;
1249cc672bb2Sdp-arm 		static struct option opts[] = {
1250cc672bb2Sdp-arm 			{ "verbose", no_argument, NULL, 'v' },
1251cc672bb2Sdp-arm 			{ NULL, no_argument, NULL, 0 }
1252cc672bb2Sdp-arm 		};
1253819281eeSdp-arm 
1254cc672bb2Sdp-arm 		/*
1255cc672bb2Sdp-arm 		 * Set POSIX mode so getopt stops at the first non-option
1256cc672bb2Sdp-arm 		 * which is the subcommand.
1257cc672bb2Sdp-arm 		 */
1258cc672bb2Sdp-arm 		c = getopt_long(argc, argv, "+v", opts, &opt_index);
1259cc672bb2Sdp-arm 		if (c == -1)
1260cc672bb2Sdp-arm 			break;
1261cc672bb2Sdp-arm 
1262cc672bb2Sdp-arm 		switch (c) {
1263cc672bb2Sdp-arm 		case 'v':
1264819281eeSdp-arm 			verbose = 1;
1265cc672bb2Sdp-arm 			break;
1266cc672bb2Sdp-arm 		default:
1267c9cb4089SMasahiro Yamada 			usage();
1268819281eeSdp-arm 		}
1269cc672bb2Sdp-arm 	}
1270cc672bb2Sdp-arm 	argc -= optind;
1271cc672bb2Sdp-arm 	argv += optind;
1272cc672bb2Sdp-arm 	/* Reset optind for subsequent getopt processing. */
1273cc672bb2Sdp-arm 	optind = 0;
1274cc672bb2Sdp-arm 
1275cc672bb2Sdp-arm 	if (argc == 0)
1276cc672bb2Sdp-arm 		usage();
1277819281eeSdp-arm 
1278e0f083a0Sdp-arm 	fill_image_descs();
1279819281eeSdp-arm 	for (i = 0; i < NELEM(cmds); i++) {
1280819281eeSdp-arm 		if (strcmp(cmds[i].name, argv[0]) == 0) {
1281819281eeSdp-arm 			ret = cmds[i].handler(argc, argv);
1282819281eeSdp-arm 			break;
1283819281eeSdp-arm 		}
1284819281eeSdp-arm 	}
1285819281eeSdp-arm 	if (i == NELEM(cmds))
1286819281eeSdp-arm 		usage();
1287e0f083a0Sdp-arm 	free_image_descs();
1288819281eeSdp-arm 	return ret;
1289819281eeSdp-arm }
1290