xref: /rk3399_ARM-atf/tools/fiptool/fiptool.c (revision 819281ee23e1fd048e8385ecc708f78dff5e51d9)
1*819281eeSdp-arm /*
2*819281eeSdp-arm  * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
3*819281eeSdp-arm  *
4*819281eeSdp-arm  * Redistribution and use in source and binary forms, with or without
5*819281eeSdp-arm  * modification, are permitted provided that the following conditions are met:
6*819281eeSdp-arm  *
7*819281eeSdp-arm  * Redistributions of source code must retain the above copyright notice, this
8*819281eeSdp-arm  * list of conditions and the following disclaimer.
9*819281eeSdp-arm  *
10*819281eeSdp-arm  * Redistributions in binary form must reproduce the above copyright notice,
11*819281eeSdp-arm  * this list of conditions and the following disclaimer in the documentation
12*819281eeSdp-arm  * and/or other materials provided with the distribution.
13*819281eeSdp-arm  *
14*819281eeSdp-arm  * Neither the name of ARM nor the names of its contributors may be used
15*819281eeSdp-arm  * to endorse or promote products derived from this software without specific
16*819281eeSdp-arm  * prior written permission.
17*819281eeSdp-arm  *
18*819281eeSdp-arm  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19*819281eeSdp-arm  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*819281eeSdp-arm  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*819281eeSdp-arm  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22*819281eeSdp-arm  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23*819281eeSdp-arm  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24*819281eeSdp-arm  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25*819281eeSdp-arm  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26*819281eeSdp-arm  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27*819281eeSdp-arm  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28*819281eeSdp-arm  * POSSIBILITY OF SUCH DAMAGE.
29*819281eeSdp-arm  */
30*819281eeSdp-arm 
31*819281eeSdp-arm #include <sys/types.h>
32*819281eeSdp-arm #include <sys/stat.h>
33*819281eeSdp-arm 
34*819281eeSdp-arm #include <assert.h>
35*819281eeSdp-arm #include <errno.h>
36*819281eeSdp-arm #include <getopt.h>
37*819281eeSdp-arm #include <limits.h>
38*819281eeSdp-arm #include <stdarg.h>
39*819281eeSdp-arm #include <stdint.h>
40*819281eeSdp-arm #include <stdio.h>
41*819281eeSdp-arm #include <stdlib.h>
42*819281eeSdp-arm #include <string.h>
43*819281eeSdp-arm #include <unistd.h>
44*819281eeSdp-arm 
45*819281eeSdp-arm #include "fiptool.h"
46*819281eeSdp-arm #include "firmware_image_package.h"
47*819281eeSdp-arm #include "tbbr_config.h"
48*819281eeSdp-arm 
49*819281eeSdp-arm #define OPT_TOC_ENTRY 0
50*819281eeSdp-arm #define OPT_PLAT_TOC_FLAGS 1
51*819281eeSdp-arm 
52*819281eeSdp-arm static int info_cmd(int argc, char *argv[]);
53*819281eeSdp-arm static void info_usage(void);
54*819281eeSdp-arm static int create_cmd(int argc, char *argv[]);
55*819281eeSdp-arm static void create_usage(void);
56*819281eeSdp-arm static int update_cmd(int argc, char *argv[]);
57*819281eeSdp-arm static void update_usage(void);
58*819281eeSdp-arm static int unpack_cmd(int argc, char *argv[]);
59*819281eeSdp-arm static void unpack_usage(void);
60*819281eeSdp-arm static int remove_cmd(int argc, char *argv[]);
61*819281eeSdp-arm static void remove_usage(void);
62*819281eeSdp-arm static int version_cmd(int argc, char *argv[]);
63*819281eeSdp-arm static void version_usage(void);
64*819281eeSdp-arm static int help_cmd(int argc, char *argv[]);
65*819281eeSdp-arm static void usage(void);
66*819281eeSdp-arm 
67*819281eeSdp-arm /* Available subcommands. */
68*819281eeSdp-arm static cmd_t cmds[] = {
69*819281eeSdp-arm 	{ .name = "info",    .handler = info_cmd,    .usage = info_usage    },
70*819281eeSdp-arm 	{ .name = "create",  .handler = create_cmd,  .usage = create_usage  },
71*819281eeSdp-arm 	{ .name = "update",  .handler = update_cmd,  .usage = update_usage  },
72*819281eeSdp-arm 	{ .name = "unpack",  .handler = unpack_cmd,  .usage = unpack_usage  },
73*819281eeSdp-arm 	{ .name = "remove",  .handler = remove_cmd,  .usage = remove_usage  },
74*819281eeSdp-arm 	{ .name = "version", .handler = version_cmd, .usage = version_usage },
75*819281eeSdp-arm 	{ .name = "help",    .handler = help_cmd,    .usage = NULL          },
76*819281eeSdp-arm };
77*819281eeSdp-arm 
78*819281eeSdp-arm static image_t *images[MAX_IMAGES];
79*819281eeSdp-arm static size_t nr_images;
80*819281eeSdp-arm static uuid_t uuid_null = { 0 };
81*819281eeSdp-arm static int verbose;
82*819281eeSdp-arm 
83*819281eeSdp-arm static void vlog(int prio, char *msg, va_list ap)
84*819281eeSdp-arm {
85*819281eeSdp-arm 	char *prefix[] = { "DEBUG", "WARN", "ERROR" };
86*819281eeSdp-arm 
87*819281eeSdp-arm 	fprintf(stderr, "%s: ", prefix[prio]);
88*819281eeSdp-arm 	vfprintf(stderr, msg, ap);
89*819281eeSdp-arm 	fputc('\n', stderr);
90*819281eeSdp-arm }
91*819281eeSdp-arm 
92*819281eeSdp-arm static void log_dbgx(char *msg, ...)
93*819281eeSdp-arm {
94*819281eeSdp-arm 	va_list ap;
95*819281eeSdp-arm 
96*819281eeSdp-arm 	va_start(ap, msg);
97*819281eeSdp-arm 	vlog(LOG_DBG, msg, ap);
98*819281eeSdp-arm 	va_end(ap);
99*819281eeSdp-arm }
100*819281eeSdp-arm 
101*819281eeSdp-arm static void log_warnx(char *msg, ...)
102*819281eeSdp-arm {
103*819281eeSdp-arm 	va_list ap;
104*819281eeSdp-arm 
105*819281eeSdp-arm 	va_start(ap, msg);
106*819281eeSdp-arm 	vlog(LOG_WARN, msg, ap);
107*819281eeSdp-arm 	va_end(ap);
108*819281eeSdp-arm }
109*819281eeSdp-arm 
110*819281eeSdp-arm static void log_err(char *msg, ...)
111*819281eeSdp-arm {
112*819281eeSdp-arm 	char buf[512];
113*819281eeSdp-arm 	va_list ap;
114*819281eeSdp-arm 
115*819281eeSdp-arm 	va_start(ap, msg);
116*819281eeSdp-arm 	snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
117*819281eeSdp-arm 	vlog(LOG_ERR, buf, ap);
118*819281eeSdp-arm 	va_end(ap);
119*819281eeSdp-arm 	exit(1);
120*819281eeSdp-arm }
121*819281eeSdp-arm 
122*819281eeSdp-arm static void log_errx(char *msg, ...)
123*819281eeSdp-arm {
124*819281eeSdp-arm 	va_list ap;
125*819281eeSdp-arm 
126*819281eeSdp-arm 	va_start(ap, msg);
127*819281eeSdp-arm 	vlog(LOG_ERR, msg, ap);
128*819281eeSdp-arm 	va_end(ap);
129*819281eeSdp-arm 	exit(1);
130*819281eeSdp-arm }
131*819281eeSdp-arm 
132*819281eeSdp-arm static void add_image(image_t *image)
133*819281eeSdp-arm {
134*819281eeSdp-arm 	if (nr_images + 1 > MAX_IMAGES)
135*819281eeSdp-arm 		log_errx("Too many images");
136*819281eeSdp-arm 	images[nr_images++] = image;
137*819281eeSdp-arm }
138*819281eeSdp-arm 
139*819281eeSdp-arm static void free_image(image_t *image)
140*819281eeSdp-arm {
141*819281eeSdp-arm 	free(image->buffer);
142*819281eeSdp-arm 	free(image);
143*819281eeSdp-arm }
144*819281eeSdp-arm 
145*819281eeSdp-arm static void replace_image(image_t *image_dst, image_t *image_src)
146*819281eeSdp-arm {
147*819281eeSdp-arm 	int i;
148*819281eeSdp-arm 
149*819281eeSdp-arm 	for (i = 0; i < nr_images; i++) {
150*819281eeSdp-arm 		if (images[i] == image_dst) {
151*819281eeSdp-arm 			free_image(images[i]);
152*819281eeSdp-arm 			images[i] = image_src;
153*819281eeSdp-arm 			break;
154*819281eeSdp-arm 		}
155*819281eeSdp-arm 	}
156*819281eeSdp-arm 	assert(i != nr_images);
157*819281eeSdp-arm }
158*819281eeSdp-arm 
159*819281eeSdp-arm static void remove_image(image_t *image)
160*819281eeSdp-arm {
161*819281eeSdp-arm 	int i;
162*819281eeSdp-arm 
163*819281eeSdp-arm 	for (i = 0; i < nr_images; i++) {
164*819281eeSdp-arm 		if (images[i] == image) {
165*819281eeSdp-arm 			free_image(images[i]);
166*819281eeSdp-arm 			images[i] = NULL;
167*819281eeSdp-arm 			break;
168*819281eeSdp-arm 		}
169*819281eeSdp-arm 	}
170*819281eeSdp-arm 	assert(i != nr_images);
171*819281eeSdp-arm 
172*819281eeSdp-arm 	/* Compact array. */
173*819281eeSdp-arm 	memmove(&images[i], &images[i + 1],
174*819281eeSdp-arm 	    (nr_images - i - 1) * sizeof(*images));
175*819281eeSdp-arm 	nr_images--;
176*819281eeSdp-arm }
177*819281eeSdp-arm 
178*819281eeSdp-arm static void free_images(void)
179*819281eeSdp-arm {
180*819281eeSdp-arm 	int i;
181*819281eeSdp-arm 
182*819281eeSdp-arm 	for (i = 0; i < nr_images; i++) {
183*819281eeSdp-arm 		free_image(images[i]);
184*819281eeSdp-arm 		images[i] = NULL;
185*819281eeSdp-arm 	}
186*819281eeSdp-arm }
187*819281eeSdp-arm 
188*819281eeSdp-arm static toc_entry_t *get_entry_lookup_from_uuid(const uuid_t *uuid)
189*819281eeSdp-arm {
190*819281eeSdp-arm 	toc_entry_t *toc_entry = toc_entries;
191*819281eeSdp-arm 
192*819281eeSdp-arm 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
193*819281eeSdp-arm 		if (memcmp(&toc_entry->uuid, uuid, sizeof(uuid_t)) == 0)
194*819281eeSdp-arm 			return toc_entry;
195*819281eeSdp-arm 	return NULL;
196*819281eeSdp-arm }
197*819281eeSdp-arm 
198*819281eeSdp-arm static int parse_fip(char *filename, fip_toc_header_t *toc_header_out)
199*819281eeSdp-arm {
200*819281eeSdp-arm 	struct stat st;
201*819281eeSdp-arm 	FILE *fp;
202*819281eeSdp-arm 	char *buf, *bufend;
203*819281eeSdp-arm 	fip_toc_header_t *toc_header;
204*819281eeSdp-arm 	fip_toc_entry_t *toc_entry;
205*819281eeSdp-arm 	image_t *image;
206*819281eeSdp-arm 	int terminated = 0;
207*819281eeSdp-arm 
208*819281eeSdp-arm 	fp = fopen(filename, "r");
209*819281eeSdp-arm 	if (fp == NULL)
210*819281eeSdp-arm 		log_err("fopen %s", filename);
211*819281eeSdp-arm 
212*819281eeSdp-arm 	if (fstat(fileno(fp), &st) == -1)
213*819281eeSdp-arm 		log_err("fstat %s", filename);
214*819281eeSdp-arm 
215*819281eeSdp-arm 	buf = malloc(st.st_size);
216*819281eeSdp-arm 	if (buf == NULL)
217*819281eeSdp-arm 		log_err("malloc");
218*819281eeSdp-arm 
219*819281eeSdp-arm 	if (fread(buf, 1, st.st_size, fp) != st.st_size)
220*819281eeSdp-arm 		log_errx("Failed to read %s", filename);
221*819281eeSdp-arm 	bufend = buf + st.st_size;
222*819281eeSdp-arm 	fclose(fp);
223*819281eeSdp-arm 
224*819281eeSdp-arm 	if (st.st_size < sizeof(fip_toc_header_t))
225*819281eeSdp-arm 		log_errx("FIP %s is truncated", filename);
226*819281eeSdp-arm 
227*819281eeSdp-arm 	toc_header = (fip_toc_header_t *)buf;
228*819281eeSdp-arm 	toc_entry = (fip_toc_entry_t *)(toc_header + 1);
229*819281eeSdp-arm 
230*819281eeSdp-arm 	if (toc_header->name != TOC_HEADER_NAME)
231*819281eeSdp-arm 		log_errx("%s is not a FIP file", filename);
232*819281eeSdp-arm 
233*819281eeSdp-arm 	/* Return the ToC header if the caller wants it. */
234*819281eeSdp-arm 	if (toc_header_out != NULL)
235*819281eeSdp-arm 		*toc_header_out = *toc_header;
236*819281eeSdp-arm 
237*819281eeSdp-arm 	/* Walk through each ToC entry in the file. */
238*819281eeSdp-arm 	while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
239*819281eeSdp-arm 		/* Found the ToC terminator, we are done. */
240*819281eeSdp-arm 		if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
241*819281eeSdp-arm 			terminated = 1;
242*819281eeSdp-arm 			break;
243*819281eeSdp-arm 		}
244*819281eeSdp-arm 
245*819281eeSdp-arm 		/*
246*819281eeSdp-arm 		 * Build a new image out of the ToC entry and add it to the
247*819281eeSdp-arm 		 * table of images.
248*819281eeSdp-arm 		 */
249*819281eeSdp-arm 		image = malloc(sizeof(*image));
250*819281eeSdp-arm 		if (image == NULL)
251*819281eeSdp-arm 			log_err("malloc");
252*819281eeSdp-arm 
253*819281eeSdp-arm 		memcpy(&image->uuid, &toc_entry->uuid, sizeof(uuid_t));
254*819281eeSdp-arm 
255*819281eeSdp-arm 		image->buffer = malloc(toc_entry->size);
256*819281eeSdp-arm 		if (image->buffer == NULL)
257*819281eeSdp-arm 			log_err("malloc");
258*819281eeSdp-arm 
259*819281eeSdp-arm 		/* Overflow checks before memory copy. */
260*819281eeSdp-arm 		if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
261*819281eeSdp-arm 			log_errx("FIP %s is corrupted", filename);
262*819281eeSdp-arm 		if (toc_entry->size + toc_entry->offset_address > st.st_size)
263*819281eeSdp-arm 			log_errx("FIP %s is corrupted", filename);
264*819281eeSdp-arm 
265*819281eeSdp-arm 		memcpy(image->buffer, buf + toc_entry->offset_address,
266*819281eeSdp-arm 		    toc_entry->size);
267*819281eeSdp-arm 		image->size = toc_entry->size;
268*819281eeSdp-arm 
269*819281eeSdp-arm 		image->toc_entry = get_entry_lookup_from_uuid(&toc_entry->uuid);
270*819281eeSdp-arm 		if (image->toc_entry == NULL) {
271*819281eeSdp-arm 			add_image(image);
272*819281eeSdp-arm 			toc_entry++;
273*819281eeSdp-arm 			continue;
274*819281eeSdp-arm 		}
275*819281eeSdp-arm 
276*819281eeSdp-arm 		assert(image->toc_entry->image == NULL);
277*819281eeSdp-arm 		/* Link backpointer from lookup entry. */
278*819281eeSdp-arm 		image->toc_entry->image = image;
279*819281eeSdp-arm 		add_image(image);
280*819281eeSdp-arm 
281*819281eeSdp-arm 		toc_entry++;
282*819281eeSdp-arm 	}
283*819281eeSdp-arm 
284*819281eeSdp-arm 	if (terminated == 0)
285*819281eeSdp-arm 		log_errx("FIP %s does not have a ToC terminator entry",
286*819281eeSdp-arm 		    filename);
287*819281eeSdp-arm 	free(buf);
288*819281eeSdp-arm 	return 0;
289*819281eeSdp-arm }
290*819281eeSdp-arm 
291*819281eeSdp-arm static image_t *read_image_from_file(toc_entry_t *toc_entry, char *filename)
292*819281eeSdp-arm {
293*819281eeSdp-arm 	struct stat st;
294*819281eeSdp-arm 	image_t *image;
295*819281eeSdp-arm 	FILE *fp;
296*819281eeSdp-arm 
297*819281eeSdp-arm 	fp = fopen(filename, "r");
298*819281eeSdp-arm 	if (fp == NULL)
299*819281eeSdp-arm 		log_err("fopen %s", filename);
300*819281eeSdp-arm 
301*819281eeSdp-arm 	if (fstat(fileno(fp), &st) == -1)
302*819281eeSdp-arm 		log_errx("fstat %s", filename);
303*819281eeSdp-arm 
304*819281eeSdp-arm 	image = malloc(sizeof(*image));
305*819281eeSdp-arm 	if (image == NULL)
306*819281eeSdp-arm 		log_err("malloc");
307*819281eeSdp-arm 
308*819281eeSdp-arm 	memcpy(&image->uuid, &toc_entry->uuid, sizeof(uuid_t));
309*819281eeSdp-arm 
310*819281eeSdp-arm 	image->buffer = malloc(st.st_size);
311*819281eeSdp-arm 	if (image->buffer == NULL)
312*819281eeSdp-arm 		log_err("malloc");
313*819281eeSdp-arm 	if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
314*819281eeSdp-arm 		log_errx("Failed to read %s", filename);
315*819281eeSdp-arm 	image->size = st.st_size;
316*819281eeSdp-arm 	image->toc_entry = toc_entry;
317*819281eeSdp-arm 
318*819281eeSdp-arm 	fclose(fp);
319*819281eeSdp-arm 	return image;
320*819281eeSdp-arm }
321*819281eeSdp-arm 
322*819281eeSdp-arm static int write_image_to_file(image_t *image, char *filename)
323*819281eeSdp-arm {
324*819281eeSdp-arm 	FILE *fp;
325*819281eeSdp-arm 
326*819281eeSdp-arm 	fp = fopen(filename, "w");
327*819281eeSdp-arm 	if (fp == NULL)
328*819281eeSdp-arm 		log_err("fopen");
329*819281eeSdp-arm 	if (fwrite(image->buffer, 1, image->size, fp) != image->size)
330*819281eeSdp-arm 		log_errx("Failed to write %s", filename);
331*819281eeSdp-arm 	fclose(fp);
332*819281eeSdp-arm 	return 0;
333*819281eeSdp-arm }
334*819281eeSdp-arm 
335*819281eeSdp-arm static int fill_common_opts(struct option *opts, int has_arg)
336*819281eeSdp-arm {
337*819281eeSdp-arm 	int i;
338*819281eeSdp-arm 
339*819281eeSdp-arm 	for (i = 0; toc_entries[i].cmdline_name != NULL; i++) {
340*819281eeSdp-arm 		opts[i].name = toc_entries[i].cmdline_name;
341*819281eeSdp-arm 		opts[i].has_arg = has_arg;
342*819281eeSdp-arm 		opts[i].flag = NULL;
343*819281eeSdp-arm 		opts[i].val = 0;
344*819281eeSdp-arm 	}
345*819281eeSdp-arm 	return i;
346*819281eeSdp-arm }
347*819281eeSdp-arm 
348*819281eeSdp-arm static void add_opt(struct option *opts, int idx, char *name,
349*819281eeSdp-arm     int has_arg, int val)
350*819281eeSdp-arm {
351*819281eeSdp-arm 	opts[idx].name = name;
352*819281eeSdp-arm 	opts[idx].has_arg = has_arg;
353*819281eeSdp-arm 	opts[idx].flag = NULL;
354*819281eeSdp-arm 	opts[idx].val = val;
355*819281eeSdp-arm }
356*819281eeSdp-arm 
357*819281eeSdp-arm static int info_cmd(int argc, char *argv[])
358*819281eeSdp-arm {
359*819281eeSdp-arm 	image_t *image;
360*819281eeSdp-arm 	uint64_t image_offset;
361*819281eeSdp-arm 	uint64_t image_size = 0;
362*819281eeSdp-arm 	fip_toc_header_t toc_header;
363*819281eeSdp-arm 	int i;
364*819281eeSdp-arm 
365*819281eeSdp-arm 	if (argc != 2)
366*819281eeSdp-arm 		usage();
367*819281eeSdp-arm 	argc--, argv++;
368*819281eeSdp-arm 
369*819281eeSdp-arm 	parse_fip(argv[0], &toc_header);
370*819281eeSdp-arm 
371*819281eeSdp-arm 	if (verbose) {
372*819281eeSdp-arm 		log_dbgx("toc_header[name]: 0x%llX",
373*819281eeSdp-arm 		    (unsigned long long)toc_header.name);
374*819281eeSdp-arm 		log_dbgx("toc_header[serial_number]: 0x%llX",
375*819281eeSdp-arm 		    (unsigned long long)toc_header.serial_number);
376*819281eeSdp-arm 		log_dbgx("toc_header[flags]: 0x%llX",
377*819281eeSdp-arm 		    (unsigned long long)toc_header.flags);
378*819281eeSdp-arm 	}
379*819281eeSdp-arm 
380*819281eeSdp-arm 	image_offset = sizeof(fip_toc_header_t) +
381*819281eeSdp-arm 	    (sizeof(fip_toc_entry_t) * (nr_images + 1));
382*819281eeSdp-arm 
383*819281eeSdp-arm 	for (i = 0; i < nr_images; i++) {
384*819281eeSdp-arm 		image = images[i];
385*819281eeSdp-arm 		if (image->toc_entry != NULL)
386*819281eeSdp-arm 			printf("%s: ", image->toc_entry->name);
387*819281eeSdp-arm 		else
388*819281eeSdp-arm 			printf("Unknown entry: ");
389*819281eeSdp-arm 		image_size = image->size;
390*819281eeSdp-arm 		printf("offset=0x%llX, size=0x%llX",
391*819281eeSdp-arm 		    (unsigned long long)image_offset,
392*819281eeSdp-arm 		    (unsigned long long)image_size);
393*819281eeSdp-arm 		if (image->toc_entry != NULL)
394*819281eeSdp-arm 			printf(", cmdline=\"--%s\"\n",
395*819281eeSdp-arm 			    image->toc_entry->cmdline_name);
396*819281eeSdp-arm 		else
397*819281eeSdp-arm 			putchar('\n');
398*819281eeSdp-arm 		image_offset += image_size;
399*819281eeSdp-arm 	}
400*819281eeSdp-arm 
401*819281eeSdp-arm 	free_images();
402*819281eeSdp-arm 	return 0;
403*819281eeSdp-arm }
404*819281eeSdp-arm 
405*819281eeSdp-arm static void info_usage(void)
406*819281eeSdp-arm {
407*819281eeSdp-arm 	printf("fiptool info FIP_FILENAME\n");
408*819281eeSdp-arm }
409*819281eeSdp-arm 
410*819281eeSdp-arm static int pack_images(char *filename, uint64_t toc_flags)
411*819281eeSdp-arm {
412*819281eeSdp-arm 	FILE *fp;
413*819281eeSdp-arm 	image_t *image;
414*819281eeSdp-arm 	fip_toc_header_t *toc_header;
415*819281eeSdp-arm 	fip_toc_entry_t *toc_entry;
416*819281eeSdp-arm 	char *buf;
417*819281eeSdp-arm 	uint64_t entry_offset, buf_size, payload_size;
418*819281eeSdp-arm 	int i;
419*819281eeSdp-arm 
420*819281eeSdp-arm 	/* Calculate total payload size and allocate scratch buffer. */
421*819281eeSdp-arm 	payload_size = 0;
422*819281eeSdp-arm 	for (i = 0; i < nr_images; i++)
423*819281eeSdp-arm 		payload_size += images[i]->size;
424*819281eeSdp-arm 
425*819281eeSdp-arm 	buf_size = sizeof(fip_toc_header_t) +
426*819281eeSdp-arm 	    sizeof(fip_toc_entry_t) * (nr_images + 1);
427*819281eeSdp-arm 	buf = calloc(1, buf_size);
428*819281eeSdp-arm 	if (buf == NULL)
429*819281eeSdp-arm 		log_err("calloc");
430*819281eeSdp-arm 
431*819281eeSdp-arm 	/* Build up header and ToC entries from the image table. */
432*819281eeSdp-arm 	toc_header = (fip_toc_header_t *)buf;
433*819281eeSdp-arm 	toc_header->name = TOC_HEADER_NAME;
434*819281eeSdp-arm 	toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
435*819281eeSdp-arm 	toc_header->flags = toc_flags;
436*819281eeSdp-arm 
437*819281eeSdp-arm 	toc_entry = (fip_toc_entry_t *)(toc_header + 1);
438*819281eeSdp-arm 
439*819281eeSdp-arm 	entry_offset = buf_size;
440*819281eeSdp-arm 	for (i = 0; i < nr_images; i++) {
441*819281eeSdp-arm 		image = images[i];
442*819281eeSdp-arm 		memcpy(&toc_entry->uuid, &image->uuid, sizeof(uuid_t));
443*819281eeSdp-arm 		toc_entry->offset_address = entry_offset;
444*819281eeSdp-arm 		toc_entry->size = image->size;
445*819281eeSdp-arm 		toc_entry->flags = 0;
446*819281eeSdp-arm 		entry_offset += toc_entry->size;
447*819281eeSdp-arm 		toc_entry++;
448*819281eeSdp-arm 	}
449*819281eeSdp-arm 
450*819281eeSdp-arm 	/* Append a null uuid entry to mark the end of ToC entries. */
451*819281eeSdp-arm 	memcpy(&toc_entry->uuid, &uuid_null, sizeof(uuid_t));
452*819281eeSdp-arm 	toc_entry->offset_address = entry_offset;
453*819281eeSdp-arm 	toc_entry->size = 0;
454*819281eeSdp-arm 	toc_entry->flags = 0;
455*819281eeSdp-arm 
456*819281eeSdp-arm 	/* Generate the FIP file. */
457*819281eeSdp-arm 	fp = fopen(filename, "w");
458*819281eeSdp-arm 	if (fp == NULL)
459*819281eeSdp-arm 		log_err("fopen %s", filename);
460*819281eeSdp-arm 
461*819281eeSdp-arm 	if (verbose)
462*819281eeSdp-arm 		log_dbgx("Metadata size: %zu bytes", buf_size);
463*819281eeSdp-arm 
464*819281eeSdp-arm 	if (fwrite(buf, 1, buf_size, fp) != buf_size)
465*819281eeSdp-arm 		log_errx("Failed to write image to %s", filename);
466*819281eeSdp-arm 	free(buf);
467*819281eeSdp-arm 
468*819281eeSdp-arm 	if (verbose)
469*819281eeSdp-arm 		log_dbgx("Payload size: %zu bytes", payload_size);
470*819281eeSdp-arm 
471*819281eeSdp-arm 	for (i = 0; i < nr_images; i++) {
472*819281eeSdp-arm 		image = images[i];
473*819281eeSdp-arm 		if (fwrite(image->buffer, 1, image->size, fp) != image->size)
474*819281eeSdp-arm 			log_errx("Failed to write image to %s", filename);
475*819281eeSdp-arm 	}
476*819281eeSdp-arm 
477*819281eeSdp-arm 	fclose(fp);
478*819281eeSdp-arm 	return 0;
479*819281eeSdp-arm }
480*819281eeSdp-arm 
481*819281eeSdp-arm /*
482*819281eeSdp-arm  * This function is shared between the create and update subcommands.
483*819281eeSdp-arm  * The difference between the two subcommands is that when the FIP file
484*819281eeSdp-arm  * is created, the parsing of an existing FIP is skipped.  This results
485*819281eeSdp-arm  * in update_fip() creating the new FIP file from scratch because the
486*819281eeSdp-arm  * internal image table is not populated.
487*819281eeSdp-arm  */
488*819281eeSdp-arm static void update_fip(void)
489*819281eeSdp-arm {
490*819281eeSdp-arm 	toc_entry_t *toc_entry;
491*819281eeSdp-arm 	image_t *image;
492*819281eeSdp-arm 
493*819281eeSdp-arm 	/* Add or replace images in the FIP file. */
494*819281eeSdp-arm 	for (toc_entry = toc_entries;
495*819281eeSdp-arm 	     toc_entry->cmdline_name != NULL;
496*819281eeSdp-arm 	     toc_entry++) {
497*819281eeSdp-arm 		if (toc_entry->action != DO_PACK)
498*819281eeSdp-arm 			continue;
499*819281eeSdp-arm 
500*819281eeSdp-arm 		image = read_image_from_file(toc_entry, toc_entry->action_arg);
501*819281eeSdp-arm 		if (toc_entry->image != NULL) {
502*819281eeSdp-arm 			if (verbose)
503*819281eeSdp-arm 				log_dbgx("Replacing image %s.bin with %s",
504*819281eeSdp-arm 				    toc_entry->cmdline_name,
505*819281eeSdp-arm 				    toc_entry->action_arg);
506*819281eeSdp-arm 			replace_image(toc_entry->image, image);
507*819281eeSdp-arm 		} else {
508*819281eeSdp-arm 			if (verbose)
509*819281eeSdp-arm 				log_dbgx("Adding image %s",
510*819281eeSdp-arm 				    toc_entry->action_arg);
511*819281eeSdp-arm 			add_image(image);
512*819281eeSdp-arm 		}
513*819281eeSdp-arm 		/* Link backpointer from lookup entry. */
514*819281eeSdp-arm 		toc_entry->image = image;
515*819281eeSdp-arm 
516*819281eeSdp-arm 		free(toc_entry->action_arg);
517*819281eeSdp-arm 		toc_entry->action_arg = NULL;
518*819281eeSdp-arm 	}
519*819281eeSdp-arm }
520*819281eeSdp-arm 
521*819281eeSdp-arm static void parse_plat_toc_flags(char *arg, unsigned long long *toc_flags)
522*819281eeSdp-arm {
523*819281eeSdp-arm 	unsigned long long flags;
524*819281eeSdp-arm 	char *endptr;
525*819281eeSdp-arm 
526*819281eeSdp-arm 	errno = 0;
527*819281eeSdp-arm 	flags = strtoull(arg, &endptr, 16);
528*819281eeSdp-arm 	if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
529*819281eeSdp-arm 		log_errx("Invalid platform ToC flags: %s", arg);
530*819281eeSdp-arm 	/* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
531*819281eeSdp-arm 	*toc_flags |= flags << 32;
532*819281eeSdp-arm }
533*819281eeSdp-arm 
534*819281eeSdp-arm static int create_cmd(int argc, char *argv[])
535*819281eeSdp-arm {
536*819281eeSdp-arm 	struct option opts[toc_entries_len + 1];
537*819281eeSdp-arm 	unsigned long long toc_flags = 0;
538*819281eeSdp-arm 	int i;
539*819281eeSdp-arm 
540*819281eeSdp-arm 	if (argc < 2)
541*819281eeSdp-arm 		usage();
542*819281eeSdp-arm 
543*819281eeSdp-arm 	i = fill_common_opts(opts, required_argument);
544*819281eeSdp-arm 	add_opt(opts, i, "plat-toc-flags", required_argument,
545*819281eeSdp-arm 	    OPT_PLAT_TOC_FLAGS);
546*819281eeSdp-arm 	add_opt(opts, ++i, NULL, 0, 0);
547*819281eeSdp-arm 
548*819281eeSdp-arm 	while (1) {
549*819281eeSdp-arm 		int c, opt_index;
550*819281eeSdp-arm 
551*819281eeSdp-arm 		c = getopt_long(argc, argv, "o:", opts, &opt_index);
552*819281eeSdp-arm 		if (c == -1)
553*819281eeSdp-arm 			break;
554*819281eeSdp-arm 
555*819281eeSdp-arm 		switch (c) {
556*819281eeSdp-arm 		case OPT_TOC_ENTRY: {
557*819281eeSdp-arm 			toc_entry_t *toc_entry;
558*819281eeSdp-arm 
559*819281eeSdp-arm 			toc_entry = &toc_entries[opt_index];
560*819281eeSdp-arm 			toc_entry->action = DO_PACK;
561*819281eeSdp-arm 			toc_entry->action_arg = strdup(optarg);
562*819281eeSdp-arm 			if (toc_entry->action_arg == NULL)
563*819281eeSdp-arm 				log_err("strdup");
564*819281eeSdp-arm 			break;
565*819281eeSdp-arm 		}
566*819281eeSdp-arm 		case OPT_PLAT_TOC_FLAGS:
567*819281eeSdp-arm 			parse_plat_toc_flags(optarg, &toc_flags);
568*819281eeSdp-arm 			break;
569*819281eeSdp-arm 		default:
570*819281eeSdp-arm 			usage();
571*819281eeSdp-arm 		}
572*819281eeSdp-arm 	}
573*819281eeSdp-arm 	argc -= optind;
574*819281eeSdp-arm 	argv += optind;
575*819281eeSdp-arm 
576*819281eeSdp-arm 	if (argc == 0)
577*819281eeSdp-arm 		usage();
578*819281eeSdp-arm 
579*819281eeSdp-arm 	update_fip();
580*819281eeSdp-arm 
581*819281eeSdp-arm 	pack_images(argv[0], toc_flags);
582*819281eeSdp-arm 	free_images();
583*819281eeSdp-arm 	return 0;
584*819281eeSdp-arm }
585*819281eeSdp-arm 
586*819281eeSdp-arm static void create_usage(void)
587*819281eeSdp-arm {
588*819281eeSdp-arm 	toc_entry_t *toc_entry = toc_entries;
589*819281eeSdp-arm 
590*819281eeSdp-arm 	printf("fiptfool create [--plat-toc-flags <value>] [opts] FIP_FILENAME\n");
591*819281eeSdp-arm 	printf("  --plat-toc-flags <value>\t16-bit platform specific flag field "
592*819281eeSdp-arm 	    "occupying bits 32-47 in 64-bit ToC header.\n");
593*819281eeSdp-arm 	fputc('\n', stderr);
594*819281eeSdp-arm 	printf("Specific images are packed with the following options:\n");
595*819281eeSdp-arm 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
596*819281eeSdp-arm 		printf("  --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
597*819281eeSdp-arm 		    toc_entry->name);
598*819281eeSdp-arm }
599*819281eeSdp-arm 
600*819281eeSdp-arm static int update_cmd(int argc, char *argv[])
601*819281eeSdp-arm {
602*819281eeSdp-arm 	struct option opts[toc_entries_len + 2];
603*819281eeSdp-arm 	char outfile[FILENAME_MAX] = { 0 };
604*819281eeSdp-arm 	fip_toc_header_t toc_header = { 0 };
605*819281eeSdp-arm 	unsigned long long toc_flags = 0;
606*819281eeSdp-arm 	int pflag = 0;
607*819281eeSdp-arm 	int i;
608*819281eeSdp-arm 
609*819281eeSdp-arm 	if (argc < 2)
610*819281eeSdp-arm 		usage();
611*819281eeSdp-arm 
612*819281eeSdp-arm 	i = fill_common_opts(opts, required_argument);
613*819281eeSdp-arm 	add_opt(opts, i, "out", required_argument, 'o');
614*819281eeSdp-arm 	add_opt(opts, ++i, "plat-toc-flags", required_argument,
615*819281eeSdp-arm 	    OPT_PLAT_TOC_FLAGS);
616*819281eeSdp-arm 	add_opt(opts, ++i, NULL, 0, 0);
617*819281eeSdp-arm 
618*819281eeSdp-arm 	while (1) {
619*819281eeSdp-arm 		int c, opt_index;
620*819281eeSdp-arm 
621*819281eeSdp-arm 		c = getopt_long(argc, argv, "o:", opts, &opt_index);
622*819281eeSdp-arm 		if (c == -1)
623*819281eeSdp-arm 			break;
624*819281eeSdp-arm 
625*819281eeSdp-arm 		switch (c) {
626*819281eeSdp-arm 		case OPT_TOC_ENTRY: {
627*819281eeSdp-arm 			toc_entry_t *toc_entry;
628*819281eeSdp-arm 
629*819281eeSdp-arm 			toc_entry = &toc_entries[opt_index];
630*819281eeSdp-arm 			toc_entry->action = DO_PACK;
631*819281eeSdp-arm 			toc_entry->action_arg = strdup(optarg);
632*819281eeSdp-arm 			if (toc_entry->action_arg == NULL)
633*819281eeSdp-arm 				log_err("strdup");
634*819281eeSdp-arm 			break;
635*819281eeSdp-arm 		}
636*819281eeSdp-arm 		case OPT_PLAT_TOC_FLAGS: {
637*819281eeSdp-arm 			parse_plat_toc_flags(optarg, &toc_flags);
638*819281eeSdp-arm 			pflag = 1;
639*819281eeSdp-arm 			break;
640*819281eeSdp-arm 		}
641*819281eeSdp-arm 		case 'o':
642*819281eeSdp-arm 			snprintf(outfile, sizeof(outfile), "%s", optarg);
643*819281eeSdp-arm 			break;
644*819281eeSdp-arm 		default:
645*819281eeSdp-arm 			usage();
646*819281eeSdp-arm 		}
647*819281eeSdp-arm 	}
648*819281eeSdp-arm 	argc -= optind;
649*819281eeSdp-arm 	argv += optind;
650*819281eeSdp-arm 
651*819281eeSdp-arm 	if (argc == 0)
652*819281eeSdp-arm 		usage();
653*819281eeSdp-arm 
654*819281eeSdp-arm 	if (outfile[0] == '\0')
655*819281eeSdp-arm 		snprintf(outfile, sizeof(outfile), "%s", argv[0]);
656*819281eeSdp-arm 
657*819281eeSdp-arm 	if (access(outfile, F_OK) == 0)
658*819281eeSdp-arm 		parse_fip(argv[0], &toc_header);
659*819281eeSdp-arm 
660*819281eeSdp-arm 	if (pflag)
661*819281eeSdp-arm 		toc_header.flags &= ~(0xffffULL << 32);
662*819281eeSdp-arm 	toc_flags = (toc_header.flags |= toc_flags);
663*819281eeSdp-arm 
664*819281eeSdp-arm 	update_fip();
665*819281eeSdp-arm 
666*819281eeSdp-arm 	pack_images(outfile, toc_flags);
667*819281eeSdp-arm 	free_images();
668*819281eeSdp-arm 	return 0;
669*819281eeSdp-arm }
670*819281eeSdp-arm 
671*819281eeSdp-arm static void update_usage(void)
672*819281eeSdp-arm {
673*819281eeSdp-arm 	toc_entry_t *toc_entry = toc_entries;
674*819281eeSdp-arm 
675*819281eeSdp-arm 	printf("fiptfool update [--out FIP_FILENAME] "
676*819281eeSdp-arm 	    "[--plat-toc-flags <value>] [opts] FIP_FILENAME\n");
677*819281eeSdp-arm 	printf("  --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
678*819281eeSdp-arm 	printf("  --plat-toc-flags <value>\t16-bit platform specific flag field "
679*819281eeSdp-arm 	    "occupying bits 32-47 in 64-bit ToC header.\n");
680*819281eeSdp-arm 	fputc('\n', stderr);
681*819281eeSdp-arm 	printf("Specific images are packed with the following options:\n");
682*819281eeSdp-arm 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
683*819281eeSdp-arm 		printf("  --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
684*819281eeSdp-arm 		    toc_entry->name);
685*819281eeSdp-arm }
686*819281eeSdp-arm 
687*819281eeSdp-arm static int unpack_cmd(int argc, char *argv[])
688*819281eeSdp-arm {
689*819281eeSdp-arm 	struct option opts[toc_entries_len + 3];
690*819281eeSdp-arm 	char file[FILENAME_MAX], outdir[PATH_MAX] = { 0 };
691*819281eeSdp-arm 	toc_entry_t *toc_entry;
692*819281eeSdp-arm 	int fflag = 0;
693*819281eeSdp-arm 	int unpack_all = 1;
694*819281eeSdp-arm 	int i;
695*819281eeSdp-arm 
696*819281eeSdp-arm 	if (argc < 2)
697*819281eeSdp-arm 		usage();
698*819281eeSdp-arm 
699*819281eeSdp-arm 	i = fill_common_opts(opts, required_argument);
700*819281eeSdp-arm 	add_opt(opts, i, "force", no_argument, 'f');
701*819281eeSdp-arm 	add_opt(opts, ++i, "out", required_argument, 'o');
702*819281eeSdp-arm 	add_opt(opts, ++i, NULL, 0, 0);
703*819281eeSdp-arm 
704*819281eeSdp-arm 	while (1) {
705*819281eeSdp-arm 		int c, opt_index;
706*819281eeSdp-arm 
707*819281eeSdp-arm 		c = getopt_long(argc, argv, "fo:", opts, &opt_index);
708*819281eeSdp-arm 		if (c == -1)
709*819281eeSdp-arm 			break;
710*819281eeSdp-arm 
711*819281eeSdp-arm 		switch (c) {
712*819281eeSdp-arm 		case OPT_TOC_ENTRY:
713*819281eeSdp-arm 			unpack_all = 0;
714*819281eeSdp-arm 			toc_entry = &toc_entries[opt_index];
715*819281eeSdp-arm 			toc_entry->action = DO_UNPACK;
716*819281eeSdp-arm 			toc_entry->action_arg = strdup(optarg);
717*819281eeSdp-arm 			if (toc_entry->action_arg == NULL)
718*819281eeSdp-arm 				log_err("strdup");
719*819281eeSdp-arm 			break;
720*819281eeSdp-arm 		case 'f':
721*819281eeSdp-arm 			fflag = 1;
722*819281eeSdp-arm 			break;
723*819281eeSdp-arm 		case 'o':
724*819281eeSdp-arm 			snprintf(outdir, sizeof(outdir), "%s", optarg);
725*819281eeSdp-arm 			break;
726*819281eeSdp-arm 		default:
727*819281eeSdp-arm 			usage();
728*819281eeSdp-arm 		}
729*819281eeSdp-arm 	}
730*819281eeSdp-arm 	argc -= optind;
731*819281eeSdp-arm 	argv += optind;
732*819281eeSdp-arm 
733*819281eeSdp-arm 	if (argc == 0)
734*819281eeSdp-arm 		usage();
735*819281eeSdp-arm 
736*819281eeSdp-arm 	parse_fip(argv[0], NULL);
737*819281eeSdp-arm 
738*819281eeSdp-arm 	if (outdir[0] != '\0')
739*819281eeSdp-arm 		if (chdir(outdir) == -1)
740*819281eeSdp-arm 			log_err("chdir %s", outdir);
741*819281eeSdp-arm 
742*819281eeSdp-arm 	/* Mark all images to be unpacked. */
743*819281eeSdp-arm 	if (unpack_all) {
744*819281eeSdp-arm 		for (toc_entry = toc_entries;
745*819281eeSdp-arm 		     toc_entry->cmdline_name != NULL;
746*819281eeSdp-arm 		     toc_entry++) {
747*819281eeSdp-arm 			if (toc_entry->image != NULL) {
748*819281eeSdp-arm 				toc_entry->action = DO_UNPACK;
749*819281eeSdp-arm 				toc_entry->action_arg = NULL;
750*819281eeSdp-arm 			}
751*819281eeSdp-arm 		}
752*819281eeSdp-arm 	}
753*819281eeSdp-arm 
754*819281eeSdp-arm 	/* Unpack all specified images. */
755*819281eeSdp-arm 	for (toc_entry = toc_entries;
756*819281eeSdp-arm 	     toc_entry->cmdline_name != NULL;
757*819281eeSdp-arm 	     toc_entry++) {
758*819281eeSdp-arm 		if (toc_entry->action != DO_UNPACK)
759*819281eeSdp-arm 			continue;
760*819281eeSdp-arm 
761*819281eeSdp-arm 		/* Build filename. */
762*819281eeSdp-arm 		if (toc_entry->action_arg == NULL)
763*819281eeSdp-arm 			snprintf(file, sizeof(file), "%s.bin",
764*819281eeSdp-arm 			    toc_entry->cmdline_name);
765*819281eeSdp-arm 		else
766*819281eeSdp-arm 			snprintf(file, sizeof(file), "%s",
767*819281eeSdp-arm 			    toc_entry->action_arg);
768*819281eeSdp-arm 
769*819281eeSdp-arm 		if (toc_entry->image == NULL) {
770*819281eeSdp-arm 			log_warnx("Requested image %s is not in %s",
771*819281eeSdp-arm 			    file, argv[0]);
772*819281eeSdp-arm 			free(toc_entry->action_arg);
773*819281eeSdp-arm 			toc_entry->action_arg = NULL;
774*819281eeSdp-arm 			continue;
775*819281eeSdp-arm 		}
776*819281eeSdp-arm 
777*819281eeSdp-arm 		if (access(file, F_OK) != 0 || fflag) {
778*819281eeSdp-arm 			if (verbose)
779*819281eeSdp-arm 				log_dbgx("Unpacking %s", file);
780*819281eeSdp-arm 			write_image_to_file(toc_entry->image, file);
781*819281eeSdp-arm 		} else {
782*819281eeSdp-arm 			log_warnx("File %s already exists, use --force to overwrite it",
783*819281eeSdp-arm 			    file);
784*819281eeSdp-arm 		}
785*819281eeSdp-arm 
786*819281eeSdp-arm 		free(toc_entry->action_arg);
787*819281eeSdp-arm 		toc_entry->action_arg = NULL;
788*819281eeSdp-arm 	}
789*819281eeSdp-arm 
790*819281eeSdp-arm 	free_images();
791*819281eeSdp-arm 	return 0;
792*819281eeSdp-arm }
793*819281eeSdp-arm 
794*819281eeSdp-arm static void unpack_usage(void)
795*819281eeSdp-arm {
796*819281eeSdp-arm 	toc_entry_t *toc_entry = toc_entries;
797*819281eeSdp-arm 
798*819281eeSdp-arm 	printf("fiptool unpack [--force] [--out <path>] [opts] FIP_FILENAME\n");
799*819281eeSdp-arm 	printf("  --force\tIf the output file already exists, use --force to "
800*819281eeSdp-arm 	    "overwrite it.\n");
801*819281eeSdp-arm 	printf("  --out path\tSet the output directory path.\n");
802*819281eeSdp-arm 	fputc('\n', stderr);
803*819281eeSdp-arm 	printf("Specific images are unpacked with the following options:\n");
804*819281eeSdp-arm 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
805*819281eeSdp-arm 		printf("  --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
806*819281eeSdp-arm 		    toc_entry->name);
807*819281eeSdp-arm 	fputc('\n', stderr);
808*819281eeSdp-arm 	printf("If no options are provided, all images will be unpacked.\n");
809*819281eeSdp-arm }
810*819281eeSdp-arm 
811*819281eeSdp-arm static int remove_cmd(int argc, char *argv[])
812*819281eeSdp-arm {
813*819281eeSdp-arm 	struct option opts[toc_entries_len + 2];
814*819281eeSdp-arm 	char outfile[FILENAME_MAX] = { 0 };
815*819281eeSdp-arm 	fip_toc_header_t toc_header;
816*819281eeSdp-arm 	toc_entry_t *toc_entry;
817*819281eeSdp-arm 	int fflag = 0;
818*819281eeSdp-arm 	int i;
819*819281eeSdp-arm 
820*819281eeSdp-arm 	if (argc < 2)
821*819281eeSdp-arm 		usage();
822*819281eeSdp-arm 
823*819281eeSdp-arm 	i = fill_common_opts(opts, no_argument);
824*819281eeSdp-arm 	add_opt(opts, i, "force", no_argument, 'f');
825*819281eeSdp-arm 	add_opt(opts, ++i, "out", required_argument, 'o');
826*819281eeSdp-arm 	add_opt(opts, ++i, NULL, 0, 0);
827*819281eeSdp-arm 
828*819281eeSdp-arm 	while (1) {
829*819281eeSdp-arm 		int c, opt_index;
830*819281eeSdp-arm 
831*819281eeSdp-arm 		c = getopt_long(argc, argv, "fo:", opts, &opt_index);
832*819281eeSdp-arm 		if (c == -1)
833*819281eeSdp-arm 			break;
834*819281eeSdp-arm 
835*819281eeSdp-arm 		switch (c) {
836*819281eeSdp-arm 		case OPT_TOC_ENTRY:
837*819281eeSdp-arm 			toc_entry = &toc_entries[opt_index];
838*819281eeSdp-arm 			toc_entry->action = DO_REMOVE;
839*819281eeSdp-arm 			break;
840*819281eeSdp-arm 		case 'f':
841*819281eeSdp-arm 			fflag = 1;
842*819281eeSdp-arm 			break;
843*819281eeSdp-arm 		case 'o':
844*819281eeSdp-arm 			snprintf(outfile, sizeof(outfile), "%s", optarg);
845*819281eeSdp-arm 			break;
846*819281eeSdp-arm 		default:
847*819281eeSdp-arm 			usage();
848*819281eeSdp-arm 		}
849*819281eeSdp-arm 	}
850*819281eeSdp-arm 	argc -= optind;
851*819281eeSdp-arm 	argv += optind;
852*819281eeSdp-arm 
853*819281eeSdp-arm 	if (argc == 0)
854*819281eeSdp-arm 		usage();
855*819281eeSdp-arm 
856*819281eeSdp-arm 	if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
857*819281eeSdp-arm 		log_errx("File %s already exists, use --force to overwrite it",
858*819281eeSdp-arm 		    outfile);
859*819281eeSdp-arm 
860*819281eeSdp-arm 	if (outfile[0] == '\0')
861*819281eeSdp-arm 		snprintf(outfile, sizeof(outfile), "%s", argv[0]);
862*819281eeSdp-arm 
863*819281eeSdp-arm 	parse_fip(argv[0], &toc_header);
864*819281eeSdp-arm 
865*819281eeSdp-arm 	for (toc_entry = toc_entries;
866*819281eeSdp-arm 	     toc_entry->cmdline_name != NULL;
867*819281eeSdp-arm 	     toc_entry++) {
868*819281eeSdp-arm 		if (toc_entry->action != DO_REMOVE)
869*819281eeSdp-arm 			continue;
870*819281eeSdp-arm 		if (toc_entry->image != NULL) {
871*819281eeSdp-arm 			if (verbose)
872*819281eeSdp-arm 				log_dbgx("Removing %s.bin",
873*819281eeSdp-arm 				    toc_entry->cmdline_name);
874*819281eeSdp-arm 			remove_image(toc_entry->image);
875*819281eeSdp-arm 		} else {
876*819281eeSdp-arm 			log_warnx("Requested image %s.bin is not in %s",
877*819281eeSdp-arm 			    toc_entry->cmdline_name, argv[0]);
878*819281eeSdp-arm 		}
879*819281eeSdp-arm 	}
880*819281eeSdp-arm 
881*819281eeSdp-arm 	pack_images(outfile, toc_header.flags);
882*819281eeSdp-arm 	free_images();
883*819281eeSdp-arm 	return 0;
884*819281eeSdp-arm }
885*819281eeSdp-arm 
886*819281eeSdp-arm static void remove_usage(void)
887*819281eeSdp-arm {
888*819281eeSdp-arm 	toc_entry_t *toc_entry = toc_entries;
889*819281eeSdp-arm 
890*819281eeSdp-arm 	printf("fiptool remove [--force] [--out FIP_FILENAME] [opts] FIP_FILENAME\n");
891*819281eeSdp-arm 	printf("  --force\t\tIf the output FIP file already exists, use --force to "
892*819281eeSdp-arm 	    "overwrite it.\n");
893*819281eeSdp-arm 	printf("  --out FIP_FILENAME\tSet an alternative output FIP file.\n");
894*819281eeSdp-arm 	fputc('\n', stderr);
895*819281eeSdp-arm 	printf("Specific images are removed with the following options:\n");
896*819281eeSdp-arm 	for (; toc_entry->cmdline_name != NULL; toc_entry++)
897*819281eeSdp-arm 		printf("  --%-16s\t%s\n", toc_entry->cmdline_name,
898*819281eeSdp-arm 		    toc_entry->name);
899*819281eeSdp-arm }
900*819281eeSdp-arm 
901*819281eeSdp-arm static int version_cmd(int argc, char *argv[])
902*819281eeSdp-arm {
903*819281eeSdp-arm #ifdef VERSION
904*819281eeSdp-arm 	puts(VERSION);
905*819281eeSdp-arm #else
906*819281eeSdp-arm 	/* If built from fiptool directory, VERSION is not set. */
907*819281eeSdp-arm 	puts("Unknown version");
908*819281eeSdp-arm #endif
909*819281eeSdp-arm 	return 0;
910*819281eeSdp-arm }
911*819281eeSdp-arm 
912*819281eeSdp-arm static void version_usage(void)
913*819281eeSdp-arm {
914*819281eeSdp-arm 	printf("fiptool version\n");
915*819281eeSdp-arm }
916*819281eeSdp-arm 
917*819281eeSdp-arm static int help_cmd(int argc, char *argv[])
918*819281eeSdp-arm {
919*819281eeSdp-arm 	int i;
920*819281eeSdp-arm 
921*819281eeSdp-arm 	if (argc < 2)
922*819281eeSdp-arm 		usage();
923*819281eeSdp-arm 	argc--, argv++;
924*819281eeSdp-arm 
925*819281eeSdp-arm 	for (i = 0; i < NELEM(cmds); i++) {
926*819281eeSdp-arm 		if (strcmp(cmds[i].name, argv[0]) == 0 &&
927*819281eeSdp-arm 		    cmds[i].usage != NULL) {
928*819281eeSdp-arm 			cmds[i].usage();
929*819281eeSdp-arm 			break;
930*819281eeSdp-arm 		}
931*819281eeSdp-arm 	}
932*819281eeSdp-arm 	if (i == NELEM(cmds))
933*819281eeSdp-arm 		printf("No help for subcommand '%s'\n", argv[0]);
934*819281eeSdp-arm 	return 0;
935*819281eeSdp-arm }
936*819281eeSdp-arm 
937*819281eeSdp-arm static void usage(void)
938*819281eeSdp-arm {
939*819281eeSdp-arm 	printf("usage: [--verbose] fiptool <command> [<args>]\n");
940*819281eeSdp-arm 	printf("Global options supported:\n");
941*819281eeSdp-arm 	printf("  --verbose\tEnable verbose output for all commands.\n");
942*819281eeSdp-arm 	fputc('\n', stderr);
943*819281eeSdp-arm 	printf("Commands supported:\n");
944*819281eeSdp-arm 	printf("  info\t\tList images contained in FIP.\n");
945*819281eeSdp-arm 	printf("  create\tCreate a new FIP with the given images.\n");
946*819281eeSdp-arm 	printf("  update\tUpdate an existing FIP with the given images.\n");
947*819281eeSdp-arm 	printf("  unpack\tUnpack images from FIP.\n");
948*819281eeSdp-arm 	printf("  remove\tRemove images from FIP.\n");
949*819281eeSdp-arm 	printf("  version\tShow fiptool version.\n");
950*819281eeSdp-arm 	printf("  help\t\tShow help for given command.\n");
951*819281eeSdp-arm 	exit(1);
952*819281eeSdp-arm }
953*819281eeSdp-arm 
954*819281eeSdp-arm int main(int argc, char *argv[])
955*819281eeSdp-arm {
956*819281eeSdp-arm 	int i, ret = 0;
957*819281eeSdp-arm 
958*819281eeSdp-arm 	if (argc < 2)
959*819281eeSdp-arm 		usage();
960*819281eeSdp-arm 	argc--, argv++;
961*819281eeSdp-arm 
962*819281eeSdp-arm 	if (strcmp(argv[0], "-v") == 0 ||
963*819281eeSdp-arm 	    strcmp(argv[0], "--verbose") == 0) {
964*819281eeSdp-arm 		verbose = 1;
965*819281eeSdp-arm 		argc--, argv++;
966*819281eeSdp-arm 	}
967*819281eeSdp-arm 
968*819281eeSdp-arm 	for (i = 0; i < NELEM(cmds); i++) {
969*819281eeSdp-arm 		if (strcmp(cmds[i].name, argv[0]) == 0) {
970*819281eeSdp-arm 			ret = cmds[i].handler(argc, argv);
971*819281eeSdp-arm 			break;
972*819281eeSdp-arm 		}
973*819281eeSdp-arm 	}
974*819281eeSdp-arm 	if (i == NELEM(cmds))
975*819281eeSdp-arm 		usage();
976*819281eeSdp-arm 	return ret;
977*819281eeSdp-arm }
978