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