blob: 5c240b56cc8c1f5baeca10374c4e4883f143d7cc [file] [log] [blame]
dp-arm4972ec52016-05-25 16:20:20 +01001/*
Masahiro Yamadae3a2b312017-05-08 18:29:03 +09002 * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
dp-arm4972ec52016-05-25 16:20:20 +01003 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
dp-arm4972ec52016-05-25 16:20:20 +01005 */
6
7#include <sys/types.h>
8#include <sys/stat.h>
9
10#include <assert.h>
11#include <errno.h>
dp-arm4972ec52016-05-25 16:20:20 +010012#include <limits.h>
13#include <stdarg.h>
14#include <stdint.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
Masahiro Yamadae3a2b312017-05-08 18:29:03 +090018
dp-arm4972ec52016-05-25 16:20:20 +010019#include "fiptool.h"
dp-arm4972ec52016-05-25 16:20:20 +010020#include "tbbr_config.h"
21
22#define OPT_TOC_ENTRY 0
23#define OPT_PLAT_TOC_FLAGS 1
Masahiro Yamada4d87eb42016-12-25 13:52:22 +090024#define OPT_ALIGN 2
dp-arm4972ec52016-05-25 16:20:20 +010025
26static int info_cmd(int argc, char *argv[]);
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -050027static void info_usage(int);
dp-arm4972ec52016-05-25 16:20:20 +010028static int create_cmd(int argc, char *argv[]);
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -050029static void create_usage(int);
dp-arm4972ec52016-05-25 16:20:20 +010030static int update_cmd(int argc, char *argv[]);
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -050031static void update_usage(int);
dp-arm4972ec52016-05-25 16:20:20 +010032static int unpack_cmd(int argc, char *argv[]);
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -050033static void unpack_usage(int);
dp-arm4972ec52016-05-25 16:20:20 +010034static int remove_cmd(int argc, char *argv[]);
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -050035static void remove_usage(int);
dp-arm4972ec52016-05-25 16:20:20 +010036static int version_cmd(int argc, char *argv[]);
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -050037static void version_usage(int);
dp-arm4972ec52016-05-25 16:20:20 +010038static int help_cmd(int argc, char *argv[]);
39static void usage(void);
40
41/* Available subcommands. */
42static cmd_t cmds[] = {
43 { .name = "info", .handler = info_cmd, .usage = info_usage },
44 { .name = "create", .handler = create_cmd, .usage = create_usage },
45 { .name = "update", .handler = update_cmd, .usage = update_usage },
46 { .name = "unpack", .handler = unpack_cmd, .usage = unpack_usage },
47 { .name = "remove", .handler = remove_cmd, .usage = remove_usage },
48 { .name = "version", .handler = version_cmd, .usage = version_usage },
49 { .name = "help", .handler = help_cmd, .usage = NULL },
50};
51
dp-arm90d2f0e2016-11-14 15:54:32 +000052static image_desc_t *image_desc_head;
53static size_t nr_image_descs;
Roberto Vargaseace8f12018-04-26 13:36:53 +010054static const uuid_t uuid_null;
dp-arm4972ec52016-05-25 16:20:20 +010055static int verbose;
56
dp-armc1f8e772016-11-04 10:52:25 +000057static void vlog(int prio, const char *msg, va_list ap)
dp-arm4972ec52016-05-25 16:20:20 +010058{
59 char *prefix[] = { "DEBUG", "WARN", "ERROR" };
60
61 fprintf(stderr, "%s: ", prefix[prio]);
62 vfprintf(stderr, msg, ap);
63 fputc('\n', stderr);
64}
65
dp-armc1f8e772016-11-04 10:52:25 +000066static void log_dbgx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010067{
68 va_list ap;
69
70 va_start(ap, msg);
71 vlog(LOG_DBG, msg, ap);
72 va_end(ap);
73}
74
dp-armc1f8e772016-11-04 10:52:25 +000075static void log_warnx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010076{
77 va_list ap;
78
79 va_start(ap, msg);
80 vlog(LOG_WARN, msg, ap);
81 va_end(ap);
82}
83
dp-armc1f8e772016-11-04 10:52:25 +000084static void log_err(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010085{
86 char buf[512];
87 va_list ap;
88
89 va_start(ap, msg);
90 snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
91 vlog(LOG_ERR, buf, ap);
92 va_end(ap);
93 exit(1);
94}
95
dp-armc1f8e772016-11-04 10:52:25 +000096static void log_errx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010097{
98 va_list ap;
99
100 va_start(ap, msg);
101 vlog(LOG_ERR, msg, ap);
102 va_end(ap);
103 exit(1);
104}
105
dp-armdb0f5e92016-11-04 10:56:25 +0000106static char *xstrdup(const char *s, const char *msg)
107{
108 char *d;
109
110 d = strdup(s);
111 if (d == NULL)
dp-arm5ba38542016-12-21 14:59:30 +0000112 log_errx("strdup: %s", msg);
dp-armdb0f5e92016-11-04 10:56:25 +0000113 return d;
114}
115
116static void *xmalloc(size_t size, const char *msg)
117{
118 void *d;
119
120 d = malloc(size);
121 if (d == NULL)
dp-arm5ba38542016-12-21 14:59:30 +0000122 log_errx("malloc: %s", msg);
dp-armdb0f5e92016-11-04 10:56:25 +0000123 return d;
124}
125
Masahiro Yamada03fdf692017-01-15 00:50:41 +0900126static void *xzalloc(size_t size, const char *msg)
127{
128 return memset(xmalloc(size, msg), 0, size);
129}
130
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900131static void xfwrite(void *buf, size_t size, FILE *fp, const char *filename)
132{
133 if (fwrite(buf, 1, size, fp) != size)
134 log_errx("Failed to write %s", filename);
135}
136
dp-arm90d2f0e2016-11-14 15:54:32 +0000137static image_desc_t *new_image_desc(const uuid_t *uuid,
138 const char *name, const char *cmdline_name)
dp-arm4972ec52016-05-25 16:20:20 +0100139{
dp-arm90d2f0e2016-11-14 15:54:32 +0000140 image_desc_t *desc;
141
Masahiro Yamada03fdf692017-01-15 00:50:41 +0900142 desc = xzalloc(sizeof(*desc),
dp-arm90d2f0e2016-11-14 15:54:32 +0000143 "failed to allocate memory for image descriptor");
144 memcpy(&desc->uuid, uuid, sizeof(uuid_t));
145 desc->name = xstrdup(name,
146 "failed to allocate memory for image name");
147 desc->cmdline_name = xstrdup(cmdline_name,
148 "failed to allocate memory for image command line name");
149 desc->action = DO_UNSPEC;
dp-arm90d2f0e2016-11-14 15:54:32 +0000150 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100151}
152
dp-armfb732312016-12-30 09:55:48 +0000153static void set_image_desc_action(image_desc_t *desc, int action,
154 const char *arg)
155{
156 assert(desc != NULL);
157
Evan Lloyd04dc3442017-05-25 19:06:47 +0100158 if (desc->action_arg != (char *)DO_UNSPEC)
dp-armfb732312016-12-30 09:55:48 +0000159 free(desc->action_arg);
160 desc->action = action;
161 desc->action_arg = NULL;
162 if (arg != NULL)
163 desc->action_arg = xstrdup(arg,
164 "failed to allocate memory for argument");
165}
166
dp-arm90d2f0e2016-11-14 15:54:32 +0000167static void free_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100168{
dp-arm90d2f0e2016-11-14 15:54:32 +0000169 free(desc->name);
170 free(desc->cmdline_name);
171 free(desc->action_arg);
Jonathan Wright242cd3c2018-05-03 15:05:09 +0100172 if (desc->image) {
173 free(desc->image->buffer);
174 free(desc->image);
175 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000176 free(desc);
dp-arm4972ec52016-05-25 16:20:20 +0100177}
178
dp-arm90d2f0e2016-11-14 15:54:32 +0000179static void add_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100180{
Masahiro Yamadad224b452017-01-14 23:22:02 +0900181 image_desc_t **p = &image_desc_head;
182
Masahiro Yamadad224b452017-01-14 23:22:02 +0900183 while (*p)
184 p = &(*p)->next;
185
Masahiro Yamadac2a7d9c2017-01-27 12:53:13 +0900186 assert(*p == NULL);
Masahiro Yamadad224b452017-01-14 23:22:02 +0900187 *p = desc;
dp-arm90d2f0e2016-11-14 15:54:32 +0000188 nr_image_descs++;
189}
dp-arm4972ec52016-05-25 16:20:20 +0100190
dp-arm90d2f0e2016-11-14 15:54:32 +0000191static void free_image_descs(void)
192{
193 image_desc_t *desc = image_desc_head, *tmp;
194
195 while (desc != NULL) {
196 tmp = desc->next;
197 free_image_desc(desc);
198 desc = tmp;
199 nr_image_descs--;
dp-arm4972ec52016-05-25 16:20:20 +0100200 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000201 assert(nr_image_descs == 0);
dp-arm4972ec52016-05-25 16:20:20 +0100202}
203
dp-arm90d2f0e2016-11-14 15:54:32 +0000204static void fill_image_descs(void)
205{
206 toc_entry_t *toc_entry;
207
208 for (toc_entry = toc_entries;
209 toc_entry->cmdline_name != NULL;
210 toc_entry++) {
211 image_desc_t *desc;
212
213 desc = new_image_desc(&toc_entry->uuid,
214 toc_entry->name,
215 toc_entry->cmdline_name);
216 add_image_desc(desc);
217 }
Pankaj Guptae4aa1bd2020-12-09 14:02:38 +0530218#ifdef PLAT_DEF_FIP_UUID
219 for (toc_entry = plat_def_toc_entries;
220 toc_entry->cmdline_name != NULL;
221 toc_entry++) {
222 image_desc_t *desc;
223
224 desc = new_image_desc(&toc_entry->uuid,
225 toc_entry->name,
226 toc_entry->cmdline_name);
227 add_image_desc(desc);
228 }
229#endif
dp-arm90d2f0e2016-11-14 15:54:32 +0000230}
231
dp-arm90d2f0e2016-11-14 15:54:32 +0000232static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid)
dp-arm4972ec52016-05-25 16:20:20 +0100233{
dp-arm90d2f0e2016-11-14 15:54:32 +0000234 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100235
dp-arm90d2f0e2016-11-14 15:54:32 +0000236 for (desc = image_desc_head; desc != NULL; desc = desc->next)
237 if (memcmp(&desc->uuid, uuid, sizeof(uuid_t)) == 0)
238 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100239 return NULL;
240}
241
dp-arm90d2f0e2016-11-14 15:54:32 +0000242static image_desc_t *lookup_image_desc_from_opt(const char *opt)
243{
244 image_desc_t *desc;
245
246 for (desc = image_desc_head; desc != NULL; desc = desc->next)
247 if (strcmp(desc->cmdline_name, opt) == 0)
248 return desc;
249 return NULL;
250}
251
dp-arm516dfcb2016-11-03 13:59:26 +0000252static void uuid_to_str(char *s, size_t len, const uuid_t *u)
253{
254 assert(len >= (_UUID_STR_LEN + 1));
255
Roberto Vargaseace8f12018-04-26 13:36:53 +0100256 snprintf(s, len,
257 "%02X%02X%02X%02X-%02X%02X-%02X%02X-%04X-%04X%04X%04X",
258 u->time_low[0], u->time_low[1], u->time_low[2], u->time_low[3],
259 u->time_mid[0], u->time_mid[1],
260 u->time_hi_and_version[0], u->time_hi_and_version[1],
261 (u->clock_seq_hi_and_reserved << 8) | u->clock_seq_low,
262 (u->node[0] << 8) | u->node[1],
263 (u->node[2] << 8) | u->node[3],
264 (u->node[4] << 8) | u->node[5]);
dp-arm516dfcb2016-11-03 13:59:26 +0000265}
266
267static void uuid_from_str(uuid_t *u, const char *s)
268{
269 int n;
270
271 if (s == NULL)
272 log_errx("UUID cannot be NULL");
273 if (strlen(s) != _UUID_STR_LEN)
274 log_errx("Invalid UUID: %s", s);
275
276 n = sscanf(s,
Roberto Vargaseace8f12018-04-26 13:36:53 +0100277 "%2hhx%2hhx%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
278 &u->time_low[0], &u->time_low[1], &u->time_low[2], &u->time_low[3],
279 &u->time_mid[0], &u->time_mid[1],
280 &u->time_hi_and_version[0], &u->time_hi_and_version[1],
281 &u->clock_seq_hi_and_reserved, &u->clock_seq_low,
282 &u->node[0], &u->node[1],
283 &u->node[2], &u->node[3],
284 &u->node[4], &u->node[5]);
dp-arm516dfcb2016-11-03 13:59:26 +0000285 /*
Andre Przywaraf41e53c2019-01-29 09:25:14 +0000286 * Given the format specifier above, we expect 16 items to be scanned
dp-arm516dfcb2016-11-03 13:59:26 +0000287 * for a properly formatted UUID.
288 */
Andre Przywaraf41e53c2019-01-29 09:25:14 +0000289 if (n != 16)
dp-arm516dfcb2016-11-03 13:59:26 +0000290 log_errx("Invalid UUID: %s", s);
291}
292
dp-armc1f8e772016-11-04 10:52:25 +0000293static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
dp-arm4972ec52016-05-25 16:20:20 +0100294{
Evan Lloyd04dc3442017-05-25 19:06:47 +0100295 struct BLD_PLAT_STAT st;
dp-arm4972ec52016-05-25 16:20:20 +0100296 FILE *fp;
297 char *buf, *bufend;
298 fip_toc_header_t *toc_header;
299 fip_toc_entry_t *toc_entry;
dp-arm4972ec52016-05-25 16:20:20 +0100300 int terminated = 0;
301
Evan Lloydb1939392017-01-13 14:13:09 +0000302 fp = fopen(filename, "rb");
dp-arm4972ec52016-05-25 16:20:20 +0100303 if (fp == NULL)
304 log_err("fopen %s", filename);
305
306 if (fstat(fileno(fp), &st) == -1)
307 log_err("fstat %s", filename);
308
dp-armdb0f5e92016-11-04 10:56:25 +0000309 buf = xmalloc(st.st_size, "failed to load file into memory");
dp-arm4972ec52016-05-25 16:20:20 +0100310 if (fread(buf, 1, st.st_size, fp) != st.st_size)
311 log_errx("Failed to read %s", filename);
312 bufend = buf + st.st_size;
313 fclose(fp);
314
315 if (st.st_size < sizeof(fip_toc_header_t))
316 log_errx("FIP %s is truncated", filename);
317
318 toc_header = (fip_toc_header_t *)buf;
319 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
320
321 if (toc_header->name != TOC_HEADER_NAME)
322 log_errx("%s is not a FIP file", filename);
323
324 /* Return the ToC header if the caller wants it. */
325 if (toc_header_out != NULL)
326 *toc_header_out = *toc_header;
327
328 /* Walk through each ToC entry in the file. */
329 while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
dp-arm516dfcb2016-11-03 13:59:26 +0000330 image_t *image;
331 image_desc_t *desc;
332
dp-arm4972ec52016-05-25 16:20:20 +0100333 /* Found the ToC terminator, we are done. */
334 if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
335 terminated = 1;
336 break;
337 }
338
339 /*
340 * Build a new image out of the ToC entry and add it to the
341 * table of images.
342 */
Masahiro Yamadad224b452017-01-14 23:22:02 +0900343 image = xzalloc(sizeof(*image),
dp-armdb0f5e92016-11-04 10:56:25 +0000344 "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900345 image->toc_e = *toc_entry;
dp-armdb0f5e92016-11-04 10:56:25 +0000346 image->buffer = xmalloc(toc_entry->size,
347 "failed to allocate image buffer, is FIP file corrupted?");
dp-arm4972ec52016-05-25 16:20:20 +0100348 /* Overflow checks before memory copy. */
349 if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
350 log_errx("FIP %s is corrupted", filename);
351 if (toc_entry->size + toc_entry->offset_address > st.st_size)
352 log_errx("FIP %s is corrupted", filename);
353
354 memcpy(image->buffer, buf + toc_entry->offset_address,
355 toc_entry->size);
dp-arm4972ec52016-05-25 16:20:20 +0100356
dp-arm516dfcb2016-11-03 13:59:26 +0000357 /* If this is an unknown image, create a descriptor for it. */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900358 desc = lookup_image_desc_from_uuid(&toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000359 if (desc == NULL) {
360 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
361
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900362 uuid_to_str(name, sizeof(name), &toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000363 snprintf(filename, sizeof(filename), "%s%s",
364 name, ".bin");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900365 desc = new_image_desc(&toc_entry->uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000366 desc->action = DO_UNPACK;
367 desc->action_arg = xstrdup(filename,
368 "failed to allocate memory for blob filename");
369 add_image_desc(desc);
370 }
371
dp-armafa1efa2017-02-14 15:22:13 +0000372 assert(desc->image == NULL);
373 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100374
375 toc_entry++;
376 }
377
378 if (terminated == 0)
379 log_errx("FIP %s does not have a ToC terminator entry",
380 filename);
381 free(buf);
382 return 0;
383}
384
dp-armc1f8e772016-11-04 10:52:25 +0000385static image_t *read_image_from_file(const uuid_t *uuid, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100386{
Evan Lloyd04dc3442017-05-25 19:06:47 +0100387 struct BLD_PLAT_STAT st;
dp-arm4972ec52016-05-25 16:20:20 +0100388 image_t *image;
389 FILE *fp;
390
dp-arm715ef422016-08-30 14:18:58 +0100391 assert(uuid != NULL);
Evan Lloyd04dc3442017-05-25 19:06:47 +0100392 assert(filename != NULL);
dp-arm715ef422016-08-30 14:18:58 +0100393
Evan Lloydb1939392017-01-13 14:13:09 +0000394 fp = fopen(filename, "rb");
dp-arm4972ec52016-05-25 16:20:20 +0100395 if (fp == NULL)
396 log_err("fopen %s", filename);
397
398 if (fstat(fileno(fp), &st) == -1)
399 log_errx("fstat %s", filename);
400
Masahiro Yamadad224b452017-01-14 23:22:02 +0900401 image = xzalloc(sizeof(*image), "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900402 image->toc_e.uuid = *uuid;
dp-armdb0f5e92016-11-04 10:56:25 +0000403 image->buffer = xmalloc(st.st_size, "failed to allocate image buffer");
dp-arm4972ec52016-05-25 16:20:20 +0100404 if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
405 log_errx("Failed to read %s", filename);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900406 image->toc_e.size = st.st_size;
dp-arm4972ec52016-05-25 16:20:20 +0100407
408 fclose(fp);
409 return image;
410}
411
dp-armc1f8e772016-11-04 10:52:25 +0000412static int write_image_to_file(const image_t *image, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100413{
414 FILE *fp;
415
Evan Lloydb1939392017-01-13 14:13:09 +0000416 fp = fopen(filename, "wb");
dp-arm4972ec52016-05-25 16:20:20 +0100417 if (fp == NULL)
418 log_err("fopen");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900419 xfwrite(image->buffer, image->toc_e.size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100420 fclose(fp);
421 return 0;
422}
423
dp-arm90d2f0e2016-11-14 15:54:32 +0000424static struct option *add_opt(struct option *opts, size_t *nr_opts,
425 const char *name, int has_arg, int val)
dp-arm4972ec52016-05-25 16:20:20 +0100426{
dp-arm90d2f0e2016-11-14 15:54:32 +0000427 opts = realloc(opts, (*nr_opts + 1) * sizeof(*opts));
428 if (opts == NULL)
429 log_err("realloc");
430 opts[*nr_opts].name = name;
431 opts[*nr_opts].has_arg = has_arg;
432 opts[*nr_opts].flag = NULL;
433 opts[*nr_opts].val = val;
434 ++*nr_opts;
435 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100436}
437
dp-arm90d2f0e2016-11-14 15:54:32 +0000438static struct option *fill_common_opts(struct option *opts, size_t *nr_opts,
439 int has_arg)
dp-arm4972ec52016-05-25 16:20:20 +0100440{
dp-arm90d2f0e2016-11-14 15:54:32 +0000441 image_desc_t *desc;
442
443 for (desc = image_desc_head; desc != NULL; desc = desc->next)
444 opts = add_opt(opts, nr_opts, desc->cmdline_name, has_arg,
445 OPT_TOC_ENTRY);
446 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100447}
448
dp-arm90d2f0e2016-11-14 15:54:32 +0000449static void md_print(const unsigned char *md, size_t len)
dp-arm12e893b2016-08-24 13:21:08 +0100450{
451 size_t i;
452
453 for (i = 0; i < len; i++)
454 printf("%02x", md[i]);
455}
456
dp-arm4972ec52016-05-25 16:20:20 +0100457static int info_cmd(int argc, char *argv[])
458{
dp-armafa1efa2017-02-14 15:22:13 +0000459 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100460 fip_toc_header_t toc_header;
dp-arm4972ec52016-05-25 16:20:20 +0100461
462 if (argc != 2)
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500463 info_usage(EXIT_FAILURE);
dp-arm4972ec52016-05-25 16:20:20 +0100464 argc--, argv++;
465
466 parse_fip(argv[0], &toc_header);
467
468 if (verbose) {
469 log_dbgx("toc_header[name]: 0x%llX",
470 (unsigned long long)toc_header.name);
471 log_dbgx("toc_header[serial_number]: 0x%llX",
472 (unsigned long long)toc_header.serial_number);
473 log_dbgx("toc_header[flags]: 0x%llX",
474 (unsigned long long)toc_header.flags);
475 }
476
dp-armafa1efa2017-02-14 15:22:13 +0000477 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
478 image_t *image = desc->image;
dp-arm715ef422016-08-30 14:18:58 +0100479
dp-armafa1efa2017-02-14 15:22:13 +0000480 if (image == NULL)
481 continue;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900482 printf("%s: offset=0x%llX, size=0x%llX, cmdline=\"--%s\"",
483 desc->name,
484 (unsigned long long)image->toc_e.offset_address,
485 (unsigned long long)image->toc_e.size,
Masahiro Yamadaeee39312017-01-15 23:20:00 +0900486 desc->cmdline_name);
Evan Lloyd04dc3442017-05-25 19:06:47 +0100487#ifndef _MSC_VER /* We don't have SHA256 for Visual Studio. */
dp-arm12e893b2016-08-24 13:21:08 +0100488 if (verbose) {
489 unsigned char md[SHA256_DIGEST_LENGTH];
490
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900491 SHA256(image->buffer, image->toc_e.size, md);
dp-arm12e893b2016-08-24 13:21:08 +0100492 printf(", sha256=");
493 md_print(md, sizeof(md));
494 }
Evan Lloyd04dc3442017-05-25 19:06:47 +0100495#endif
dp-arm12e893b2016-08-24 13:21:08 +0100496 putchar('\n');
dp-arm4972ec52016-05-25 16:20:20 +0100497 }
498
dp-arm4972ec52016-05-25 16:20:20 +0100499 return 0;
500}
501
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500502static void info_usage(int exit_status)
dp-arm4972ec52016-05-25 16:20:20 +0100503{
504 printf("fiptool info FIP_FILENAME\n");
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500505 exit(exit_status);
dp-arm4972ec52016-05-25 16:20:20 +0100506}
507
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900508static int pack_images(const char *filename, uint64_t toc_flags, unsigned long align)
dp-arm4972ec52016-05-25 16:20:20 +0100509{
510 FILE *fp;
dp-armafa1efa2017-02-14 15:22:13 +0000511 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100512 fip_toc_header_t *toc_header;
513 fip_toc_entry_t *toc_entry;
514 char *buf;
Roberto Vargas81935762017-12-19 11:56:57 +0000515 uint64_t entry_offset, buf_size, payload_size = 0, pad_size;
dp-armafa1efa2017-02-14 15:22:13 +0000516 size_t nr_images = 0;
517
518 for (desc = image_desc_head; desc != NULL; desc = desc->next)
519 if (desc->image != NULL)
520 nr_images++;
dp-arm4972ec52016-05-25 16:20:20 +0100521
522 buf_size = sizeof(fip_toc_header_t) +
523 sizeof(fip_toc_entry_t) * (nr_images + 1);
524 buf = calloc(1, buf_size);
525 if (buf == NULL)
526 log_err("calloc");
527
528 /* Build up header and ToC entries from the image table. */
529 toc_header = (fip_toc_header_t *)buf;
530 toc_header->name = TOC_HEADER_NAME;
531 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
532 toc_header->flags = toc_flags;
533
534 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
535
536 entry_offset = buf_size;
dp-armafa1efa2017-02-14 15:22:13 +0000537 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
538 image_t *image = desc->image;
539
Manish V Badarkhe0624d732021-12-18 11:26:25 +0000540 if (image == NULL || (image->toc_e.size == 0ULL))
dp-armafa1efa2017-02-14 15:22:13 +0000541 continue;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900542 payload_size += image->toc_e.size;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900543 entry_offset = (entry_offset + align - 1) & ~(align - 1);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900544 image->toc_e.offset_address = entry_offset;
545 *toc_entry++ = image->toc_e;
546 entry_offset += image->toc_e.size;
dp-arm4972ec52016-05-25 16:20:20 +0100547 }
548
Roberto Vargas81935762017-12-19 11:56:57 +0000549 /*
550 * Append a null uuid entry to mark the end of ToC entries.
551 * NOTE the offset address for the last toc_entry must match the fip
552 * size.
553 */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900554 memset(toc_entry, 0, sizeof(*toc_entry));
Roberto Vargas81935762017-12-19 11:56:57 +0000555 toc_entry->offset_address = (entry_offset + align - 1) & ~(align - 1);
dp-arm4972ec52016-05-25 16:20:20 +0100556
557 /* Generate the FIP file. */
Evan Lloydb1939392017-01-13 14:13:09 +0000558 fp = fopen(filename, "wb");
dp-arm4972ec52016-05-25 16:20:20 +0100559 if (fp == NULL)
560 log_err("fopen %s", filename);
561
562 if (verbose)
563 log_dbgx("Metadata size: %zu bytes", buf_size);
564
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900565 xfwrite(buf, buf_size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100566
567 if (verbose)
568 log_dbgx("Payload size: %zu bytes", payload_size);
569
dp-armafa1efa2017-02-14 15:22:13 +0000570 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
571 image_t *image = desc->image;
572
573 if (image == NULL)
574 continue;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900575 if (fseek(fp, image->toc_e.offset_address, SEEK_SET))
576 log_errx("Failed to set file position");
577
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900578 xfwrite(image->buffer, image->toc_e.size, fp, filename);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900579 }
dp-arm4972ec52016-05-25 16:20:20 +0100580
Roberto Vargas81935762017-12-19 11:56:57 +0000581 if (fseek(fp, entry_offset, SEEK_SET))
582 log_errx("Failed to set file position");
583
584 pad_size = toc_entry->offset_address - entry_offset;
585 while (pad_size--)
586 fputc(0x0, fp);
587
Andreas Färber860b5dc2018-01-27 16:46:59 +0100588 free(buf);
dp-arm4972ec52016-05-25 16:20:20 +0100589 fclose(fp);
590 return 0;
591}
592
593/*
594 * This function is shared between the create and update subcommands.
595 * The difference between the two subcommands is that when the FIP file
596 * is created, the parsing of an existing FIP is skipped. This results
597 * in update_fip() creating the new FIP file from scratch because the
598 * internal image table is not populated.
599 */
600static void update_fip(void)
601{
dp-arm90d2f0e2016-11-14 15:54:32 +0000602 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100603
604 /* Add or replace images in the FIP file. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000605 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-armafa1efa2017-02-14 15:22:13 +0000606 image_t *image;
dp-arm516dfcb2016-11-03 13:59:26 +0000607
dp-arm90d2f0e2016-11-14 15:54:32 +0000608 if (desc->action != DO_PACK)
dp-arm4972ec52016-05-25 16:20:20 +0100609 continue;
610
dp-armafa1efa2017-02-14 15:22:13 +0000611 image = read_image_from_file(&desc->uuid,
dp-arm90d2f0e2016-11-14 15:54:32 +0000612 desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000613 if (desc->image != NULL) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000614 if (verbose) {
dp-arm516dfcb2016-11-03 13:59:26 +0000615 log_dbgx("Replacing %s with %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000616 desc->cmdline_name,
617 desc->action_arg);
618 }
dp-armafa1efa2017-02-14 15:22:13 +0000619 free(desc->image);
620 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100621 } else {
622 if (verbose)
623 log_dbgx("Adding image %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000624 desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000625 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100626 }
dp-arm4972ec52016-05-25 16:20:20 +0100627 }
628}
629
dp-arm90d2f0e2016-11-14 15:54:32 +0000630static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
dp-arm4972ec52016-05-25 16:20:20 +0100631{
632 unsigned long long flags;
633 char *endptr;
634
635 errno = 0;
636 flags = strtoull(arg, &endptr, 16);
637 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
638 log_errx("Invalid platform ToC flags: %s", arg);
639 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
640 *toc_flags |= flags << 32;
641}
642
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900643static int is_power_of_2(unsigned long x)
644{
645 return x && !(x & (x - 1));
646}
647
648static unsigned long get_image_align(char *arg)
649{
650 char *endptr;
651 unsigned long align;
652
653 errno = 0;
Andreas Färber242a7b72017-04-21 19:39:10 +0200654 align = strtoul(arg, &endptr, 0);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900655 if (*endptr != '\0' || !is_power_of_2(align) || errno != 0)
656 log_errx("Invalid alignment: %s", arg);
657
658 return align;
659}
660
dp-arm516dfcb2016-11-03 13:59:26 +0000661static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
662{
663 char *p;
664
665 for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
666 if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
667 p += strlen("uuid=");
668 uuid_from_str(uuid, p);
669 } else if (strncmp(p, "file=", strlen("file=")) == 0) {
670 p += strlen("file=");
671 snprintf(filename, len, "%s", p);
672 }
673 }
674}
675
dp-arm4972ec52016-05-25 16:20:20 +0100676static int create_cmd(int argc, char *argv[])
677{
dp-arm90d2f0e2016-11-14 15:54:32 +0000678 struct option *opts = NULL;
679 size_t nr_opts = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100680 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900681 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100682
683 if (argc < 2)
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500684 create_usage(EXIT_FAILURE);
dp-arm4972ec52016-05-25 16:20:20 +0100685
dp-arm90d2f0e2016-11-14 15:54:32 +0000686 opts = fill_common_opts(opts, &nr_opts, required_argument);
687 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100688 OPT_PLAT_TOC_FLAGS);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900689 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000690 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000691 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100692
693 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000694 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100695
dp-arm516dfcb2016-11-03 13:59:26 +0000696 c = getopt_long(argc, argv, "b:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100697 if (c == -1)
698 break;
699
700 switch (c) {
701 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000702 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100703
dp-arm90d2f0e2016-11-14 15:54:32 +0000704 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000705 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100706 break;
707 }
708 case OPT_PLAT_TOC_FLAGS:
709 parse_plat_toc_flags(optarg, &toc_flags);
710 break;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900711 case OPT_ALIGN:
712 align = get_image_align(optarg);
713 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000714 case 'b': {
715 char name[_UUID_STR_LEN + 1];
716 char filename[PATH_MAX] = { 0 };
Roberto Vargaseace8f12018-04-26 13:36:53 +0100717 uuid_t uuid = uuid_null;
dp-arm516dfcb2016-11-03 13:59:26 +0000718 image_desc_t *desc;
719
720 parse_blob_opt(optarg, &uuid,
721 filename, sizeof(filename));
722
723 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
724 filename[0] == '\0')
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500725 create_usage(EXIT_FAILURE);
dp-arm516dfcb2016-11-03 13:59:26 +0000726
727 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000728 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000729 uuid_to_str(name, sizeof(name), &uuid);
730 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000731 add_image_desc(desc);
732 }
dp-armfb732312016-12-30 09:55:48 +0000733 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000734 break;
735 }
dp-arm4972ec52016-05-25 16:20:20 +0100736 default:
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500737 create_usage(EXIT_FAILURE);
dp-arm4972ec52016-05-25 16:20:20 +0100738 }
739 }
740 argc -= optind;
741 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000742 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100743
744 if (argc == 0)
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500745 create_usage(EXIT_SUCCESS);
dp-arm4972ec52016-05-25 16:20:20 +0100746
747 update_fip();
748
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900749 pack_images(argv[0], toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100750 return 0;
751}
752
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500753static void create_usage(int exit_status)
dp-arm4972ec52016-05-25 16:20:20 +0100754{
755 toc_entry_t *toc_entry = toc_entries;
756
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900757 printf("fiptool create [opts] FIP_FILENAME\n");
758 printf("\n");
759 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900760 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900761 printf(" --blob uuid=...,file=...\tAdd an image with the given UUID pointed to by file.\n");
762 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field occupying bits 32-47 in 64-bit ToC header.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +0900763 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100764 printf("Specific images are packed with the following options:\n");
765 for (; toc_entry->cmdline_name != NULL; toc_entry++)
766 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
767 toc_entry->name);
Pankaj Guptae4aa1bd2020-12-09 14:02:38 +0530768#ifdef PLAT_DEF_FIP_UUID
769 toc_entry = plat_def_toc_entries;
770 for (; toc_entry->cmdline_name != NULL; toc_entry++)
771 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
772 toc_entry->name);
773#endif
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500774 exit(exit_status);
dp-arm4972ec52016-05-25 16:20:20 +0100775}
776
777static int update_cmd(int argc, char *argv[])
778{
dp-arm90d2f0e2016-11-14 15:54:32 +0000779 struct option *opts = NULL;
780 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000781 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100782 fip_toc_header_t toc_header = { 0 };
783 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900784 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100785 int pflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100786
787 if (argc < 2)
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500788 update_usage(EXIT_FAILURE);
dp-arm4972ec52016-05-25 16:20:20 +0100789
dp-arm90d2f0e2016-11-14 15:54:32 +0000790 opts = fill_common_opts(opts, &nr_opts, required_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900791 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000792 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000793 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
794 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100795 OPT_PLAT_TOC_FLAGS);
dp-arm90d2f0e2016-11-14 15:54:32 +0000796 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100797
798 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000799 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100800
dp-arm516dfcb2016-11-03 13:59:26 +0000801 c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100802 if (c == -1)
803 break;
804
805 switch (c) {
806 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000807 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100808
dp-arm90d2f0e2016-11-14 15:54:32 +0000809 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000810 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100811 break;
812 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000813 case OPT_PLAT_TOC_FLAGS:
dp-arm4972ec52016-05-25 16:20:20 +0100814 parse_plat_toc_flags(optarg, &toc_flags);
815 pflag = 1;
816 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000817 case 'b': {
818 char name[_UUID_STR_LEN + 1];
819 char filename[PATH_MAX] = { 0 };
Roberto Vargaseace8f12018-04-26 13:36:53 +0100820 uuid_t uuid = uuid_null;
dp-arm516dfcb2016-11-03 13:59:26 +0000821 image_desc_t *desc;
822
823 parse_blob_opt(optarg, &uuid,
824 filename, sizeof(filename));
825
826 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
827 filename[0] == '\0')
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500828 update_usage(EXIT_FAILURE);
dp-arm516dfcb2016-11-03 13:59:26 +0000829
830 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000831 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000832 uuid_to_str(name, sizeof(name), &uuid);
833 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000834 add_image_desc(desc);
835 }
dp-armfb732312016-12-30 09:55:48 +0000836 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000837 break;
838 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900839 case OPT_ALIGN:
840 align = get_image_align(optarg);
841 break;
dp-arm4972ec52016-05-25 16:20:20 +0100842 case 'o':
843 snprintf(outfile, sizeof(outfile), "%s", optarg);
844 break;
845 default:
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500846 update_usage(EXIT_FAILURE);
dp-arm4972ec52016-05-25 16:20:20 +0100847 }
848 }
849 argc -= optind;
850 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000851 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100852
853 if (argc == 0)
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500854 update_usage(EXIT_SUCCESS);
dp-arm4972ec52016-05-25 16:20:20 +0100855
856 if (outfile[0] == '\0')
857 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
858
Masahiro Yamadaebb6e372016-12-25 12:41:41 +0900859 if (access(argv[0], F_OK) == 0)
dp-arm4972ec52016-05-25 16:20:20 +0100860 parse_fip(argv[0], &toc_header);
861
862 if (pflag)
863 toc_header.flags &= ~(0xffffULL << 32);
864 toc_flags = (toc_header.flags |= toc_flags);
865
866 update_fip();
867
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900868 pack_images(outfile, toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100869 return 0;
870}
871
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500872static void update_usage(int exit_status)
dp-arm4972ec52016-05-25 16:20:20 +0100873{
874 toc_entry_t *toc_entry = toc_entries;
875
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900876 printf("fiptool update [opts] FIP_FILENAME\n");
877 printf("\n");
878 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900879 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900880 printf(" --blob uuid=...,file=...\tAdd or update an image with the given UUID pointed to by file.\n");
dp-arm4972ec52016-05-25 16:20:20 +0100881 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900882 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field occupying bits 32-47 in 64-bit ToC header.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +0900883 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100884 printf("Specific images are packed with the following options:\n");
885 for (; toc_entry->cmdline_name != NULL; toc_entry++)
886 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
887 toc_entry->name);
Pankaj Guptae4aa1bd2020-12-09 14:02:38 +0530888#ifdef PLAT_DEF_FIP_UUID
889 toc_entry = plat_def_toc_entries;
890 for (; toc_entry->cmdline_name != NULL; toc_entry++)
891 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
892 toc_entry->name);
893#endif
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500894 exit(exit_status);
dp-arm4972ec52016-05-25 16:20:20 +0100895}
896
897static int unpack_cmd(int argc, char *argv[])
898{
dp-arm90d2f0e2016-11-14 15:54:32 +0000899 struct option *opts = NULL;
900 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000901 char outdir[PATH_MAX] = { 0 };
dp-arm90d2f0e2016-11-14 15:54:32 +0000902 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100903 int fflag = 0;
904 int unpack_all = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100905
906 if (argc < 2)
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500907 unpack_usage(EXIT_FAILURE);
dp-arm4972ec52016-05-25 16:20:20 +0100908
dp-arm90d2f0e2016-11-14 15:54:32 +0000909 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000910 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000911 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
912 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
913 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100914
915 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000916 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100917
dp-arm516dfcb2016-11-03 13:59:26 +0000918 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100919 if (c == -1)
920 break;
921
922 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000923 case OPT_TOC_ENTRY: {
924 image_desc_t *desc;
925
926 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000927 set_image_desc_action(desc, DO_UNPACK, optarg);
dp-arm90d2f0e2016-11-14 15:54:32 +0000928 unpack_all = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100929 break;
dp-arm90d2f0e2016-11-14 15:54:32 +0000930 }
dp-arm516dfcb2016-11-03 13:59:26 +0000931 case 'b': {
932 char name[_UUID_STR_LEN + 1];
933 char filename[PATH_MAX] = { 0 };
Roberto Vargaseace8f12018-04-26 13:36:53 +0100934 uuid_t uuid = uuid_null;
dp-arm516dfcb2016-11-03 13:59:26 +0000935 image_desc_t *desc;
936
937 parse_blob_opt(optarg, &uuid,
938 filename, sizeof(filename));
939
940 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
941 filename[0] == '\0')
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500942 unpack_usage(EXIT_FAILURE);
dp-arm516dfcb2016-11-03 13:59:26 +0000943
944 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000945 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000946 uuid_to_str(name, sizeof(name), &uuid);
947 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000948 add_image_desc(desc);
949 }
dp-armfb732312016-12-30 09:55:48 +0000950 set_image_desc_action(desc, DO_UNPACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000951 unpack_all = 0;
952 break;
953 }
dp-arm4972ec52016-05-25 16:20:20 +0100954 case 'f':
955 fflag = 1;
956 break;
957 case 'o':
958 snprintf(outdir, sizeof(outdir), "%s", optarg);
959 break;
960 default:
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500961 unpack_usage(EXIT_FAILURE);
dp-arm4972ec52016-05-25 16:20:20 +0100962 }
963 }
964 argc -= optind;
965 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000966 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100967
968 if (argc == 0)
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -0500969 unpack_usage(EXIT_SUCCESS);
dp-arm4972ec52016-05-25 16:20:20 +0100970
971 parse_fip(argv[0], NULL);
972
973 if (outdir[0] != '\0')
974 if (chdir(outdir) == -1)
975 log_err("chdir %s", outdir);
976
dp-arm4972ec52016-05-25 16:20:20 +0100977 /* Unpack all specified images. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000978 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +0000979 char file[PATH_MAX];
dp-armafa1efa2017-02-14 15:22:13 +0000980 image_t *image = desc->image;
dp-arm715ef422016-08-30 14:18:58 +0100981
dp-arm90d2f0e2016-11-14 15:54:32 +0000982 if (!unpack_all && desc->action != DO_UNPACK)
dp-arm4972ec52016-05-25 16:20:20 +0100983 continue;
984
985 /* Build filename. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000986 if (desc->action_arg == NULL)
dp-arm4972ec52016-05-25 16:20:20 +0100987 snprintf(file, sizeof(file), "%s.bin",
dp-arm90d2f0e2016-11-14 15:54:32 +0000988 desc->cmdline_name);
dp-arm4972ec52016-05-25 16:20:20 +0100989 else
990 snprintf(file, sizeof(file), "%s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000991 desc->action_arg);
dp-arm4972ec52016-05-25 16:20:20 +0100992
dp-arm715ef422016-08-30 14:18:58 +0100993 if (image == NULL) {
994 if (!unpack_all)
dp-arm516dfcb2016-11-03 13:59:26 +0000995 log_warnx("%s does not exist in %s",
dp-arm715ef422016-08-30 14:18:58 +0100996 file, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +0100997 continue;
998 }
999
1000 if (access(file, F_OK) != 0 || fflag) {
1001 if (verbose)
1002 log_dbgx("Unpacking %s", file);
dp-arm715ef422016-08-30 14:18:58 +01001003 write_image_to_file(image, file);
dp-arm4972ec52016-05-25 16:20:20 +01001004 } else {
1005 log_warnx("File %s already exists, use --force to overwrite it",
1006 file);
1007 }
dp-arm4972ec52016-05-25 16:20:20 +01001008 }
1009
dp-arm4972ec52016-05-25 16:20:20 +01001010 return 0;
1011}
1012
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -05001013static void unpack_usage(int exit_status)
dp-arm4972ec52016-05-25 16:20:20 +01001014{
1015 toc_entry_t *toc_entry = toc_entries;
1016
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001017 printf("fiptool unpack [opts] FIP_FILENAME\n");
1018 printf("\n");
1019 printf("Options:\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +09001020 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID to file.\n");
1021 printf(" --force\t\t\tIf the output file already exists, use --force to overwrite it.\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001022 printf(" --out path\t\t\tSet the output directory path.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001023 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001024 printf("Specific images are unpacked with the following options:\n");
1025 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1026 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
1027 toc_entry->name);
Pankaj Guptae4aa1bd2020-12-09 14:02:38 +05301028#ifdef PLAT_DEF_FIP_UUID
1029 toc_entry = plat_def_toc_entries;
1030 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1031 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
1032 toc_entry->name);
1033#endif
Masahiro Yamada1b900152017-02-02 16:34:14 +09001034 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001035 printf("If no options are provided, all images will be unpacked.\n");
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -05001036 exit(exit_status);
dp-arm4972ec52016-05-25 16:20:20 +01001037}
1038
1039static int remove_cmd(int argc, char *argv[])
1040{
dp-arm90d2f0e2016-11-14 15:54:32 +00001041 struct option *opts = NULL;
1042 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +00001043 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +01001044 fip_toc_header_t toc_header;
dp-arm90d2f0e2016-11-14 15:54:32 +00001045 image_desc_t *desc;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001046 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +01001047 int fflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001048
1049 if (argc < 2)
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -05001050 remove_usage(EXIT_FAILURE);
dp-arm4972ec52016-05-25 16:20:20 +01001051
dp-arm90d2f0e2016-11-14 15:54:32 +00001052 opts = fill_common_opts(opts, &nr_opts, no_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001053 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +00001054 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +00001055 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
1056 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
1057 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +01001058
1059 while (1) {
dp-armfe92b892016-11-07 11:13:54 +00001060 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001061
dp-arm516dfcb2016-11-03 13:59:26 +00001062 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +01001063 if (c == -1)
1064 break;
1065
1066 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001067 case OPT_TOC_ENTRY: {
1068 image_desc_t *desc;
1069
1070 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +00001071 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm4972ec52016-05-25 16:20:20 +01001072 break;
dp-arm90d2f0e2016-11-14 15:54:32 +00001073 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001074 case OPT_ALIGN:
1075 align = get_image_align(optarg);
1076 break;
dp-arm516dfcb2016-11-03 13:59:26 +00001077 case 'b': {
1078 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
Roberto Vargaseace8f12018-04-26 13:36:53 +01001079 uuid_t uuid = uuid_null;
dp-arm516dfcb2016-11-03 13:59:26 +00001080 image_desc_t *desc;
1081
1082 parse_blob_opt(optarg, &uuid,
1083 filename, sizeof(filename));
1084
1085 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -05001086 remove_usage(EXIT_FAILURE);
dp-arm516dfcb2016-11-03 13:59:26 +00001087
1088 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +00001089 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +00001090 uuid_to_str(name, sizeof(name), &uuid);
1091 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +00001092 add_image_desc(desc);
1093 }
dp-armfb732312016-12-30 09:55:48 +00001094 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm516dfcb2016-11-03 13:59:26 +00001095 break;
1096 }
dp-arm4972ec52016-05-25 16:20:20 +01001097 case 'f':
1098 fflag = 1;
1099 break;
1100 case 'o':
1101 snprintf(outfile, sizeof(outfile), "%s", optarg);
1102 break;
1103 default:
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -05001104 remove_usage(EXIT_FAILURE);
dp-arm4972ec52016-05-25 16:20:20 +01001105 }
1106 }
1107 argc -= optind;
1108 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001109 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001110
1111 if (argc == 0)
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -05001112 remove_usage(EXIT_SUCCESS);
dp-arm4972ec52016-05-25 16:20:20 +01001113
1114 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
1115 log_errx("File %s already exists, use --force to overwrite it",
1116 outfile);
1117
1118 if (outfile[0] == '\0')
1119 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
1120
1121 parse_fip(argv[0], &toc_header);
1122
dp-arm90d2f0e2016-11-14 15:54:32 +00001123 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001124 if (desc->action != DO_REMOVE)
dp-arm4972ec52016-05-25 16:20:20 +01001125 continue;
dp-arm516dfcb2016-11-03 13:59:26 +00001126
dp-armafa1efa2017-02-14 15:22:13 +00001127 if (desc->image != NULL) {
dp-arm4972ec52016-05-25 16:20:20 +01001128 if (verbose)
dp-arm516dfcb2016-11-03 13:59:26 +00001129 log_dbgx("Removing %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001130 desc->cmdline_name);
dp-armafa1efa2017-02-14 15:22:13 +00001131 free(desc->image);
1132 desc->image = NULL;
dp-arm4972ec52016-05-25 16:20:20 +01001133 } else {
dp-arm516dfcb2016-11-03 13:59:26 +00001134 log_warnx("%s does not exist in %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001135 desc->cmdline_name, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001136 }
1137 }
1138
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001139 pack_images(outfile, toc_header.flags, align);
dp-arm4972ec52016-05-25 16:20:20 +01001140 return 0;
1141}
1142
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -05001143static void remove_usage(int exit_status)
dp-arm4972ec52016-05-25 16:20:20 +01001144{
1145 toc_entry_t *toc_entry = toc_entries;
1146
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001147 printf("fiptool remove [opts] FIP_FILENAME\n");
1148 printf("\n");
1149 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001150 printf(" --align <value>\tEach image is aligned to <value> (default: 1).\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001151 printf(" --blob uuid=...\tRemove an image with the given UUID.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +09001152 printf(" --force\t\tIf the output FIP file already exists, use --force to overwrite it.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001153 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001154 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001155 printf("Specific images are removed with the following options:\n");
1156 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1157 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
1158 toc_entry->name);
Pankaj Guptae4aa1bd2020-12-09 14:02:38 +05301159#ifdef PLAT_DEF_FIP_UUID
1160 toc_entry = plat_def_toc_entries;
1161 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1162 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
1163 toc_entry->name);
1164#endif
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -05001165 exit(exit_status);
dp-arm4972ec52016-05-25 16:20:20 +01001166}
1167
1168static int version_cmd(int argc, char *argv[])
1169{
1170#ifdef VERSION
1171 puts(VERSION);
1172#else
1173 /* If built from fiptool directory, VERSION is not set. */
1174 puts("Unknown version");
1175#endif
1176 return 0;
1177}
1178
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -05001179static void version_usage(int exit_status)
dp-arm4972ec52016-05-25 16:20:20 +01001180{
1181 printf("fiptool version\n");
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -05001182 exit(exit_status);
dp-arm4972ec52016-05-25 16:20:20 +01001183}
1184
1185static int help_cmd(int argc, char *argv[])
1186{
1187 int i;
1188
1189 if (argc < 2)
1190 usage();
1191 argc--, argv++;
1192
1193 for (i = 0; i < NELEM(cmds); i++) {
1194 if (strcmp(cmds[i].name, argv[0]) == 0 &&
dp-arm29f1b5c2016-09-15 09:58:50 +01001195 cmds[i].usage != NULL)
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -05001196 cmds[i].usage(EXIT_SUCCESS);
dp-arm4972ec52016-05-25 16:20:20 +01001197 }
1198 if (i == NELEM(cmds))
1199 printf("No help for subcommand '%s'\n", argv[0]);
1200 return 0;
1201}
1202
1203static void usage(void)
1204{
Masahiro Yamada252c3362017-01-13 02:13:06 +09001205 printf("usage: fiptool [--verbose] <command> [<args>]\n");
dp-arm4972ec52016-05-25 16:20:20 +01001206 printf("Global options supported:\n");
1207 printf(" --verbose\tEnable verbose output for all commands.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001208 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001209 printf("Commands supported:\n");
1210 printf(" info\t\tList images contained in FIP.\n");
1211 printf(" create\tCreate a new FIP with the given images.\n");
1212 printf(" update\tUpdate an existing FIP with the given images.\n");
1213 printf(" unpack\tUnpack images from FIP.\n");
1214 printf(" remove\tRemove images from FIP.\n");
1215 printf(" version\tShow fiptool version.\n");
1216 printf(" help\t\tShow help for given command.\n");
Leonardo Sandovalfc6d3852020-06-29 18:09:24 -05001217 exit(EXIT_SUCCESS);
dp-arm4972ec52016-05-25 16:20:20 +01001218}
1219
1220int main(int argc, char *argv[])
1221{
1222 int i, ret = 0;
1223
dp-arm5cd10ae2016-11-07 10:45:59 +00001224 while (1) {
1225 int c, opt_index = 0;
1226 static struct option opts[] = {
1227 { "verbose", no_argument, NULL, 'v' },
1228 { NULL, no_argument, NULL, 0 }
1229 };
1230
1231 /*
1232 * Set POSIX mode so getopt stops at the first non-option
1233 * which is the subcommand.
1234 */
1235 c = getopt_long(argc, argv, "+v", opts, &opt_index);
1236 if (c == -1)
1237 break;
dp-arm4972ec52016-05-25 16:20:20 +01001238
dp-arm5cd10ae2016-11-07 10:45:59 +00001239 switch (c) {
1240 case 'v':
1241 verbose = 1;
1242 break;
1243 default:
Masahiro Yamada48a24972016-10-26 13:24:26 +09001244 usage();
dp-arm5cd10ae2016-11-07 10:45:59 +00001245 }
dp-arm4972ec52016-05-25 16:20:20 +01001246 }
dp-arm5cd10ae2016-11-07 10:45:59 +00001247 argc -= optind;
1248 argv += optind;
1249 /* Reset optind for subsequent getopt processing. */
1250 optind = 0;
1251
1252 if (argc == 0)
1253 usage();
dp-arm4972ec52016-05-25 16:20:20 +01001254
dp-arm90d2f0e2016-11-14 15:54:32 +00001255 fill_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001256 for (i = 0; i < NELEM(cmds); i++) {
1257 if (strcmp(cmds[i].name, argv[0]) == 0) {
1258 ret = cmds[i].handler(argc, argv);
1259 break;
1260 }
1261 }
1262 if (i == NELEM(cmds))
1263 usage();
dp-arm90d2f0e2016-11-14 15:54:32 +00001264 free_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001265 return ret;
1266}