blob: 0d4f929bb582d25d68c9f923f0c57d65bad7529a [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[]);
27static void info_usage(void);
28static int create_cmd(int argc, char *argv[]);
29static void create_usage(void);
30static int update_cmd(int argc, char *argv[]);
31static void update_usage(void);
32static int unpack_cmd(int argc, char *argv[]);
33static void unpack_usage(void);
34static int remove_cmd(int argc, char *argv[]);
35static void remove_usage(void);
36static int version_cmd(int argc, char *argv[]);
37static void version_usage(void);
38static 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 }
218}
219
dp-arm90d2f0e2016-11-14 15:54:32 +0000220static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid)
dp-arm4972ec52016-05-25 16:20:20 +0100221{
dp-arm90d2f0e2016-11-14 15:54:32 +0000222 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100223
dp-arm90d2f0e2016-11-14 15:54:32 +0000224 for (desc = image_desc_head; desc != NULL; desc = desc->next)
225 if (memcmp(&desc->uuid, uuid, sizeof(uuid_t)) == 0)
226 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100227 return NULL;
228}
229
dp-arm90d2f0e2016-11-14 15:54:32 +0000230static image_desc_t *lookup_image_desc_from_opt(const char *opt)
231{
232 image_desc_t *desc;
233
234 for (desc = image_desc_head; desc != NULL; desc = desc->next)
235 if (strcmp(desc->cmdline_name, opt) == 0)
236 return desc;
237 return NULL;
238}
239
dp-arm516dfcb2016-11-03 13:59:26 +0000240static void uuid_to_str(char *s, size_t len, const uuid_t *u)
241{
242 assert(len >= (_UUID_STR_LEN + 1));
243
Roberto Vargaseace8f12018-04-26 13:36:53 +0100244 snprintf(s, len,
245 "%02X%02X%02X%02X-%02X%02X-%02X%02X-%04X-%04X%04X%04X",
246 u->time_low[0], u->time_low[1], u->time_low[2], u->time_low[3],
247 u->time_mid[0], u->time_mid[1],
248 u->time_hi_and_version[0], u->time_hi_and_version[1],
249 (u->clock_seq_hi_and_reserved << 8) | u->clock_seq_low,
250 (u->node[0] << 8) | u->node[1],
251 (u->node[2] << 8) | u->node[3],
252 (u->node[4] << 8) | u->node[5]);
dp-arm516dfcb2016-11-03 13:59:26 +0000253}
254
255static void uuid_from_str(uuid_t *u, const char *s)
256{
257 int n;
258
259 if (s == NULL)
260 log_errx("UUID cannot be NULL");
261 if (strlen(s) != _UUID_STR_LEN)
262 log_errx("Invalid UUID: %s", s);
263
264 n = sscanf(s,
Roberto Vargaseace8f12018-04-26 13:36:53 +0100265 "%2hhx%2hhx%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
266 &u->time_low[0], &u->time_low[1], &u->time_low[2], &u->time_low[3],
267 &u->time_mid[0], &u->time_mid[1],
268 &u->time_hi_and_version[0], &u->time_hi_and_version[1],
269 &u->clock_seq_hi_and_reserved, &u->clock_seq_low,
270 &u->node[0], &u->node[1],
271 &u->node[2], &u->node[3],
272 &u->node[4], &u->node[5]);
dp-arm516dfcb2016-11-03 13:59:26 +0000273 /*
274 * Given the format specifier above, we expect 11 items to be scanned
275 * for a properly formatted UUID.
276 */
277 if (n != 11)
278 log_errx("Invalid UUID: %s", s);
279}
280
dp-armc1f8e772016-11-04 10:52:25 +0000281static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
dp-arm4972ec52016-05-25 16:20:20 +0100282{
Evan Lloyd04dc3442017-05-25 19:06:47 +0100283 struct BLD_PLAT_STAT st;
dp-arm4972ec52016-05-25 16:20:20 +0100284 FILE *fp;
285 char *buf, *bufend;
286 fip_toc_header_t *toc_header;
287 fip_toc_entry_t *toc_entry;
dp-arm4972ec52016-05-25 16:20:20 +0100288 int terminated = 0;
289
Evan Lloydb1939392017-01-13 14:13:09 +0000290 fp = fopen(filename, "rb");
dp-arm4972ec52016-05-25 16:20:20 +0100291 if (fp == NULL)
292 log_err("fopen %s", filename);
293
294 if (fstat(fileno(fp), &st) == -1)
295 log_err("fstat %s", filename);
296
dp-armdb0f5e92016-11-04 10:56:25 +0000297 buf = xmalloc(st.st_size, "failed to load file into memory");
dp-arm4972ec52016-05-25 16:20:20 +0100298 if (fread(buf, 1, st.st_size, fp) != st.st_size)
299 log_errx("Failed to read %s", filename);
300 bufend = buf + st.st_size;
301 fclose(fp);
302
303 if (st.st_size < sizeof(fip_toc_header_t))
304 log_errx("FIP %s is truncated", filename);
305
306 toc_header = (fip_toc_header_t *)buf;
307 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
308
309 if (toc_header->name != TOC_HEADER_NAME)
310 log_errx("%s is not a FIP file", filename);
311
312 /* Return the ToC header if the caller wants it. */
313 if (toc_header_out != NULL)
314 *toc_header_out = *toc_header;
315
316 /* Walk through each ToC entry in the file. */
317 while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
dp-arm516dfcb2016-11-03 13:59:26 +0000318 image_t *image;
319 image_desc_t *desc;
320
dp-arm4972ec52016-05-25 16:20:20 +0100321 /* Found the ToC terminator, we are done. */
322 if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
323 terminated = 1;
324 break;
325 }
326
327 /*
328 * Build a new image out of the ToC entry and add it to the
329 * table of images.
330 */
Masahiro Yamadad224b452017-01-14 23:22:02 +0900331 image = xzalloc(sizeof(*image),
dp-armdb0f5e92016-11-04 10:56:25 +0000332 "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900333 image->toc_e = *toc_entry;
dp-armdb0f5e92016-11-04 10:56:25 +0000334 image->buffer = xmalloc(toc_entry->size,
335 "failed to allocate image buffer, is FIP file corrupted?");
dp-arm4972ec52016-05-25 16:20:20 +0100336 /* Overflow checks before memory copy. */
337 if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
338 log_errx("FIP %s is corrupted", filename);
339 if (toc_entry->size + toc_entry->offset_address > st.st_size)
340 log_errx("FIP %s is corrupted", filename);
341
342 memcpy(image->buffer, buf + toc_entry->offset_address,
343 toc_entry->size);
dp-arm4972ec52016-05-25 16:20:20 +0100344
dp-arm516dfcb2016-11-03 13:59:26 +0000345 /* If this is an unknown image, create a descriptor for it. */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900346 desc = lookup_image_desc_from_uuid(&toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000347 if (desc == NULL) {
348 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
349
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900350 uuid_to_str(name, sizeof(name), &toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000351 snprintf(filename, sizeof(filename), "%s%s",
352 name, ".bin");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900353 desc = new_image_desc(&toc_entry->uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000354 desc->action = DO_UNPACK;
355 desc->action_arg = xstrdup(filename,
356 "failed to allocate memory for blob filename");
357 add_image_desc(desc);
358 }
359
dp-armafa1efa2017-02-14 15:22:13 +0000360 assert(desc->image == NULL);
361 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100362
363 toc_entry++;
364 }
365
366 if (terminated == 0)
367 log_errx("FIP %s does not have a ToC terminator entry",
368 filename);
369 free(buf);
370 return 0;
371}
372
dp-armc1f8e772016-11-04 10:52:25 +0000373static image_t *read_image_from_file(const uuid_t *uuid, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100374{
Evan Lloyd04dc3442017-05-25 19:06:47 +0100375 struct BLD_PLAT_STAT st;
dp-arm4972ec52016-05-25 16:20:20 +0100376 image_t *image;
377 FILE *fp;
378
dp-arm715ef422016-08-30 14:18:58 +0100379 assert(uuid != NULL);
Evan Lloyd04dc3442017-05-25 19:06:47 +0100380 assert(filename != NULL);
dp-arm715ef422016-08-30 14:18:58 +0100381
Evan Lloydb1939392017-01-13 14:13:09 +0000382 fp = fopen(filename, "rb");
dp-arm4972ec52016-05-25 16:20:20 +0100383 if (fp == NULL)
384 log_err("fopen %s", filename);
385
386 if (fstat(fileno(fp), &st) == -1)
387 log_errx("fstat %s", filename);
388
Masahiro Yamadad224b452017-01-14 23:22:02 +0900389 image = xzalloc(sizeof(*image), "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900390 image->toc_e.uuid = *uuid;
dp-armdb0f5e92016-11-04 10:56:25 +0000391 image->buffer = xmalloc(st.st_size, "failed to allocate image buffer");
dp-arm4972ec52016-05-25 16:20:20 +0100392 if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
393 log_errx("Failed to read %s", filename);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900394 image->toc_e.size = st.st_size;
dp-arm4972ec52016-05-25 16:20:20 +0100395
396 fclose(fp);
397 return image;
398}
399
dp-armc1f8e772016-11-04 10:52:25 +0000400static int write_image_to_file(const image_t *image, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100401{
402 FILE *fp;
403
Evan Lloydb1939392017-01-13 14:13:09 +0000404 fp = fopen(filename, "wb");
dp-arm4972ec52016-05-25 16:20:20 +0100405 if (fp == NULL)
406 log_err("fopen");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900407 xfwrite(image->buffer, image->toc_e.size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100408 fclose(fp);
409 return 0;
410}
411
dp-arm90d2f0e2016-11-14 15:54:32 +0000412static struct option *add_opt(struct option *opts, size_t *nr_opts,
413 const char *name, int has_arg, int val)
dp-arm4972ec52016-05-25 16:20:20 +0100414{
dp-arm90d2f0e2016-11-14 15:54:32 +0000415 opts = realloc(opts, (*nr_opts + 1) * sizeof(*opts));
416 if (opts == NULL)
417 log_err("realloc");
418 opts[*nr_opts].name = name;
419 opts[*nr_opts].has_arg = has_arg;
420 opts[*nr_opts].flag = NULL;
421 opts[*nr_opts].val = val;
422 ++*nr_opts;
423 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100424}
425
dp-arm90d2f0e2016-11-14 15:54:32 +0000426static struct option *fill_common_opts(struct option *opts, size_t *nr_opts,
427 int has_arg)
dp-arm4972ec52016-05-25 16:20:20 +0100428{
dp-arm90d2f0e2016-11-14 15:54:32 +0000429 image_desc_t *desc;
430
431 for (desc = image_desc_head; desc != NULL; desc = desc->next)
432 opts = add_opt(opts, nr_opts, desc->cmdline_name, has_arg,
433 OPT_TOC_ENTRY);
434 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100435}
436
dp-arm90d2f0e2016-11-14 15:54:32 +0000437static void md_print(const unsigned char *md, size_t len)
dp-arm12e893b2016-08-24 13:21:08 +0100438{
439 size_t i;
440
441 for (i = 0; i < len; i++)
442 printf("%02x", md[i]);
443}
444
dp-arm4972ec52016-05-25 16:20:20 +0100445static int info_cmd(int argc, char *argv[])
446{
dp-armafa1efa2017-02-14 15:22:13 +0000447 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100448 fip_toc_header_t toc_header;
dp-arm4972ec52016-05-25 16:20:20 +0100449
450 if (argc != 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100451 info_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100452 argc--, argv++;
453
454 parse_fip(argv[0], &toc_header);
455
456 if (verbose) {
457 log_dbgx("toc_header[name]: 0x%llX",
458 (unsigned long long)toc_header.name);
459 log_dbgx("toc_header[serial_number]: 0x%llX",
460 (unsigned long long)toc_header.serial_number);
461 log_dbgx("toc_header[flags]: 0x%llX",
462 (unsigned long long)toc_header.flags);
463 }
464
dp-armafa1efa2017-02-14 15:22:13 +0000465 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
466 image_t *image = desc->image;
dp-arm715ef422016-08-30 14:18:58 +0100467
dp-armafa1efa2017-02-14 15:22:13 +0000468 if (image == NULL)
469 continue;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900470 printf("%s: offset=0x%llX, size=0x%llX, cmdline=\"--%s\"",
471 desc->name,
472 (unsigned long long)image->toc_e.offset_address,
473 (unsigned long long)image->toc_e.size,
Masahiro Yamadaeee39312017-01-15 23:20:00 +0900474 desc->cmdline_name);
Evan Lloyd04dc3442017-05-25 19:06:47 +0100475#ifndef _MSC_VER /* We don't have SHA256 for Visual Studio. */
dp-arm12e893b2016-08-24 13:21:08 +0100476 if (verbose) {
477 unsigned char md[SHA256_DIGEST_LENGTH];
478
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900479 SHA256(image->buffer, image->toc_e.size, md);
dp-arm12e893b2016-08-24 13:21:08 +0100480 printf(", sha256=");
481 md_print(md, sizeof(md));
482 }
Evan Lloyd04dc3442017-05-25 19:06:47 +0100483#endif
dp-arm12e893b2016-08-24 13:21:08 +0100484 putchar('\n');
dp-arm4972ec52016-05-25 16:20:20 +0100485 }
486
dp-arm4972ec52016-05-25 16:20:20 +0100487 return 0;
488}
489
490static void info_usage(void)
491{
492 printf("fiptool info FIP_FILENAME\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100493 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100494}
495
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900496static int pack_images(const char *filename, uint64_t toc_flags, unsigned long align)
dp-arm4972ec52016-05-25 16:20:20 +0100497{
498 FILE *fp;
dp-armafa1efa2017-02-14 15:22:13 +0000499 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100500 fip_toc_header_t *toc_header;
501 fip_toc_entry_t *toc_entry;
502 char *buf;
Roberto Vargas81935762017-12-19 11:56:57 +0000503 uint64_t entry_offset, buf_size, payload_size = 0, pad_size;
dp-armafa1efa2017-02-14 15:22:13 +0000504 size_t nr_images = 0;
505
506 for (desc = image_desc_head; desc != NULL; desc = desc->next)
507 if (desc->image != NULL)
508 nr_images++;
dp-arm4972ec52016-05-25 16:20:20 +0100509
510 buf_size = sizeof(fip_toc_header_t) +
511 sizeof(fip_toc_entry_t) * (nr_images + 1);
512 buf = calloc(1, buf_size);
513 if (buf == NULL)
514 log_err("calloc");
515
516 /* Build up header and ToC entries from the image table. */
517 toc_header = (fip_toc_header_t *)buf;
518 toc_header->name = TOC_HEADER_NAME;
519 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
520 toc_header->flags = toc_flags;
521
522 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
523
524 entry_offset = buf_size;
dp-armafa1efa2017-02-14 15:22:13 +0000525 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
526 image_t *image = desc->image;
527
528 if (image == NULL)
529 continue;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900530 payload_size += image->toc_e.size;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900531 entry_offset = (entry_offset + align - 1) & ~(align - 1);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900532 image->toc_e.offset_address = entry_offset;
533 *toc_entry++ = image->toc_e;
534 entry_offset += image->toc_e.size;
dp-arm4972ec52016-05-25 16:20:20 +0100535 }
536
Roberto Vargas81935762017-12-19 11:56:57 +0000537 /*
538 * Append a null uuid entry to mark the end of ToC entries.
539 * NOTE the offset address for the last toc_entry must match the fip
540 * size.
541 */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900542 memset(toc_entry, 0, sizeof(*toc_entry));
Roberto Vargas81935762017-12-19 11:56:57 +0000543 toc_entry->offset_address = (entry_offset + align - 1) & ~(align - 1);
dp-arm4972ec52016-05-25 16:20:20 +0100544
545 /* Generate the FIP file. */
Evan Lloydb1939392017-01-13 14:13:09 +0000546 fp = fopen(filename, "wb");
dp-arm4972ec52016-05-25 16:20:20 +0100547 if (fp == NULL)
548 log_err("fopen %s", filename);
549
550 if (verbose)
551 log_dbgx("Metadata size: %zu bytes", buf_size);
552
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900553 xfwrite(buf, buf_size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100554
555 if (verbose)
556 log_dbgx("Payload size: %zu bytes", payload_size);
557
dp-armafa1efa2017-02-14 15:22:13 +0000558 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
559 image_t *image = desc->image;
560
561 if (image == NULL)
562 continue;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900563 if (fseek(fp, image->toc_e.offset_address, SEEK_SET))
564 log_errx("Failed to set file position");
565
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900566 xfwrite(image->buffer, image->toc_e.size, fp, filename);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900567 }
dp-arm4972ec52016-05-25 16:20:20 +0100568
Roberto Vargas81935762017-12-19 11:56:57 +0000569 if (fseek(fp, entry_offset, SEEK_SET))
570 log_errx("Failed to set file position");
571
572 pad_size = toc_entry->offset_address - entry_offset;
573 while (pad_size--)
574 fputc(0x0, fp);
575
Andreas Färber860b5dc2018-01-27 16:46:59 +0100576 free(buf);
dp-arm4972ec52016-05-25 16:20:20 +0100577 fclose(fp);
578 return 0;
579}
580
581/*
582 * This function is shared between the create and update subcommands.
583 * The difference between the two subcommands is that when the FIP file
584 * is created, the parsing of an existing FIP is skipped. This results
585 * in update_fip() creating the new FIP file from scratch because the
586 * internal image table is not populated.
587 */
588static void update_fip(void)
589{
dp-arm90d2f0e2016-11-14 15:54:32 +0000590 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100591
592 /* Add or replace images in the FIP file. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000593 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-armafa1efa2017-02-14 15:22:13 +0000594 image_t *image;
dp-arm516dfcb2016-11-03 13:59:26 +0000595
dp-arm90d2f0e2016-11-14 15:54:32 +0000596 if (desc->action != DO_PACK)
dp-arm4972ec52016-05-25 16:20:20 +0100597 continue;
598
dp-armafa1efa2017-02-14 15:22:13 +0000599 image = read_image_from_file(&desc->uuid,
dp-arm90d2f0e2016-11-14 15:54:32 +0000600 desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000601 if (desc->image != NULL) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000602 if (verbose) {
dp-arm516dfcb2016-11-03 13:59:26 +0000603 log_dbgx("Replacing %s with %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000604 desc->cmdline_name,
605 desc->action_arg);
606 }
dp-armafa1efa2017-02-14 15:22:13 +0000607 free(desc->image);
608 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100609 } else {
610 if (verbose)
611 log_dbgx("Adding image %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000612 desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000613 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100614 }
dp-arm4972ec52016-05-25 16:20:20 +0100615 }
616}
617
dp-arm90d2f0e2016-11-14 15:54:32 +0000618static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
dp-arm4972ec52016-05-25 16:20:20 +0100619{
620 unsigned long long flags;
621 char *endptr;
622
623 errno = 0;
624 flags = strtoull(arg, &endptr, 16);
625 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
626 log_errx("Invalid platform ToC flags: %s", arg);
627 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
628 *toc_flags |= flags << 32;
629}
630
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900631static int is_power_of_2(unsigned long x)
632{
633 return x && !(x & (x - 1));
634}
635
636static unsigned long get_image_align(char *arg)
637{
638 char *endptr;
639 unsigned long align;
640
641 errno = 0;
Andreas Färber242a7b72017-04-21 19:39:10 +0200642 align = strtoul(arg, &endptr, 0);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900643 if (*endptr != '\0' || !is_power_of_2(align) || errno != 0)
644 log_errx("Invalid alignment: %s", arg);
645
646 return align;
647}
648
dp-arm516dfcb2016-11-03 13:59:26 +0000649static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
650{
651 char *p;
652
653 for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
654 if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
655 p += strlen("uuid=");
656 uuid_from_str(uuid, p);
657 } else if (strncmp(p, "file=", strlen("file=")) == 0) {
658 p += strlen("file=");
659 snprintf(filename, len, "%s", p);
660 }
661 }
662}
663
dp-arm4972ec52016-05-25 16:20:20 +0100664static int create_cmd(int argc, char *argv[])
665{
dp-arm90d2f0e2016-11-14 15:54:32 +0000666 struct option *opts = NULL;
667 size_t nr_opts = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100668 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900669 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100670
671 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100672 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100673
dp-arm90d2f0e2016-11-14 15:54:32 +0000674 opts = fill_common_opts(opts, &nr_opts, required_argument);
675 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100676 OPT_PLAT_TOC_FLAGS);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900677 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000678 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000679 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100680
681 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000682 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100683
dp-arm516dfcb2016-11-03 13:59:26 +0000684 c = getopt_long(argc, argv, "b:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100685 if (c == -1)
686 break;
687
688 switch (c) {
689 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000690 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100691
dp-arm90d2f0e2016-11-14 15:54:32 +0000692 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000693 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100694 break;
695 }
696 case OPT_PLAT_TOC_FLAGS:
697 parse_plat_toc_flags(optarg, &toc_flags);
698 break;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900699 case OPT_ALIGN:
700 align = get_image_align(optarg);
701 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000702 case 'b': {
703 char name[_UUID_STR_LEN + 1];
704 char filename[PATH_MAX] = { 0 };
Roberto Vargaseace8f12018-04-26 13:36:53 +0100705 uuid_t uuid = uuid_null;
dp-arm516dfcb2016-11-03 13:59:26 +0000706 image_desc_t *desc;
707
708 parse_blob_opt(optarg, &uuid,
709 filename, sizeof(filename));
710
711 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
712 filename[0] == '\0')
713 create_usage();
714
715 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000716 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000717 uuid_to_str(name, sizeof(name), &uuid);
718 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000719 add_image_desc(desc);
720 }
dp-armfb732312016-12-30 09:55:48 +0000721 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000722 break;
723 }
dp-arm4972ec52016-05-25 16:20:20 +0100724 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100725 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100726 }
727 }
728 argc -= optind;
729 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000730 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100731
732 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100733 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100734
735 update_fip();
736
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900737 pack_images(argv[0], toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100738 return 0;
739}
740
741static void create_usage(void)
742{
743 toc_entry_t *toc_entry = toc_entries;
744
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900745 printf("fiptool create [opts] FIP_FILENAME\n");
746 printf("\n");
747 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900748 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900749 printf(" --blob uuid=...,file=...\tAdd an image with the given UUID pointed to by file.\n");
750 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 +0900751 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100752 printf("Specific images are packed with the following options:\n");
753 for (; toc_entry->cmdline_name != NULL; toc_entry++)
754 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
755 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100756 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100757}
758
759static int update_cmd(int argc, char *argv[])
760{
dp-arm90d2f0e2016-11-14 15:54:32 +0000761 struct option *opts = NULL;
762 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000763 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100764 fip_toc_header_t toc_header = { 0 };
765 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900766 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100767 int pflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100768
769 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100770 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100771
dp-arm90d2f0e2016-11-14 15:54:32 +0000772 opts = fill_common_opts(opts, &nr_opts, required_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900773 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000774 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000775 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
776 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100777 OPT_PLAT_TOC_FLAGS);
dp-arm90d2f0e2016-11-14 15:54:32 +0000778 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100779
780 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000781 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100782
dp-arm516dfcb2016-11-03 13:59:26 +0000783 c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100784 if (c == -1)
785 break;
786
787 switch (c) {
788 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000789 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100790
dp-arm90d2f0e2016-11-14 15:54:32 +0000791 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000792 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100793 break;
794 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000795 case OPT_PLAT_TOC_FLAGS:
dp-arm4972ec52016-05-25 16:20:20 +0100796 parse_plat_toc_flags(optarg, &toc_flags);
797 pflag = 1;
798 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000799 case 'b': {
800 char name[_UUID_STR_LEN + 1];
801 char filename[PATH_MAX] = { 0 };
Roberto Vargaseace8f12018-04-26 13:36:53 +0100802 uuid_t uuid = uuid_null;
dp-arm516dfcb2016-11-03 13:59:26 +0000803 image_desc_t *desc;
804
805 parse_blob_opt(optarg, &uuid,
806 filename, sizeof(filename));
807
808 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
809 filename[0] == '\0')
810 update_usage();
811
812 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000813 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000814 uuid_to_str(name, sizeof(name), &uuid);
815 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000816 add_image_desc(desc);
817 }
dp-armfb732312016-12-30 09:55:48 +0000818 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000819 break;
820 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900821 case OPT_ALIGN:
822 align = get_image_align(optarg);
823 break;
dp-arm4972ec52016-05-25 16:20:20 +0100824 case 'o':
825 snprintf(outfile, sizeof(outfile), "%s", optarg);
826 break;
827 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100828 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100829 }
830 }
831 argc -= optind;
832 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000833 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100834
835 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100836 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100837
838 if (outfile[0] == '\0')
839 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
840
Masahiro Yamadaebb6e372016-12-25 12:41:41 +0900841 if (access(argv[0], F_OK) == 0)
dp-arm4972ec52016-05-25 16:20:20 +0100842 parse_fip(argv[0], &toc_header);
843
844 if (pflag)
845 toc_header.flags &= ~(0xffffULL << 32);
846 toc_flags = (toc_header.flags |= toc_flags);
847
848 update_fip();
849
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900850 pack_images(outfile, toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100851 return 0;
852}
853
854static void update_usage(void)
855{
856 toc_entry_t *toc_entry = toc_entries;
857
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900858 printf("fiptool update [opts] FIP_FILENAME\n");
859 printf("\n");
860 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900861 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900862 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 +0100863 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900864 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 +0900865 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100866 printf("Specific images are packed with the following options:\n");
867 for (; toc_entry->cmdline_name != NULL; toc_entry++)
868 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
869 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100870 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100871}
872
873static int unpack_cmd(int argc, char *argv[])
874{
dp-arm90d2f0e2016-11-14 15:54:32 +0000875 struct option *opts = NULL;
876 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000877 char outdir[PATH_MAX] = { 0 };
dp-arm90d2f0e2016-11-14 15:54:32 +0000878 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100879 int fflag = 0;
880 int unpack_all = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100881
882 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100883 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100884
dp-arm90d2f0e2016-11-14 15:54:32 +0000885 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000886 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000887 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
888 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
889 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100890
891 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000892 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100893
dp-arm516dfcb2016-11-03 13:59:26 +0000894 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100895 if (c == -1)
896 break;
897
898 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000899 case OPT_TOC_ENTRY: {
900 image_desc_t *desc;
901
902 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000903 set_image_desc_action(desc, DO_UNPACK, optarg);
dp-arm90d2f0e2016-11-14 15:54:32 +0000904 unpack_all = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100905 break;
dp-arm90d2f0e2016-11-14 15:54:32 +0000906 }
dp-arm516dfcb2016-11-03 13:59:26 +0000907 case 'b': {
908 char name[_UUID_STR_LEN + 1];
909 char filename[PATH_MAX] = { 0 };
Roberto Vargaseace8f12018-04-26 13:36:53 +0100910 uuid_t uuid = uuid_null;
dp-arm516dfcb2016-11-03 13:59:26 +0000911 image_desc_t *desc;
912
913 parse_blob_opt(optarg, &uuid,
914 filename, sizeof(filename));
915
916 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
917 filename[0] == '\0')
918 unpack_usage();
919
920 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000921 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000922 uuid_to_str(name, sizeof(name), &uuid);
923 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000924 add_image_desc(desc);
925 }
dp-armfb732312016-12-30 09:55:48 +0000926 set_image_desc_action(desc, DO_UNPACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000927 unpack_all = 0;
928 break;
929 }
dp-arm4972ec52016-05-25 16:20:20 +0100930 case 'f':
931 fflag = 1;
932 break;
933 case 'o':
934 snprintf(outdir, sizeof(outdir), "%s", optarg);
935 break;
936 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100937 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100938 }
939 }
940 argc -= optind;
941 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000942 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100943
944 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100945 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100946
947 parse_fip(argv[0], NULL);
948
949 if (outdir[0] != '\0')
950 if (chdir(outdir) == -1)
951 log_err("chdir %s", outdir);
952
dp-arm4972ec52016-05-25 16:20:20 +0100953 /* Unpack all specified images. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000954 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +0000955 char file[PATH_MAX];
dp-armafa1efa2017-02-14 15:22:13 +0000956 image_t *image = desc->image;
dp-arm715ef422016-08-30 14:18:58 +0100957
dp-arm90d2f0e2016-11-14 15:54:32 +0000958 if (!unpack_all && desc->action != DO_UNPACK)
dp-arm4972ec52016-05-25 16:20:20 +0100959 continue;
960
961 /* Build filename. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000962 if (desc->action_arg == NULL)
dp-arm4972ec52016-05-25 16:20:20 +0100963 snprintf(file, sizeof(file), "%s.bin",
dp-arm90d2f0e2016-11-14 15:54:32 +0000964 desc->cmdline_name);
dp-arm4972ec52016-05-25 16:20:20 +0100965 else
966 snprintf(file, sizeof(file), "%s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000967 desc->action_arg);
dp-arm4972ec52016-05-25 16:20:20 +0100968
dp-arm715ef422016-08-30 14:18:58 +0100969 if (image == NULL) {
970 if (!unpack_all)
dp-arm516dfcb2016-11-03 13:59:26 +0000971 log_warnx("%s does not exist in %s",
dp-arm715ef422016-08-30 14:18:58 +0100972 file, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +0100973 continue;
974 }
975
976 if (access(file, F_OK) != 0 || fflag) {
977 if (verbose)
978 log_dbgx("Unpacking %s", file);
dp-arm715ef422016-08-30 14:18:58 +0100979 write_image_to_file(image, file);
dp-arm4972ec52016-05-25 16:20:20 +0100980 } else {
981 log_warnx("File %s already exists, use --force to overwrite it",
982 file);
983 }
dp-arm4972ec52016-05-25 16:20:20 +0100984 }
985
dp-arm4972ec52016-05-25 16:20:20 +0100986 return 0;
987}
988
989static void unpack_usage(void)
990{
991 toc_entry_t *toc_entry = toc_entries;
992
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900993 printf("fiptool unpack [opts] FIP_FILENAME\n");
994 printf("\n");
995 printf("Options:\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900996 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID to file.\n");
997 printf(" --force\t\t\tIf the output file already exists, use --force to overwrite it.\n");
dp-arm516dfcb2016-11-03 13:59:26 +0000998 printf(" --out path\t\t\tSet the output directory path.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +0900999 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001000 printf("Specific images are unpacked with the following options:\n");
1001 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1002 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
1003 toc_entry->name);
Masahiro Yamada1b900152017-02-02 16:34:14 +09001004 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001005 printf("If no options are provided, all images will be unpacked.\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001006 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001007}
1008
1009static int remove_cmd(int argc, char *argv[])
1010{
dp-arm90d2f0e2016-11-14 15:54:32 +00001011 struct option *opts = NULL;
1012 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +00001013 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +01001014 fip_toc_header_t toc_header;
dp-arm90d2f0e2016-11-14 15:54:32 +00001015 image_desc_t *desc;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001016 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +01001017 int fflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001018
1019 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +01001020 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001021
dp-arm90d2f0e2016-11-14 15:54:32 +00001022 opts = fill_common_opts(opts, &nr_opts, no_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001023 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +00001024 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +00001025 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
1026 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
1027 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +01001028
1029 while (1) {
dp-armfe92b892016-11-07 11:13:54 +00001030 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001031
dp-arm516dfcb2016-11-03 13:59:26 +00001032 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +01001033 if (c == -1)
1034 break;
1035
1036 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001037 case OPT_TOC_ENTRY: {
1038 image_desc_t *desc;
1039
1040 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +00001041 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm4972ec52016-05-25 16:20:20 +01001042 break;
dp-arm90d2f0e2016-11-14 15:54:32 +00001043 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001044 case OPT_ALIGN:
1045 align = get_image_align(optarg);
1046 break;
dp-arm516dfcb2016-11-03 13:59:26 +00001047 case 'b': {
1048 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
Roberto Vargaseace8f12018-04-26 13:36:53 +01001049 uuid_t uuid = uuid_null;
dp-arm516dfcb2016-11-03 13:59:26 +00001050 image_desc_t *desc;
1051
1052 parse_blob_opt(optarg, &uuid,
1053 filename, sizeof(filename));
1054
1055 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
1056 remove_usage();
1057
1058 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +00001059 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +00001060 uuid_to_str(name, sizeof(name), &uuid);
1061 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +00001062 add_image_desc(desc);
1063 }
dp-armfb732312016-12-30 09:55:48 +00001064 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm516dfcb2016-11-03 13:59:26 +00001065 break;
1066 }
dp-arm4972ec52016-05-25 16:20:20 +01001067 case 'f':
1068 fflag = 1;
1069 break;
1070 case 'o':
1071 snprintf(outfile, sizeof(outfile), "%s", optarg);
1072 break;
1073 default:
dp-arm29f1b5c2016-09-15 09:58:50 +01001074 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001075 }
1076 }
1077 argc -= optind;
1078 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001079 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001080
1081 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001082 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001083
1084 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
1085 log_errx("File %s already exists, use --force to overwrite it",
1086 outfile);
1087
1088 if (outfile[0] == '\0')
1089 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
1090
1091 parse_fip(argv[0], &toc_header);
1092
dp-arm90d2f0e2016-11-14 15:54:32 +00001093 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001094 if (desc->action != DO_REMOVE)
dp-arm4972ec52016-05-25 16:20:20 +01001095 continue;
dp-arm516dfcb2016-11-03 13:59:26 +00001096
dp-armafa1efa2017-02-14 15:22:13 +00001097 if (desc->image != NULL) {
dp-arm4972ec52016-05-25 16:20:20 +01001098 if (verbose)
dp-arm516dfcb2016-11-03 13:59:26 +00001099 log_dbgx("Removing %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001100 desc->cmdline_name);
dp-armafa1efa2017-02-14 15:22:13 +00001101 free(desc->image);
1102 desc->image = NULL;
dp-arm4972ec52016-05-25 16:20:20 +01001103 } else {
dp-arm516dfcb2016-11-03 13:59:26 +00001104 log_warnx("%s does not exist in %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001105 desc->cmdline_name, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001106 }
1107 }
1108
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001109 pack_images(outfile, toc_header.flags, align);
dp-arm4972ec52016-05-25 16:20:20 +01001110 return 0;
1111}
1112
1113static void remove_usage(void)
1114{
1115 toc_entry_t *toc_entry = toc_entries;
1116
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001117 printf("fiptool remove [opts] FIP_FILENAME\n");
1118 printf("\n");
1119 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001120 printf(" --align <value>\tEach image is aligned to <value> (default: 1).\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001121 printf(" --blob uuid=...\tRemove an image with the given UUID.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +09001122 printf(" --force\t\tIf the output FIP file already exists, use --force to overwrite it.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001123 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001124 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001125 printf("Specific images are removed with the following options:\n");
1126 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1127 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
1128 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +01001129 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001130}
1131
1132static int version_cmd(int argc, char *argv[])
1133{
1134#ifdef VERSION
1135 puts(VERSION);
1136#else
1137 /* If built from fiptool directory, VERSION is not set. */
1138 puts("Unknown version");
1139#endif
1140 return 0;
1141}
1142
1143static void version_usage(void)
1144{
1145 printf("fiptool version\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001146 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001147}
1148
1149static int help_cmd(int argc, char *argv[])
1150{
1151 int i;
1152
1153 if (argc < 2)
1154 usage();
1155 argc--, argv++;
1156
1157 for (i = 0; i < NELEM(cmds); i++) {
1158 if (strcmp(cmds[i].name, argv[0]) == 0 &&
dp-arm29f1b5c2016-09-15 09:58:50 +01001159 cmds[i].usage != NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001160 cmds[i].usage();
dp-arm4972ec52016-05-25 16:20:20 +01001161 }
1162 if (i == NELEM(cmds))
1163 printf("No help for subcommand '%s'\n", argv[0]);
1164 return 0;
1165}
1166
1167static void usage(void)
1168{
Masahiro Yamada252c3362017-01-13 02:13:06 +09001169 printf("usage: fiptool [--verbose] <command> [<args>]\n");
dp-arm4972ec52016-05-25 16:20:20 +01001170 printf("Global options supported:\n");
1171 printf(" --verbose\tEnable verbose output for all commands.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001172 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001173 printf("Commands supported:\n");
1174 printf(" info\t\tList images contained in FIP.\n");
1175 printf(" create\tCreate a new FIP with the given images.\n");
1176 printf(" update\tUpdate an existing FIP with the given images.\n");
1177 printf(" unpack\tUnpack images from FIP.\n");
1178 printf(" remove\tRemove images from FIP.\n");
1179 printf(" version\tShow fiptool version.\n");
1180 printf(" help\t\tShow help for given command.\n");
1181 exit(1);
1182}
1183
1184int main(int argc, char *argv[])
1185{
1186 int i, ret = 0;
1187
dp-arm5cd10ae2016-11-07 10:45:59 +00001188 while (1) {
1189 int c, opt_index = 0;
1190 static struct option opts[] = {
1191 { "verbose", no_argument, NULL, 'v' },
1192 { NULL, no_argument, NULL, 0 }
1193 };
1194
1195 /*
1196 * Set POSIX mode so getopt stops at the first non-option
1197 * which is the subcommand.
1198 */
1199 c = getopt_long(argc, argv, "+v", opts, &opt_index);
1200 if (c == -1)
1201 break;
dp-arm4972ec52016-05-25 16:20:20 +01001202
dp-arm5cd10ae2016-11-07 10:45:59 +00001203 switch (c) {
1204 case 'v':
1205 verbose = 1;
1206 break;
1207 default:
Masahiro Yamada48a24972016-10-26 13:24:26 +09001208 usage();
dp-arm5cd10ae2016-11-07 10:45:59 +00001209 }
dp-arm4972ec52016-05-25 16:20:20 +01001210 }
dp-arm5cd10ae2016-11-07 10:45:59 +00001211 argc -= optind;
1212 argv += optind;
1213 /* Reset optind for subsequent getopt processing. */
1214 optind = 0;
1215
1216 if (argc == 0)
1217 usage();
dp-arm4972ec52016-05-25 16:20:20 +01001218
dp-arm90d2f0e2016-11-14 15:54:32 +00001219 fill_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001220 for (i = 0; i < NELEM(cmds); i++) {
1221 if (strcmp(cmds[i].name, argv[0]) == 0) {
1222 ret = cmds[i].handler(argc, argv);
1223 break;
1224 }
1225 }
1226 if (i == NELEM(cmds))
1227 usage();
dp-arm90d2f0e2016-11-14 15:54:32 +00001228 free_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001229 return ret;
1230}