blob: 7bfd2720e2bf13bdf89ad90ec0ae626fe0564db6 [file] [log] [blame]
dp-arm4972ec52016-05-25 16:20:20 +01001/*
2 * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
3 *
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>
12#include <getopt.h>
13#include <limits.h>
14#include <stdarg.h>
15#include <stdint.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <unistd.h>
20
dp-arm12e893b2016-08-24 13:21:08 +010021#include <openssl/sha.h>
22
dp-arm4972ec52016-05-25 16:20:20 +010023#include "fiptool.h"
24#include "firmware_image_package.h"
25#include "tbbr_config.h"
26
27#define OPT_TOC_ENTRY 0
28#define OPT_PLAT_TOC_FLAGS 1
Masahiro Yamada4d87eb42016-12-25 13:52:22 +090029#define OPT_ALIGN 2
dp-arm4972ec52016-05-25 16:20:20 +010030
31static int info_cmd(int argc, char *argv[]);
32static void info_usage(void);
33static int create_cmd(int argc, char *argv[]);
34static void create_usage(void);
35static int update_cmd(int argc, char *argv[]);
36static void update_usage(void);
37static int unpack_cmd(int argc, char *argv[]);
38static void unpack_usage(void);
39static int remove_cmd(int argc, char *argv[]);
40static void remove_usage(void);
41static int version_cmd(int argc, char *argv[]);
42static void version_usage(void);
43static int help_cmd(int argc, char *argv[]);
44static void usage(void);
45
46/* Available subcommands. */
47static cmd_t cmds[] = {
48 { .name = "info", .handler = info_cmd, .usage = info_usage },
49 { .name = "create", .handler = create_cmd, .usage = create_usage },
50 { .name = "update", .handler = update_cmd, .usage = update_usage },
51 { .name = "unpack", .handler = unpack_cmd, .usage = unpack_usage },
52 { .name = "remove", .handler = remove_cmd, .usage = remove_usage },
53 { .name = "version", .handler = version_cmd, .usage = version_usage },
54 { .name = "help", .handler = help_cmd, .usage = NULL },
55};
56
dp-arm90d2f0e2016-11-14 15:54:32 +000057static image_desc_t *image_desc_head;
58static size_t nr_image_descs;
dp-arm4972ec52016-05-25 16:20:20 +010059static uuid_t uuid_null = { 0 };
60static int verbose;
61
dp-armc1f8e772016-11-04 10:52:25 +000062static void vlog(int prio, const char *msg, va_list ap)
dp-arm4972ec52016-05-25 16:20:20 +010063{
64 char *prefix[] = { "DEBUG", "WARN", "ERROR" };
65
66 fprintf(stderr, "%s: ", prefix[prio]);
67 vfprintf(stderr, msg, ap);
68 fputc('\n', stderr);
69}
70
dp-armc1f8e772016-11-04 10:52:25 +000071static void log_dbgx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010072{
73 va_list ap;
74
75 va_start(ap, msg);
76 vlog(LOG_DBG, msg, ap);
77 va_end(ap);
78}
79
dp-armc1f8e772016-11-04 10:52:25 +000080static void log_warnx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010081{
82 va_list ap;
83
84 va_start(ap, msg);
85 vlog(LOG_WARN, msg, ap);
86 va_end(ap);
87}
88
dp-armc1f8e772016-11-04 10:52:25 +000089static void log_err(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010090{
91 char buf[512];
92 va_list ap;
93
94 va_start(ap, msg);
95 snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
96 vlog(LOG_ERR, buf, ap);
97 va_end(ap);
98 exit(1);
99}
100
dp-armc1f8e772016-11-04 10:52:25 +0000101static void log_errx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +0100102{
103 va_list ap;
104
105 va_start(ap, msg);
106 vlog(LOG_ERR, msg, ap);
107 va_end(ap);
108 exit(1);
109}
110
dp-armdb0f5e92016-11-04 10:56:25 +0000111static char *xstrdup(const char *s, const char *msg)
112{
113 char *d;
114
115 d = strdup(s);
116 if (d == NULL)
dp-arm5ba38542016-12-21 14:59:30 +0000117 log_errx("strdup: %s", msg);
dp-armdb0f5e92016-11-04 10:56:25 +0000118 return d;
119}
120
121static void *xmalloc(size_t size, const char *msg)
122{
123 void *d;
124
125 d = malloc(size);
126 if (d == NULL)
dp-arm5ba38542016-12-21 14:59:30 +0000127 log_errx("malloc: %s", msg);
dp-armdb0f5e92016-11-04 10:56:25 +0000128 return d;
129}
130
Masahiro Yamada03fdf692017-01-15 00:50:41 +0900131static void *xzalloc(size_t size, const char *msg)
132{
133 return memset(xmalloc(size, msg), 0, size);
134}
135
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900136static void xfwrite(void *buf, size_t size, FILE *fp, const char *filename)
137{
138 if (fwrite(buf, 1, size, fp) != size)
139 log_errx("Failed to write %s", filename);
140}
141
dp-arm90d2f0e2016-11-14 15:54:32 +0000142static image_desc_t *new_image_desc(const uuid_t *uuid,
143 const char *name, const char *cmdline_name)
dp-arm4972ec52016-05-25 16:20:20 +0100144{
dp-arm90d2f0e2016-11-14 15:54:32 +0000145 image_desc_t *desc;
146
Masahiro Yamada03fdf692017-01-15 00:50:41 +0900147 desc = xzalloc(sizeof(*desc),
dp-arm90d2f0e2016-11-14 15:54:32 +0000148 "failed to allocate memory for image descriptor");
149 memcpy(&desc->uuid, uuid, sizeof(uuid_t));
150 desc->name = xstrdup(name,
151 "failed to allocate memory for image name");
152 desc->cmdline_name = xstrdup(cmdline_name,
153 "failed to allocate memory for image command line name");
154 desc->action = DO_UNSPEC;
dp-arm90d2f0e2016-11-14 15:54:32 +0000155 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100156}
157
dp-armfb732312016-12-30 09:55:48 +0000158static void set_image_desc_action(image_desc_t *desc, int action,
159 const char *arg)
160{
161 assert(desc != NULL);
162
163 if (desc->action_arg != DO_UNSPEC)
164 free(desc->action_arg);
165 desc->action = action;
166 desc->action_arg = NULL;
167 if (arg != NULL)
168 desc->action_arg = xstrdup(arg,
169 "failed to allocate memory for argument");
170}
171
dp-arm90d2f0e2016-11-14 15:54:32 +0000172static void free_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100173{
dp-arm90d2f0e2016-11-14 15:54:32 +0000174 free(desc->name);
175 free(desc->cmdline_name);
176 free(desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000177 free(desc->image);
dp-arm90d2f0e2016-11-14 15:54:32 +0000178 free(desc);
dp-arm4972ec52016-05-25 16:20:20 +0100179}
180
dp-arm90d2f0e2016-11-14 15:54:32 +0000181static void add_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100182{
Masahiro Yamadad224b452017-01-14 23:22:02 +0900183 image_desc_t **p = &image_desc_head;
184
Masahiro Yamadad224b452017-01-14 23:22:02 +0900185 while (*p)
186 p = &(*p)->next;
187
Masahiro Yamadac2a7d9c2017-01-27 12:53:13 +0900188 assert(*p == NULL);
Masahiro Yamadad224b452017-01-14 23:22:02 +0900189 *p = desc;
dp-arm90d2f0e2016-11-14 15:54:32 +0000190 nr_image_descs++;
191}
dp-arm4972ec52016-05-25 16:20:20 +0100192
dp-arm90d2f0e2016-11-14 15:54:32 +0000193static void free_image_descs(void)
194{
195 image_desc_t *desc = image_desc_head, *tmp;
196
197 while (desc != NULL) {
198 tmp = desc->next;
199 free_image_desc(desc);
200 desc = tmp;
201 nr_image_descs--;
dp-arm4972ec52016-05-25 16:20:20 +0100202 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000203 assert(nr_image_descs == 0);
dp-arm4972ec52016-05-25 16:20:20 +0100204}
205
dp-arm90d2f0e2016-11-14 15:54:32 +0000206static void fill_image_descs(void)
207{
208 toc_entry_t *toc_entry;
209
210 for (toc_entry = toc_entries;
211 toc_entry->cmdline_name != NULL;
212 toc_entry++) {
213 image_desc_t *desc;
214
215 desc = new_image_desc(&toc_entry->uuid,
216 toc_entry->name,
217 toc_entry->cmdline_name);
218 add_image_desc(desc);
219 }
220}
221
dp-arm90d2f0e2016-11-14 15:54:32 +0000222static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid)
dp-arm4972ec52016-05-25 16:20:20 +0100223{
dp-arm90d2f0e2016-11-14 15:54:32 +0000224 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100225
dp-arm90d2f0e2016-11-14 15:54:32 +0000226 for (desc = image_desc_head; desc != NULL; desc = desc->next)
227 if (memcmp(&desc->uuid, uuid, sizeof(uuid_t)) == 0)
228 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100229 return NULL;
230}
231
dp-arm90d2f0e2016-11-14 15:54:32 +0000232static image_desc_t *lookup_image_desc_from_opt(const char *opt)
233{
234 image_desc_t *desc;
235
236 for (desc = image_desc_head; desc != NULL; desc = desc->next)
237 if (strcmp(desc->cmdline_name, opt) == 0)
238 return desc;
239 return NULL;
240}
241
dp-arm516dfcb2016-11-03 13:59:26 +0000242static void uuid_to_str(char *s, size_t len, const uuid_t *u)
243{
244 assert(len >= (_UUID_STR_LEN + 1));
245
246 snprintf(s, len, "%08X-%04X-%04X-%04X-%04X%04X%04X",
247 u->time_low,
248 u->time_mid,
249 u->time_hi_and_version,
250 ((uint16_t)u->clock_seq_hi_and_reserved << 8) | u->clock_seq_low,
251 ((uint16_t)u->node[0] << 8) | u->node[1],
252 ((uint16_t)u->node[2] << 8) | u->node[3],
253 ((uint16_t)u->node[4] << 8) | u->node[5]);
254}
255
256static void uuid_from_str(uuid_t *u, const char *s)
257{
258 int n;
259
260 if (s == NULL)
261 log_errx("UUID cannot be NULL");
262 if (strlen(s) != _UUID_STR_LEN)
263 log_errx("Invalid UUID: %s", s);
264
265 n = sscanf(s,
266 "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
267 &u->time_low, &u->time_mid, &u->time_hi_and_version,
268 &u->clock_seq_hi_and_reserved, &u->clock_seq_low, &u->node[0],
269 &u->node[1], &u->node[2], &u->node[3], &u->node[4], &u->node[5]);
270 /*
271 * Given the format specifier above, we expect 11 items to be scanned
272 * for a properly formatted UUID.
273 */
274 if (n != 11)
275 log_errx("Invalid UUID: %s", s);
276}
277
dp-armc1f8e772016-11-04 10:52:25 +0000278static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
dp-arm4972ec52016-05-25 16:20:20 +0100279{
280 struct stat st;
281 FILE *fp;
282 char *buf, *bufend;
283 fip_toc_header_t *toc_header;
284 fip_toc_entry_t *toc_entry;
dp-arm4972ec52016-05-25 16:20:20 +0100285 int terminated = 0;
286
287 fp = fopen(filename, "r");
288 if (fp == NULL)
289 log_err("fopen %s", filename);
290
291 if (fstat(fileno(fp), &st) == -1)
292 log_err("fstat %s", filename);
293
dp-armdb0f5e92016-11-04 10:56:25 +0000294 buf = xmalloc(st.st_size, "failed to load file into memory");
dp-arm4972ec52016-05-25 16:20:20 +0100295 if (fread(buf, 1, st.st_size, fp) != st.st_size)
296 log_errx("Failed to read %s", filename);
297 bufend = buf + st.st_size;
298 fclose(fp);
299
300 if (st.st_size < sizeof(fip_toc_header_t))
301 log_errx("FIP %s is truncated", filename);
302
303 toc_header = (fip_toc_header_t *)buf;
304 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
305
306 if (toc_header->name != TOC_HEADER_NAME)
307 log_errx("%s is not a FIP file", filename);
308
309 /* Return the ToC header if the caller wants it. */
310 if (toc_header_out != NULL)
311 *toc_header_out = *toc_header;
312
313 /* Walk through each ToC entry in the file. */
314 while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
dp-arm516dfcb2016-11-03 13:59:26 +0000315 image_t *image;
316 image_desc_t *desc;
317
dp-arm4972ec52016-05-25 16:20:20 +0100318 /* Found the ToC terminator, we are done. */
319 if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
320 terminated = 1;
321 break;
322 }
323
324 /*
325 * Build a new image out of the ToC entry and add it to the
326 * table of images.
327 */
Masahiro Yamadad224b452017-01-14 23:22:02 +0900328 image = xzalloc(sizeof(*image),
dp-armdb0f5e92016-11-04 10:56:25 +0000329 "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900330 image->toc_e = *toc_entry;
dp-armdb0f5e92016-11-04 10:56:25 +0000331 image->buffer = xmalloc(toc_entry->size,
332 "failed to allocate image buffer, is FIP file corrupted?");
dp-arm4972ec52016-05-25 16:20:20 +0100333 /* Overflow checks before memory copy. */
334 if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
335 log_errx("FIP %s is corrupted", filename);
336 if (toc_entry->size + toc_entry->offset_address > st.st_size)
337 log_errx("FIP %s is corrupted", filename);
338
339 memcpy(image->buffer, buf + toc_entry->offset_address,
340 toc_entry->size);
dp-arm4972ec52016-05-25 16:20:20 +0100341
dp-arm516dfcb2016-11-03 13:59:26 +0000342 /* If this is an unknown image, create a descriptor for it. */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900343 desc = lookup_image_desc_from_uuid(&toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000344 if (desc == NULL) {
345 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
346
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900347 uuid_to_str(name, sizeof(name), &toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000348 snprintf(filename, sizeof(filename), "%s%s",
349 name, ".bin");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900350 desc = new_image_desc(&toc_entry->uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000351 desc->action = DO_UNPACK;
352 desc->action_arg = xstrdup(filename,
353 "failed to allocate memory for blob filename");
354 add_image_desc(desc);
355 }
356
dp-armafa1efa2017-02-14 15:22:13 +0000357 assert(desc->image == NULL);
358 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100359
360 toc_entry++;
361 }
362
363 if (terminated == 0)
364 log_errx("FIP %s does not have a ToC terminator entry",
365 filename);
366 free(buf);
367 return 0;
368}
369
dp-armc1f8e772016-11-04 10:52:25 +0000370static image_t *read_image_from_file(const uuid_t *uuid, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100371{
372 struct stat st;
373 image_t *image;
374 FILE *fp;
375
dp-arm715ef422016-08-30 14:18:58 +0100376 assert(uuid != NULL);
377
dp-arm4972ec52016-05-25 16:20:20 +0100378 fp = fopen(filename, "r");
379 if (fp == NULL)
380 log_err("fopen %s", filename);
381
382 if (fstat(fileno(fp), &st) == -1)
383 log_errx("fstat %s", filename);
384
Masahiro Yamadad224b452017-01-14 23:22:02 +0900385 image = xzalloc(sizeof(*image), "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900386 image->toc_e.uuid = *uuid;
dp-armdb0f5e92016-11-04 10:56:25 +0000387 image->buffer = xmalloc(st.st_size, "failed to allocate image buffer");
dp-arm4972ec52016-05-25 16:20:20 +0100388 if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
389 log_errx("Failed to read %s", filename);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900390 image->toc_e.size = st.st_size;
dp-arm4972ec52016-05-25 16:20:20 +0100391
392 fclose(fp);
393 return image;
394}
395
dp-armc1f8e772016-11-04 10:52:25 +0000396static int write_image_to_file(const image_t *image, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100397{
398 FILE *fp;
399
400 fp = fopen(filename, "w");
401 if (fp == NULL)
402 log_err("fopen");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900403 xfwrite(image->buffer, image->toc_e.size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100404 fclose(fp);
405 return 0;
406}
407
dp-arm90d2f0e2016-11-14 15:54:32 +0000408static struct option *add_opt(struct option *opts, size_t *nr_opts,
409 const char *name, int has_arg, int val)
dp-arm4972ec52016-05-25 16:20:20 +0100410{
dp-arm90d2f0e2016-11-14 15:54:32 +0000411 opts = realloc(opts, (*nr_opts + 1) * sizeof(*opts));
412 if (opts == NULL)
413 log_err("realloc");
414 opts[*nr_opts].name = name;
415 opts[*nr_opts].has_arg = has_arg;
416 opts[*nr_opts].flag = NULL;
417 opts[*nr_opts].val = val;
418 ++*nr_opts;
419 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100420}
421
dp-arm90d2f0e2016-11-14 15:54:32 +0000422static struct option *fill_common_opts(struct option *opts, size_t *nr_opts,
423 int has_arg)
dp-arm4972ec52016-05-25 16:20:20 +0100424{
dp-arm90d2f0e2016-11-14 15:54:32 +0000425 image_desc_t *desc;
426
427 for (desc = image_desc_head; desc != NULL; desc = desc->next)
428 opts = add_opt(opts, nr_opts, desc->cmdline_name, has_arg,
429 OPT_TOC_ENTRY);
430 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100431}
432
dp-arm90d2f0e2016-11-14 15:54:32 +0000433static void md_print(const unsigned char *md, size_t len)
dp-arm12e893b2016-08-24 13:21:08 +0100434{
435 size_t i;
436
437 for (i = 0; i < len; i++)
438 printf("%02x", md[i]);
439}
440
dp-arm4972ec52016-05-25 16:20:20 +0100441static int info_cmd(int argc, char *argv[])
442{
dp-armafa1efa2017-02-14 15:22:13 +0000443 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100444 fip_toc_header_t toc_header;
dp-arm4972ec52016-05-25 16:20:20 +0100445
446 if (argc != 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100447 info_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100448 argc--, argv++;
449
450 parse_fip(argv[0], &toc_header);
451
452 if (verbose) {
453 log_dbgx("toc_header[name]: 0x%llX",
454 (unsigned long long)toc_header.name);
455 log_dbgx("toc_header[serial_number]: 0x%llX",
456 (unsigned long long)toc_header.serial_number);
457 log_dbgx("toc_header[flags]: 0x%llX",
458 (unsigned long long)toc_header.flags);
459 }
460
dp-armafa1efa2017-02-14 15:22:13 +0000461 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
462 image_t *image = desc->image;
dp-arm715ef422016-08-30 14:18:58 +0100463
dp-armafa1efa2017-02-14 15:22:13 +0000464 if (image == NULL)
465 continue;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900466 printf("%s: offset=0x%llX, size=0x%llX, cmdline=\"--%s\"",
467 desc->name,
468 (unsigned long long)image->toc_e.offset_address,
469 (unsigned long long)image->toc_e.size,
Masahiro Yamadaeee39312017-01-15 23:20:00 +0900470 desc->cmdline_name);
dp-arm12e893b2016-08-24 13:21:08 +0100471 if (verbose) {
472 unsigned char md[SHA256_DIGEST_LENGTH];
473
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900474 SHA256(image->buffer, image->toc_e.size, md);
dp-arm12e893b2016-08-24 13:21:08 +0100475 printf(", sha256=");
476 md_print(md, sizeof(md));
477 }
478 putchar('\n');
dp-arm4972ec52016-05-25 16:20:20 +0100479 }
480
dp-arm4972ec52016-05-25 16:20:20 +0100481 return 0;
482}
483
484static void info_usage(void)
485{
486 printf("fiptool info FIP_FILENAME\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100487 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100488}
489
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900490static int pack_images(const char *filename, uint64_t toc_flags, unsigned long align)
dp-arm4972ec52016-05-25 16:20:20 +0100491{
492 FILE *fp;
dp-armafa1efa2017-02-14 15:22:13 +0000493 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100494 fip_toc_header_t *toc_header;
495 fip_toc_entry_t *toc_entry;
496 char *buf;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900497 uint64_t entry_offset, buf_size, payload_size = 0;
dp-armafa1efa2017-02-14 15:22:13 +0000498 size_t nr_images = 0;
499
500 for (desc = image_desc_head; desc != NULL; desc = desc->next)
501 if (desc->image != NULL)
502 nr_images++;
dp-arm4972ec52016-05-25 16:20:20 +0100503
504 buf_size = sizeof(fip_toc_header_t) +
505 sizeof(fip_toc_entry_t) * (nr_images + 1);
506 buf = calloc(1, buf_size);
507 if (buf == NULL)
508 log_err("calloc");
509
510 /* Build up header and ToC entries from the image table. */
511 toc_header = (fip_toc_header_t *)buf;
512 toc_header->name = TOC_HEADER_NAME;
513 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
514 toc_header->flags = toc_flags;
515
516 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
517
518 entry_offset = buf_size;
dp-armafa1efa2017-02-14 15:22:13 +0000519 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
520 image_t *image = desc->image;
521
522 if (image == NULL)
523 continue;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900524 payload_size += image->toc_e.size;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900525 entry_offset = (entry_offset + align - 1) & ~(align - 1);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900526 image->toc_e.offset_address = entry_offset;
527 *toc_entry++ = image->toc_e;
528 entry_offset += image->toc_e.size;
dp-arm4972ec52016-05-25 16:20:20 +0100529 }
530
531 /* Append a null uuid entry to mark the end of ToC entries. */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900532 memset(toc_entry, 0, sizeof(*toc_entry));
dp-arm4972ec52016-05-25 16:20:20 +0100533 toc_entry->offset_address = entry_offset;
dp-arm4972ec52016-05-25 16:20:20 +0100534
535 /* Generate the FIP file. */
536 fp = fopen(filename, "w");
537 if (fp == NULL)
538 log_err("fopen %s", filename);
539
540 if (verbose)
541 log_dbgx("Metadata size: %zu bytes", buf_size);
542
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900543 xfwrite(buf, buf_size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100544 free(buf);
545
546 if (verbose)
547 log_dbgx("Payload size: %zu bytes", payload_size);
548
dp-armafa1efa2017-02-14 15:22:13 +0000549 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
550 image_t *image = desc->image;
551
552 if (image == NULL)
553 continue;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900554 if (fseek(fp, image->toc_e.offset_address, SEEK_SET))
555 log_errx("Failed to set file position");
556
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900557 xfwrite(image->buffer, image->toc_e.size, fp, filename);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900558 }
dp-arm4972ec52016-05-25 16:20:20 +0100559
560 fclose(fp);
561 return 0;
562}
563
564/*
565 * This function is shared between the create and update subcommands.
566 * The difference between the two subcommands is that when the FIP file
567 * is created, the parsing of an existing FIP is skipped. This results
568 * in update_fip() creating the new FIP file from scratch because the
569 * internal image table is not populated.
570 */
571static void update_fip(void)
572{
dp-arm90d2f0e2016-11-14 15:54:32 +0000573 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100574
575 /* Add or replace images in the FIP file. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000576 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-armafa1efa2017-02-14 15:22:13 +0000577 image_t *image;
dp-arm516dfcb2016-11-03 13:59:26 +0000578
dp-arm90d2f0e2016-11-14 15:54:32 +0000579 if (desc->action != DO_PACK)
dp-arm4972ec52016-05-25 16:20:20 +0100580 continue;
581
dp-armafa1efa2017-02-14 15:22:13 +0000582 image = read_image_from_file(&desc->uuid,
dp-arm90d2f0e2016-11-14 15:54:32 +0000583 desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000584 if (desc->image != NULL) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000585 if (verbose) {
dp-arm516dfcb2016-11-03 13:59:26 +0000586 log_dbgx("Replacing %s with %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000587 desc->cmdline_name,
588 desc->action_arg);
589 }
dp-armafa1efa2017-02-14 15:22:13 +0000590 free(desc->image);
591 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100592 } else {
593 if (verbose)
594 log_dbgx("Adding image %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000595 desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000596 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100597 }
dp-arm4972ec52016-05-25 16:20:20 +0100598 }
599}
600
dp-arm90d2f0e2016-11-14 15:54:32 +0000601static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
dp-arm4972ec52016-05-25 16:20:20 +0100602{
603 unsigned long long flags;
604 char *endptr;
605
606 errno = 0;
607 flags = strtoull(arg, &endptr, 16);
608 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
609 log_errx("Invalid platform ToC flags: %s", arg);
610 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
611 *toc_flags |= flags << 32;
612}
613
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900614static int is_power_of_2(unsigned long x)
615{
616 return x && !(x & (x - 1));
617}
618
619static unsigned long get_image_align(char *arg)
620{
621 char *endptr;
622 unsigned long align;
623
624 errno = 0;
Andreas Färber242a7b72017-04-21 19:39:10 +0200625 align = strtoul(arg, &endptr, 0);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900626 if (*endptr != '\0' || !is_power_of_2(align) || errno != 0)
627 log_errx("Invalid alignment: %s", arg);
628
629 return align;
630}
631
dp-arm516dfcb2016-11-03 13:59:26 +0000632static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
633{
634 char *p;
635
636 for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
637 if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
638 p += strlen("uuid=");
639 uuid_from_str(uuid, p);
640 } else if (strncmp(p, "file=", strlen("file=")) == 0) {
641 p += strlen("file=");
642 snprintf(filename, len, "%s", p);
643 }
644 }
645}
646
dp-arm4972ec52016-05-25 16:20:20 +0100647static int create_cmd(int argc, char *argv[])
648{
dp-arm90d2f0e2016-11-14 15:54:32 +0000649 struct option *opts = NULL;
650 size_t nr_opts = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100651 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900652 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100653
654 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100655 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100656
dp-arm90d2f0e2016-11-14 15:54:32 +0000657 opts = fill_common_opts(opts, &nr_opts, required_argument);
658 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100659 OPT_PLAT_TOC_FLAGS);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900660 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000661 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000662 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100663
664 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000665 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100666
dp-arm516dfcb2016-11-03 13:59:26 +0000667 c = getopt_long(argc, argv, "b:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100668 if (c == -1)
669 break;
670
671 switch (c) {
672 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000673 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100674
dp-arm90d2f0e2016-11-14 15:54:32 +0000675 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000676 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100677 break;
678 }
679 case OPT_PLAT_TOC_FLAGS:
680 parse_plat_toc_flags(optarg, &toc_flags);
681 break;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900682 case OPT_ALIGN:
683 align = get_image_align(optarg);
684 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000685 case 'b': {
686 char name[_UUID_STR_LEN + 1];
687 char filename[PATH_MAX] = { 0 };
688 uuid_t uuid = { 0 };
689 image_desc_t *desc;
690
691 parse_blob_opt(optarg, &uuid,
692 filename, sizeof(filename));
693
694 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
695 filename[0] == '\0')
696 create_usage();
697
698 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000699 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000700 uuid_to_str(name, sizeof(name), &uuid);
701 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000702 add_image_desc(desc);
703 }
dp-armfb732312016-12-30 09:55:48 +0000704 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000705 break;
706 }
dp-arm4972ec52016-05-25 16:20:20 +0100707 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100708 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100709 }
710 }
711 argc -= optind;
712 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000713 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100714
715 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100716 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100717
718 update_fip();
719
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900720 pack_images(argv[0], toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100721 return 0;
722}
723
724static void create_usage(void)
725{
726 toc_entry_t *toc_entry = toc_entries;
727
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900728 printf("fiptool create [opts] FIP_FILENAME\n");
729 printf("\n");
730 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900731 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900732 printf(" --blob uuid=...,file=...\tAdd an image with the given UUID pointed to by file.\n");
733 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 +0900734 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100735 printf("Specific images are packed with the following options:\n");
736 for (; toc_entry->cmdline_name != NULL; toc_entry++)
737 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
738 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100739 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100740}
741
742static int update_cmd(int argc, char *argv[])
743{
dp-arm90d2f0e2016-11-14 15:54:32 +0000744 struct option *opts = NULL;
745 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000746 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100747 fip_toc_header_t toc_header = { 0 };
748 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900749 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100750 int pflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100751
752 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100753 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100754
dp-arm90d2f0e2016-11-14 15:54:32 +0000755 opts = fill_common_opts(opts, &nr_opts, required_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900756 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000757 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000758 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
759 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100760 OPT_PLAT_TOC_FLAGS);
dp-arm90d2f0e2016-11-14 15:54:32 +0000761 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100762
763 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000764 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100765
dp-arm516dfcb2016-11-03 13:59:26 +0000766 c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100767 if (c == -1)
768 break;
769
770 switch (c) {
771 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000772 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100773
dp-arm90d2f0e2016-11-14 15:54:32 +0000774 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000775 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100776 break;
777 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000778 case OPT_PLAT_TOC_FLAGS:
dp-arm4972ec52016-05-25 16:20:20 +0100779 parse_plat_toc_flags(optarg, &toc_flags);
780 pflag = 1;
781 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000782 case 'b': {
783 char name[_UUID_STR_LEN + 1];
784 char filename[PATH_MAX] = { 0 };
785 uuid_t uuid = { 0 };
786 image_desc_t *desc;
787
788 parse_blob_opt(optarg, &uuid,
789 filename, sizeof(filename));
790
791 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
792 filename[0] == '\0')
793 update_usage();
794
795 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000796 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000797 uuid_to_str(name, sizeof(name), &uuid);
798 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000799 add_image_desc(desc);
800 }
dp-armfb732312016-12-30 09:55:48 +0000801 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000802 break;
803 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900804 case OPT_ALIGN:
805 align = get_image_align(optarg);
806 break;
dp-arm4972ec52016-05-25 16:20:20 +0100807 case 'o':
808 snprintf(outfile, sizeof(outfile), "%s", optarg);
809 break;
810 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100811 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100812 }
813 }
814 argc -= optind;
815 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000816 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100817
818 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100819 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100820
821 if (outfile[0] == '\0')
822 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
823
Masahiro Yamadaebb6e372016-12-25 12:41:41 +0900824 if (access(argv[0], F_OK) == 0)
dp-arm4972ec52016-05-25 16:20:20 +0100825 parse_fip(argv[0], &toc_header);
826
827 if (pflag)
828 toc_header.flags &= ~(0xffffULL << 32);
829 toc_flags = (toc_header.flags |= toc_flags);
830
831 update_fip();
832
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900833 pack_images(outfile, toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100834 return 0;
835}
836
837static void update_usage(void)
838{
839 toc_entry_t *toc_entry = toc_entries;
840
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900841 printf("fiptool update [opts] FIP_FILENAME\n");
842 printf("\n");
843 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900844 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900845 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 +0100846 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900847 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 +0900848 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100849 printf("Specific images are packed with the following options:\n");
850 for (; toc_entry->cmdline_name != NULL; toc_entry++)
851 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
852 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100853 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100854}
855
856static int unpack_cmd(int argc, char *argv[])
857{
dp-arm90d2f0e2016-11-14 15:54:32 +0000858 struct option *opts = NULL;
859 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000860 char outdir[PATH_MAX] = { 0 };
dp-arm90d2f0e2016-11-14 15:54:32 +0000861 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100862 int fflag = 0;
863 int unpack_all = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100864
865 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100866 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100867
dp-arm90d2f0e2016-11-14 15:54:32 +0000868 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000869 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000870 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
871 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
872 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100873
874 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000875 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100876
dp-arm516dfcb2016-11-03 13:59:26 +0000877 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100878 if (c == -1)
879 break;
880
881 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000882 case OPT_TOC_ENTRY: {
883 image_desc_t *desc;
884
885 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000886 set_image_desc_action(desc, DO_UNPACK, optarg);
dp-arm90d2f0e2016-11-14 15:54:32 +0000887 unpack_all = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100888 break;
dp-arm90d2f0e2016-11-14 15:54:32 +0000889 }
dp-arm516dfcb2016-11-03 13:59:26 +0000890 case 'b': {
891 char name[_UUID_STR_LEN + 1];
892 char filename[PATH_MAX] = { 0 };
893 uuid_t uuid = { 0 };
894 image_desc_t *desc;
895
896 parse_blob_opt(optarg, &uuid,
897 filename, sizeof(filename));
898
899 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
900 filename[0] == '\0')
901 unpack_usage();
902
903 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000904 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000905 uuid_to_str(name, sizeof(name), &uuid);
906 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000907 add_image_desc(desc);
908 }
dp-armfb732312016-12-30 09:55:48 +0000909 set_image_desc_action(desc, DO_UNPACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000910 unpack_all = 0;
911 break;
912 }
dp-arm4972ec52016-05-25 16:20:20 +0100913 case 'f':
914 fflag = 1;
915 break;
916 case 'o':
917 snprintf(outdir, sizeof(outdir), "%s", optarg);
918 break;
919 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100920 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100921 }
922 }
923 argc -= optind;
924 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000925 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100926
927 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100928 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100929
930 parse_fip(argv[0], NULL);
931
932 if (outdir[0] != '\0')
933 if (chdir(outdir) == -1)
934 log_err("chdir %s", outdir);
935
dp-arm4972ec52016-05-25 16:20:20 +0100936 /* Unpack all specified images. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000937 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +0000938 char file[PATH_MAX];
dp-armafa1efa2017-02-14 15:22:13 +0000939 image_t *image = desc->image;
dp-arm715ef422016-08-30 14:18:58 +0100940
dp-arm90d2f0e2016-11-14 15:54:32 +0000941 if (!unpack_all && desc->action != DO_UNPACK)
dp-arm4972ec52016-05-25 16:20:20 +0100942 continue;
943
944 /* Build filename. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000945 if (desc->action_arg == NULL)
dp-arm4972ec52016-05-25 16:20:20 +0100946 snprintf(file, sizeof(file), "%s.bin",
dp-arm90d2f0e2016-11-14 15:54:32 +0000947 desc->cmdline_name);
dp-arm4972ec52016-05-25 16:20:20 +0100948 else
949 snprintf(file, sizeof(file), "%s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000950 desc->action_arg);
dp-arm4972ec52016-05-25 16:20:20 +0100951
dp-arm715ef422016-08-30 14:18:58 +0100952 if (image == NULL) {
953 if (!unpack_all)
dp-arm516dfcb2016-11-03 13:59:26 +0000954 log_warnx("%s does not exist in %s",
dp-arm715ef422016-08-30 14:18:58 +0100955 file, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +0100956 continue;
957 }
958
959 if (access(file, F_OK) != 0 || fflag) {
960 if (verbose)
961 log_dbgx("Unpacking %s", file);
dp-arm715ef422016-08-30 14:18:58 +0100962 write_image_to_file(image, file);
dp-arm4972ec52016-05-25 16:20:20 +0100963 } else {
964 log_warnx("File %s already exists, use --force to overwrite it",
965 file);
966 }
dp-arm4972ec52016-05-25 16:20:20 +0100967 }
968
dp-arm4972ec52016-05-25 16:20:20 +0100969 return 0;
970}
971
972static void unpack_usage(void)
973{
974 toc_entry_t *toc_entry = toc_entries;
975
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900976 printf("fiptool unpack [opts] FIP_FILENAME\n");
977 printf("\n");
978 printf("Options:\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900979 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID to file.\n");
980 printf(" --force\t\t\tIf the output file already exists, use --force to overwrite it.\n");
dp-arm516dfcb2016-11-03 13:59:26 +0000981 printf(" --out path\t\t\tSet the output directory path.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +0900982 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100983 printf("Specific images are unpacked with the following options:\n");
984 for (; toc_entry->cmdline_name != NULL; toc_entry++)
985 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
986 toc_entry->name);
Masahiro Yamada1b900152017-02-02 16:34:14 +0900987 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100988 printf("If no options are provided, all images will be unpacked.\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100989 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100990}
991
992static int remove_cmd(int argc, char *argv[])
993{
dp-arm90d2f0e2016-11-14 15:54:32 +0000994 struct option *opts = NULL;
995 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000996 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100997 fip_toc_header_t toc_header;
dp-arm90d2f0e2016-11-14 15:54:32 +0000998 image_desc_t *desc;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900999 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +01001000 int fflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001001
1002 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +01001003 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001004
dp-arm90d2f0e2016-11-14 15:54:32 +00001005 opts = fill_common_opts(opts, &nr_opts, no_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001006 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +00001007 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +00001008 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
1009 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
1010 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +01001011
1012 while (1) {
dp-armfe92b892016-11-07 11:13:54 +00001013 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001014
dp-arm516dfcb2016-11-03 13:59:26 +00001015 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +01001016 if (c == -1)
1017 break;
1018
1019 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001020 case OPT_TOC_ENTRY: {
1021 image_desc_t *desc;
1022
1023 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +00001024 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm4972ec52016-05-25 16:20:20 +01001025 break;
dp-arm90d2f0e2016-11-14 15:54:32 +00001026 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001027 case OPT_ALIGN:
1028 align = get_image_align(optarg);
1029 break;
dp-arm516dfcb2016-11-03 13:59:26 +00001030 case 'b': {
1031 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
1032 uuid_t uuid = { 0 };
1033 image_desc_t *desc;
1034
1035 parse_blob_opt(optarg, &uuid,
1036 filename, sizeof(filename));
1037
1038 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
1039 remove_usage();
1040
1041 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +00001042 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +00001043 uuid_to_str(name, sizeof(name), &uuid);
1044 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +00001045 add_image_desc(desc);
1046 }
dp-armfb732312016-12-30 09:55:48 +00001047 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm516dfcb2016-11-03 13:59:26 +00001048 break;
1049 }
dp-arm4972ec52016-05-25 16:20:20 +01001050 case 'f':
1051 fflag = 1;
1052 break;
1053 case 'o':
1054 snprintf(outfile, sizeof(outfile), "%s", optarg);
1055 break;
1056 default:
dp-arm29f1b5c2016-09-15 09:58:50 +01001057 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001058 }
1059 }
1060 argc -= optind;
1061 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001062 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001063
1064 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001065 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001066
1067 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
1068 log_errx("File %s already exists, use --force to overwrite it",
1069 outfile);
1070
1071 if (outfile[0] == '\0')
1072 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
1073
1074 parse_fip(argv[0], &toc_header);
1075
dp-arm90d2f0e2016-11-14 15:54:32 +00001076 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001077 if (desc->action != DO_REMOVE)
dp-arm4972ec52016-05-25 16:20:20 +01001078 continue;
dp-arm516dfcb2016-11-03 13:59:26 +00001079
dp-armafa1efa2017-02-14 15:22:13 +00001080 if (desc->image != NULL) {
dp-arm4972ec52016-05-25 16:20:20 +01001081 if (verbose)
dp-arm516dfcb2016-11-03 13:59:26 +00001082 log_dbgx("Removing %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001083 desc->cmdline_name);
dp-armafa1efa2017-02-14 15:22:13 +00001084 free(desc->image);
1085 desc->image = NULL;
dp-arm4972ec52016-05-25 16:20:20 +01001086 } else {
dp-arm516dfcb2016-11-03 13:59:26 +00001087 log_warnx("%s does not exist in %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001088 desc->cmdline_name, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001089 }
1090 }
1091
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001092 pack_images(outfile, toc_header.flags, align);
dp-arm4972ec52016-05-25 16:20:20 +01001093 return 0;
1094}
1095
1096static void remove_usage(void)
1097{
1098 toc_entry_t *toc_entry = toc_entries;
1099
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001100 printf("fiptool remove [opts] FIP_FILENAME\n");
1101 printf("\n");
1102 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001103 printf(" --align <value>\tEach image is aligned to <value> (default: 1).\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001104 printf(" --blob uuid=...\tRemove an image with the given UUID.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +09001105 printf(" --force\t\tIf the output FIP file already exists, use --force to overwrite it.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001106 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001107 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001108 printf("Specific images are removed with the following options:\n");
1109 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1110 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
1111 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +01001112 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001113}
1114
1115static int version_cmd(int argc, char *argv[])
1116{
1117#ifdef VERSION
1118 puts(VERSION);
1119#else
1120 /* If built from fiptool directory, VERSION is not set. */
1121 puts("Unknown version");
1122#endif
1123 return 0;
1124}
1125
1126static void version_usage(void)
1127{
1128 printf("fiptool version\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001129 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001130}
1131
1132static int help_cmd(int argc, char *argv[])
1133{
1134 int i;
1135
1136 if (argc < 2)
1137 usage();
1138 argc--, argv++;
1139
1140 for (i = 0; i < NELEM(cmds); i++) {
1141 if (strcmp(cmds[i].name, argv[0]) == 0 &&
dp-arm29f1b5c2016-09-15 09:58:50 +01001142 cmds[i].usage != NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001143 cmds[i].usage();
dp-arm4972ec52016-05-25 16:20:20 +01001144 }
1145 if (i == NELEM(cmds))
1146 printf("No help for subcommand '%s'\n", argv[0]);
1147 return 0;
1148}
1149
1150static void usage(void)
1151{
Masahiro Yamada252c3362017-01-13 02:13:06 +09001152 printf("usage: fiptool [--verbose] <command> [<args>]\n");
dp-arm4972ec52016-05-25 16:20:20 +01001153 printf("Global options supported:\n");
1154 printf(" --verbose\tEnable verbose output for all commands.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001155 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001156 printf("Commands supported:\n");
1157 printf(" info\t\tList images contained in FIP.\n");
1158 printf(" create\tCreate a new FIP with the given images.\n");
1159 printf(" update\tUpdate an existing FIP with the given images.\n");
1160 printf(" unpack\tUnpack images from FIP.\n");
1161 printf(" remove\tRemove images from FIP.\n");
1162 printf(" version\tShow fiptool version.\n");
1163 printf(" help\t\tShow help for given command.\n");
1164 exit(1);
1165}
1166
1167int main(int argc, char *argv[])
1168{
1169 int i, ret = 0;
1170
dp-arm5cd10ae2016-11-07 10:45:59 +00001171 while (1) {
1172 int c, opt_index = 0;
1173 static struct option opts[] = {
1174 { "verbose", no_argument, NULL, 'v' },
1175 { NULL, no_argument, NULL, 0 }
1176 };
1177
1178 /*
1179 * Set POSIX mode so getopt stops at the first non-option
1180 * which is the subcommand.
1181 */
1182 c = getopt_long(argc, argv, "+v", opts, &opt_index);
1183 if (c == -1)
1184 break;
dp-arm4972ec52016-05-25 16:20:20 +01001185
dp-arm5cd10ae2016-11-07 10:45:59 +00001186 switch (c) {
1187 case 'v':
1188 verbose = 1;
1189 break;
1190 default:
Masahiro Yamada48a24972016-10-26 13:24:26 +09001191 usage();
dp-arm5cd10ae2016-11-07 10:45:59 +00001192 }
dp-arm4972ec52016-05-25 16:20:20 +01001193 }
dp-arm5cd10ae2016-11-07 10:45:59 +00001194 argc -= optind;
1195 argv += optind;
1196 /* Reset optind for subsequent getopt processing. */
1197 optind = 0;
1198
1199 if (argc == 0)
1200 usage();
dp-arm4972ec52016-05-25 16:20:20 +01001201
dp-arm90d2f0e2016-11-14 15:54:32 +00001202 fill_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001203 for (i = 0; i < NELEM(cmds); i++) {
1204 if (strcmp(cmds[i].name, argv[0]) == 0) {
1205 ret = cmds[i].handler(argc, argv);
1206 break;
1207 }
1208 }
1209 if (i == NELEM(cmds))
1210 usage();
dp-arm90d2f0e2016-11-14 15:54:32 +00001211 free_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001212 return ret;
1213}