blob: e4348ee857b7a597f7c704eb8ca9b2f7d04c529a [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;
dp-arm4972ec52016-05-25 16:20:20 +010054static uuid_t uuid_null = { 0 };
55static 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
244 snprintf(s, len, "%08X-%04X-%04X-%04X-%04X%04X%04X",
245 u->time_low,
246 u->time_mid,
247 u->time_hi_and_version,
248 ((uint16_t)u->clock_seq_hi_and_reserved << 8) | u->clock_seq_low,
249 ((uint16_t)u->node[0] << 8) | u->node[1],
250 ((uint16_t)u->node[2] << 8) | u->node[3],
251 ((uint16_t)u->node[4] << 8) | u->node[5]);
252}
253
254static void uuid_from_str(uuid_t *u, const char *s)
255{
256 int n;
257
258 if (s == NULL)
259 log_errx("UUID cannot be NULL");
260 if (strlen(s) != _UUID_STR_LEN)
261 log_errx("Invalid UUID: %s", s);
262
263 n = sscanf(s,
264 "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
265 &u->time_low, &u->time_mid, &u->time_hi_and_version,
266 &u->clock_seq_hi_and_reserved, &u->clock_seq_low, &u->node[0],
267 &u->node[1], &u->node[2], &u->node[3], &u->node[4], &u->node[5]);
268 /*
269 * Given the format specifier above, we expect 11 items to be scanned
270 * for a properly formatted UUID.
271 */
272 if (n != 11)
273 log_errx("Invalid UUID: %s", s);
274}
275
dp-armc1f8e772016-11-04 10:52:25 +0000276static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
dp-arm4972ec52016-05-25 16:20:20 +0100277{
Evan Lloyd04dc3442017-05-25 19:06:47 +0100278 struct BLD_PLAT_STAT st;
dp-arm4972ec52016-05-25 16:20:20 +0100279 FILE *fp;
280 char *buf, *bufend;
281 fip_toc_header_t *toc_header;
282 fip_toc_entry_t *toc_entry;
dp-arm4972ec52016-05-25 16:20:20 +0100283 int terminated = 0;
284
Evan Lloydb1939392017-01-13 14:13:09 +0000285 fp = fopen(filename, "rb");
dp-arm4972ec52016-05-25 16:20:20 +0100286 if (fp == NULL)
287 log_err("fopen %s", filename);
288
289 if (fstat(fileno(fp), &st) == -1)
290 log_err("fstat %s", filename);
291
dp-armdb0f5e92016-11-04 10:56:25 +0000292 buf = xmalloc(st.st_size, "failed to load file into memory");
dp-arm4972ec52016-05-25 16:20:20 +0100293 if (fread(buf, 1, st.st_size, fp) != st.st_size)
294 log_errx("Failed to read %s", filename);
295 bufend = buf + st.st_size;
296 fclose(fp);
297
298 if (st.st_size < sizeof(fip_toc_header_t))
299 log_errx("FIP %s is truncated", filename);
300
301 toc_header = (fip_toc_header_t *)buf;
302 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
303
304 if (toc_header->name != TOC_HEADER_NAME)
305 log_errx("%s is not a FIP file", filename);
306
307 /* Return the ToC header if the caller wants it. */
308 if (toc_header_out != NULL)
309 *toc_header_out = *toc_header;
310
311 /* Walk through each ToC entry in the file. */
312 while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
dp-arm516dfcb2016-11-03 13:59:26 +0000313 image_t *image;
314 image_desc_t *desc;
315
dp-arm4972ec52016-05-25 16:20:20 +0100316 /* Found the ToC terminator, we are done. */
317 if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
318 terminated = 1;
319 break;
320 }
321
322 /*
323 * Build a new image out of the ToC entry and add it to the
324 * table of images.
325 */
Masahiro Yamadad224b452017-01-14 23:22:02 +0900326 image = xzalloc(sizeof(*image),
dp-armdb0f5e92016-11-04 10:56:25 +0000327 "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900328 image->toc_e = *toc_entry;
dp-armdb0f5e92016-11-04 10:56:25 +0000329 image->buffer = xmalloc(toc_entry->size,
330 "failed to allocate image buffer, is FIP file corrupted?");
dp-arm4972ec52016-05-25 16:20:20 +0100331 /* Overflow checks before memory copy. */
332 if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
333 log_errx("FIP %s is corrupted", filename);
334 if (toc_entry->size + toc_entry->offset_address > st.st_size)
335 log_errx("FIP %s is corrupted", filename);
336
337 memcpy(image->buffer, buf + toc_entry->offset_address,
338 toc_entry->size);
dp-arm4972ec52016-05-25 16:20:20 +0100339
dp-arm516dfcb2016-11-03 13:59:26 +0000340 /* If this is an unknown image, create a descriptor for it. */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900341 desc = lookup_image_desc_from_uuid(&toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000342 if (desc == NULL) {
343 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
344
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900345 uuid_to_str(name, sizeof(name), &toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000346 snprintf(filename, sizeof(filename), "%s%s",
347 name, ".bin");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900348 desc = new_image_desc(&toc_entry->uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000349 desc->action = DO_UNPACK;
350 desc->action_arg = xstrdup(filename,
351 "failed to allocate memory for blob filename");
352 add_image_desc(desc);
353 }
354
dp-armafa1efa2017-02-14 15:22:13 +0000355 assert(desc->image == NULL);
356 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100357
358 toc_entry++;
359 }
360
361 if (terminated == 0)
362 log_errx("FIP %s does not have a ToC terminator entry",
363 filename);
364 free(buf);
365 return 0;
366}
367
dp-armc1f8e772016-11-04 10:52:25 +0000368static image_t *read_image_from_file(const uuid_t *uuid, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100369{
Evan Lloyd04dc3442017-05-25 19:06:47 +0100370 struct BLD_PLAT_STAT st;
dp-arm4972ec52016-05-25 16:20:20 +0100371 image_t *image;
372 FILE *fp;
373
dp-arm715ef422016-08-30 14:18:58 +0100374 assert(uuid != NULL);
Evan Lloyd04dc3442017-05-25 19:06:47 +0100375 assert(filename != NULL);
dp-arm715ef422016-08-30 14:18:58 +0100376
Evan Lloydb1939392017-01-13 14:13:09 +0000377 fp = fopen(filename, "rb");
dp-arm4972ec52016-05-25 16:20:20 +0100378 if (fp == NULL)
379 log_err("fopen %s", filename);
380
381 if (fstat(fileno(fp), &st) == -1)
382 log_errx("fstat %s", filename);
383
Masahiro Yamadad224b452017-01-14 23:22:02 +0900384 image = xzalloc(sizeof(*image), "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900385 image->toc_e.uuid = *uuid;
dp-armdb0f5e92016-11-04 10:56:25 +0000386 image->buffer = xmalloc(st.st_size, "failed to allocate image buffer");
dp-arm4972ec52016-05-25 16:20:20 +0100387 if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
388 log_errx("Failed to read %s", filename);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900389 image->toc_e.size = st.st_size;
dp-arm4972ec52016-05-25 16:20:20 +0100390
391 fclose(fp);
392 return image;
393}
394
dp-armc1f8e772016-11-04 10:52:25 +0000395static int write_image_to_file(const image_t *image, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100396{
397 FILE *fp;
398
Evan Lloydb1939392017-01-13 14:13:09 +0000399 fp = fopen(filename, "wb");
dp-arm4972ec52016-05-25 16:20:20 +0100400 if (fp == NULL)
401 log_err("fopen");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900402 xfwrite(image->buffer, image->toc_e.size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100403 fclose(fp);
404 return 0;
405}
406
dp-arm90d2f0e2016-11-14 15:54:32 +0000407static struct option *add_opt(struct option *opts, size_t *nr_opts,
408 const char *name, int has_arg, int val)
dp-arm4972ec52016-05-25 16:20:20 +0100409{
dp-arm90d2f0e2016-11-14 15:54:32 +0000410 opts = realloc(opts, (*nr_opts + 1) * sizeof(*opts));
411 if (opts == NULL)
412 log_err("realloc");
413 opts[*nr_opts].name = name;
414 opts[*nr_opts].has_arg = has_arg;
415 opts[*nr_opts].flag = NULL;
416 opts[*nr_opts].val = val;
417 ++*nr_opts;
418 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100419}
420
dp-arm90d2f0e2016-11-14 15:54:32 +0000421static struct option *fill_common_opts(struct option *opts, size_t *nr_opts,
422 int has_arg)
dp-arm4972ec52016-05-25 16:20:20 +0100423{
dp-arm90d2f0e2016-11-14 15:54:32 +0000424 image_desc_t *desc;
425
426 for (desc = image_desc_head; desc != NULL; desc = desc->next)
427 opts = add_opt(opts, nr_opts, desc->cmdline_name, has_arg,
428 OPT_TOC_ENTRY);
429 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100430}
431
dp-arm90d2f0e2016-11-14 15:54:32 +0000432static void md_print(const unsigned char *md, size_t len)
dp-arm12e893b2016-08-24 13:21:08 +0100433{
434 size_t i;
435
436 for (i = 0; i < len; i++)
437 printf("%02x", md[i]);
438}
439
dp-arm4972ec52016-05-25 16:20:20 +0100440static int info_cmd(int argc, char *argv[])
441{
dp-armafa1efa2017-02-14 15:22:13 +0000442 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100443 fip_toc_header_t toc_header;
dp-arm4972ec52016-05-25 16:20:20 +0100444
445 if (argc != 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100446 info_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100447 argc--, argv++;
448
449 parse_fip(argv[0], &toc_header);
450
451 if (verbose) {
452 log_dbgx("toc_header[name]: 0x%llX",
453 (unsigned long long)toc_header.name);
454 log_dbgx("toc_header[serial_number]: 0x%llX",
455 (unsigned long long)toc_header.serial_number);
456 log_dbgx("toc_header[flags]: 0x%llX",
457 (unsigned long long)toc_header.flags);
458 }
459
dp-armafa1efa2017-02-14 15:22:13 +0000460 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
461 image_t *image = desc->image;
dp-arm715ef422016-08-30 14:18:58 +0100462
dp-armafa1efa2017-02-14 15:22:13 +0000463 if (image == NULL)
464 continue;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900465 printf("%s: offset=0x%llX, size=0x%llX, cmdline=\"--%s\"",
466 desc->name,
467 (unsigned long long)image->toc_e.offset_address,
468 (unsigned long long)image->toc_e.size,
Masahiro Yamadaeee39312017-01-15 23:20:00 +0900469 desc->cmdline_name);
Evan Lloyd04dc3442017-05-25 19:06:47 +0100470#ifndef _MSC_VER /* We don't have SHA256 for Visual Studio. */
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 }
Evan Lloyd04dc3442017-05-25 19:06:47 +0100478#endif
dp-arm12e893b2016-08-24 13:21:08 +0100479 putchar('\n');
dp-arm4972ec52016-05-25 16:20:20 +0100480 }
481
dp-arm4972ec52016-05-25 16:20:20 +0100482 return 0;
483}
484
485static void info_usage(void)
486{
487 printf("fiptool info FIP_FILENAME\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100488 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100489}
490
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900491static int pack_images(const char *filename, uint64_t toc_flags, unsigned long align)
dp-arm4972ec52016-05-25 16:20:20 +0100492{
493 FILE *fp;
dp-armafa1efa2017-02-14 15:22:13 +0000494 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100495 fip_toc_header_t *toc_header;
496 fip_toc_entry_t *toc_entry;
497 char *buf;
Roberto Vargas81935762017-12-19 11:56:57 +0000498 uint64_t entry_offset, buf_size, payload_size = 0, pad_size;
dp-armafa1efa2017-02-14 15:22:13 +0000499 size_t nr_images = 0;
500
501 for (desc = image_desc_head; desc != NULL; desc = desc->next)
502 if (desc->image != NULL)
503 nr_images++;
dp-arm4972ec52016-05-25 16:20:20 +0100504
505 buf_size = sizeof(fip_toc_header_t) +
506 sizeof(fip_toc_entry_t) * (nr_images + 1);
507 buf = calloc(1, buf_size);
508 if (buf == NULL)
509 log_err("calloc");
510
511 /* Build up header and ToC entries from the image table. */
512 toc_header = (fip_toc_header_t *)buf;
513 toc_header->name = TOC_HEADER_NAME;
514 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
515 toc_header->flags = toc_flags;
516
517 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
518
519 entry_offset = buf_size;
dp-armafa1efa2017-02-14 15:22:13 +0000520 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
521 image_t *image = desc->image;
522
523 if (image == NULL)
524 continue;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900525 payload_size += image->toc_e.size;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900526 entry_offset = (entry_offset + align - 1) & ~(align - 1);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900527 image->toc_e.offset_address = entry_offset;
528 *toc_entry++ = image->toc_e;
529 entry_offset += image->toc_e.size;
dp-arm4972ec52016-05-25 16:20:20 +0100530 }
531
Roberto Vargas81935762017-12-19 11:56:57 +0000532 /*
533 * Append a null uuid entry to mark the end of ToC entries.
534 * NOTE the offset address for the last toc_entry must match the fip
535 * size.
536 */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900537 memset(toc_entry, 0, sizeof(*toc_entry));
Roberto Vargas81935762017-12-19 11:56:57 +0000538 toc_entry->offset_address = (entry_offset + align - 1) & ~(align - 1);
dp-arm4972ec52016-05-25 16:20:20 +0100539
540 /* Generate the FIP file. */
Evan Lloydb1939392017-01-13 14:13:09 +0000541 fp = fopen(filename, "wb");
dp-arm4972ec52016-05-25 16:20:20 +0100542 if (fp == NULL)
543 log_err("fopen %s", filename);
544
545 if (verbose)
546 log_dbgx("Metadata size: %zu bytes", buf_size);
547
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900548 xfwrite(buf, buf_size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100549
550 if (verbose)
551 log_dbgx("Payload size: %zu bytes", payload_size);
552
dp-armafa1efa2017-02-14 15:22:13 +0000553 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
554 image_t *image = desc->image;
555
556 if (image == NULL)
557 continue;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900558 if (fseek(fp, image->toc_e.offset_address, SEEK_SET))
559 log_errx("Failed to set file position");
560
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900561 xfwrite(image->buffer, image->toc_e.size, fp, filename);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900562 }
dp-arm4972ec52016-05-25 16:20:20 +0100563
Roberto Vargas81935762017-12-19 11:56:57 +0000564 if (fseek(fp, entry_offset, SEEK_SET))
565 log_errx("Failed to set file position");
566
567 pad_size = toc_entry->offset_address - entry_offset;
568 while (pad_size--)
569 fputc(0x0, fp);
570
Andreas Färber860b5dc2018-01-27 16:46:59 +0100571 free(buf);
dp-arm4972ec52016-05-25 16:20:20 +0100572 fclose(fp);
573 return 0;
574}
575
576/*
577 * This function is shared between the create and update subcommands.
578 * The difference between the two subcommands is that when the FIP file
579 * is created, the parsing of an existing FIP is skipped. This results
580 * in update_fip() creating the new FIP file from scratch because the
581 * internal image table is not populated.
582 */
583static void update_fip(void)
584{
dp-arm90d2f0e2016-11-14 15:54:32 +0000585 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100586
587 /* Add or replace images in the FIP file. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000588 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-armafa1efa2017-02-14 15:22:13 +0000589 image_t *image;
dp-arm516dfcb2016-11-03 13:59:26 +0000590
dp-arm90d2f0e2016-11-14 15:54:32 +0000591 if (desc->action != DO_PACK)
dp-arm4972ec52016-05-25 16:20:20 +0100592 continue;
593
dp-armafa1efa2017-02-14 15:22:13 +0000594 image = read_image_from_file(&desc->uuid,
dp-arm90d2f0e2016-11-14 15:54:32 +0000595 desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000596 if (desc->image != NULL) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000597 if (verbose) {
dp-arm516dfcb2016-11-03 13:59:26 +0000598 log_dbgx("Replacing %s with %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000599 desc->cmdline_name,
600 desc->action_arg);
601 }
dp-armafa1efa2017-02-14 15:22:13 +0000602 free(desc->image);
603 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100604 } else {
605 if (verbose)
606 log_dbgx("Adding image %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000607 desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000608 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100609 }
dp-arm4972ec52016-05-25 16:20:20 +0100610 }
611}
612
dp-arm90d2f0e2016-11-14 15:54:32 +0000613static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
dp-arm4972ec52016-05-25 16:20:20 +0100614{
615 unsigned long long flags;
616 char *endptr;
617
618 errno = 0;
619 flags = strtoull(arg, &endptr, 16);
620 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
621 log_errx("Invalid platform ToC flags: %s", arg);
622 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
623 *toc_flags |= flags << 32;
624}
625
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900626static int is_power_of_2(unsigned long x)
627{
628 return x && !(x & (x - 1));
629}
630
631static unsigned long get_image_align(char *arg)
632{
633 char *endptr;
634 unsigned long align;
635
636 errno = 0;
Andreas Färber242a7b72017-04-21 19:39:10 +0200637 align = strtoul(arg, &endptr, 0);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900638 if (*endptr != '\0' || !is_power_of_2(align) || errno != 0)
639 log_errx("Invalid alignment: %s", arg);
640
641 return align;
642}
643
dp-arm516dfcb2016-11-03 13:59:26 +0000644static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
645{
646 char *p;
647
648 for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
649 if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
650 p += strlen("uuid=");
651 uuid_from_str(uuid, p);
652 } else if (strncmp(p, "file=", strlen("file=")) == 0) {
653 p += strlen("file=");
654 snprintf(filename, len, "%s", p);
655 }
656 }
657}
658
dp-arm4972ec52016-05-25 16:20:20 +0100659static int create_cmd(int argc, char *argv[])
660{
dp-arm90d2f0e2016-11-14 15:54:32 +0000661 struct option *opts = NULL;
662 size_t nr_opts = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100663 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900664 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100665
666 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100667 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100668
dp-arm90d2f0e2016-11-14 15:54:32 +0000669 opts = fill_common_opts(opts, &nr_opts, required_argument);
670 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100671 OPT_PLAT_TOC_FLAGS);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900672 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000673 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000674 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100675
676 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000677 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100678
dp-arm516dfcb2016-11-03 13:59:26 +0000679 c = getopt_long(argc, argv, "b:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100680 if (c == -1)
681 break;
682
683 switch (c) {
684 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000685 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100686
dp-arm90d2f0e2016-11-14 15:54:32 +0000687 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000688 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100689 break;
690 }
691 case OPT_PLAT_TOC_FLAGS:
692 parse_plat_toc_flags(optarg, &toc_flags);
693 break;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900694 case OPT_ALIGN:
695 align = get_image_align(optarg);
696 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000697 case 'b': {
698 char name[_UUID_STR_LEN + 1];
699 char filename[PATH_MAX] = { 0 };
700 uuid_t uuid = { 0 };
701 image_desc_t *desc;
702
703 parse_blob_opt(optarg, &uuid,
704 filename, sizeof(filename));
705
706 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
707 filename[0] == '\0')
708 create_usage();
709
710 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000711 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000712 uuid_to_str(name, sizeof(name), &uuid);
713 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000714 add_image_desc(desc);
715 }
dp-armfb732312016-12-30 09:55:48 +0000716 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000717 break;
718 }
dp-arm4972ec52016-05-25 16:20:20 +0100719 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100720 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100721 }
722 }
723 argc -= optind;
724 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000725 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100726
727 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100728 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100729
730 update_fip();
731
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900732 pack_images(argv[0], toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100733 return 0;
734}
735
736static void create_usage(void)
737{
738 toc_entry_t *toc_entry = toc_entries;
739
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900740 printf("fiptool create [opts] FIP_FILENAME\n");
741 printf("\n");
742 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900743 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900744 printf(" --blob uuid=...,file=...\tAdd an image with the given UUID pointed to by file.\n");
745 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 +0900746 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100747 printf("Specific images are packed with the following options:\n");
748 for (; toc_entry->cmdline_name != NULL; toc_entry++)
749 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
750 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100751 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100752}
753
754static int update_cmd(int argc, char *argv[])
755{
dp-arm90d2f0e2016-11-14 15:54:32 +0000756 struct option *opts = NULL;
757 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000758 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100759 fip_toc_header_t toc_header = { 0 };
760 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900761 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100762 int pflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100763
764 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100765 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100766
dp-arm90d2f0e2016-11-14 15:54:32 +0000767 opts = fill_common_opts(opts, &nr_opts, required_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900768 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000769 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000770 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
771 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100772 OPT_PLAT_TOC_FLAGS);
dp-arm90d2f0e2016-11-14 15:54:32 +0000773 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100774
775 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000776 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100777
dp-arm516dfcb2016-11-03 13:59:26 +0000778 c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100779 if (c == -1)
780 break;
781
782 switch (c) {
783 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000784 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100785
dp-arm90d2f0e2016-11-14 15:54:32 +0000786 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000787 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100788 break;
789 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000790 case OPT_PLAT_TOC_FLAGS:
dp-arm4972ec52016-05-25 16:20:20 +0100791 parse_plat_toc_flags(optarg, &toc_flags);
792 pflag = 1;
793 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000794 case 'b': {
795 char name[_UUID_STR_LEN + 1];
796 char filename[PATH_MAX] = { 0 };
797 uuid_t uuid = { 0 };
798 image_desc_t *desc;
799
800 parse_blob_opt(optarg, &uuid,
801 filename, sizeof(filename));
802
803 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
804 filename[0] == '\0')
805 update_usage();
806
807 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000808 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000809 uuid_to_str(name, sizeof(name), &uuid);
810 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000811 add_image_desc(desc);
812 }
dp-armfb732312016-12-30 09:55:48 +0000813 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000814 break;
815 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900816 case OPT_ALIGN:
817 align = get_image_align(optarg);
818 break;
dp-arm4972ec52016-05-25 16:20:20 +0100819 case 'o':
820 snprintf(outfile, sizeof(outfile), "%s", optarg);
821 break;
822 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100823 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100824 }
825 }
826 argc -= optind;
827 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000828 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100829
830 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100831 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100832
833 if (outfile[0] == '\0')
834 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
835
Masahiro Yamadaebb6e372016-12-25 12:41:41 +0900836 if (access(argv[0], F_OK) == 0)
dp-arm4972ec52016-05-25 16:20:20 +0100837 parse_fip(argv[0], &toc_header);
838
839 if (pflag)
840 toc_header.flags &= ~(0xffffULL << 32);
841 toc_flags = (toc_header.flags |= toc_flags);
842
843 update_fip();
844
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900845 pack_images(outfile, toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100846 return 0;
847}
848
849static void update_usage(void)
850{
851 toc_entry_t *toc_entry = toc_entries;
852
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900853 printf("fiptool update [opts] FIP_FILENAME\n");
854 printf("\n");
855 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900856 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900857 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 +0100858 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900859 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 +0900860 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100861 printf("Specific images are packed with the following options:\n");
862 for (; toc_entry->cmdline_name != NULL; toc_entry++)
863 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
864 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100865 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100866}
867
868static int unpack_cmd(int argc, char *argv[])
869{
dp-arm90d2f0e2016-11-14 15:54:32 +0000870 struct option *opts = NULL;
871 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000872 char outdir[PATH_MAX] = { 0 };
dp-arm90d2f0e2016-11-14 15:54:32 +0000873 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100874 int fflag = 0;
875 int unpack_all = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100876
877 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100878 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100879
dp-arm90d2f0e2016-11-14 15:54:32 +0000880 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000881 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000882 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
883 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
884 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100885
886 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000887 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100888
dp-arm516dfcb2016-11-03 13:59:26 +0000889 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100890 if (c == -1)
891 break;
892
893 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000894 case OPT_TOC_ENTRY: {
895 image_desc_t *desc;
896
897 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000898 set_image_desc_action(desc, DO_UNPACK, optarg);
dp-arm90d2f0e2016-11-14 15:54:32 +0000899 unpack_all = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100900 break;
dp-arm90d2f0e2016-11-14 15:54:32 +0000901 }
dp-arm516dfcb2016-11-03 13:59:26 +0000902 case 'b': {
903 char name[_UUID_STR_LEN + 1];
904 char filename[PATH_MAX] = { 0 };
905 uuid_t uuid = { 0 };
906 image_desc_t *desc;
907
908 parse_blob_opt(optarg, &uuid,
909 filename, sizeof(filename));
910
911 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
912 filename[0] == '\0')
913 unpack_usage();
914
915 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000916 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000917 uuid_to_str(name, sizeof(name), &uuid);
918 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000919 add_image_desc(desc);
920 }
dp-armfb732312016-12-30 09:55:48 +0000921 set_image_desc_action(desc, DO_UNPACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000922 unpack_all = 0;
923 break;
924 }
dp-arm4972ec52016-05-25 16:20:20 +0100925 case 'f':
926 fflag = 1;
927 break;
928 case 'o':
929 snprintf(outdir, sizeof(outdir), "%s", optarg);
930 break;
931 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100932 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100933 }
934 }
935 argc -= optind;
936 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000937 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100938
939 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100940 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100941
942 parse_fip(argv[0], NULL);
943
944 if (outdir[0] != '\0')
945 if (chdir(outdir) == -1)
946 log_err("chdir %s", outdir);
947
dp-arm4972ec52016-05-25 16:20:20 +0100948 /* Unpack all specified images. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000949 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +0000950 char file[PATH_MAX];
dp-armafa1efa2017-02-14 15:22:13 +0000951 image_t *image = desc->image;
dp-arm715ef422016-08-30 14:18:58 +0100952
dp-arm90d2f0e2016-11-14 15:54:32 +0000953 if (!unpack_all && desc->action != DO_UNPACK)
dp-arm4972ec52016-05-25 16:20:20 +0100954 continue;
955
956 /* Build filename. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000957 if (desc->action_arg == NULL)
dp-arm4972ec52016-05-25 16:20:20 +0100958 snprintf(file, sizeof(file), "%s.bin",
dp-arm90d2f0e2016-11-14 15:54:32 +0000959 desc->cmdline_name);
dp-arm4972ec52016-05-25 16:20:20 +0100960 else
961 snprintf(file, sizeof(file), "%s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000962 desc->action_arg);
dp-arm4972ec52016-05-25 16:20:20 +0100963
dp-arm715ef422016-08-30 14:18:58 +0100964 if (image == NULL) {
965 if (!unpack_all)
dp-arm516dfcb2016-11-03 13:59:26 +0000966 log_warnx("%s does not exist in %s",
dp-arm715ef422016-08-30 14:18:58 +0100967 file, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +0100968 continue;
969 }
970
971 if (access(file, F_OK) != 0 || fflag) {
972 if (verbose)
973 log_dbgx("Unpacking %s", file);
dp-arm715ef422016-08-30 14:18:58 +0100974 write_image_to_file(image, file);
dp-arm4972ec52016-05-25 16:20:20 +0100975 } else {
976 log_warnx("File %s already exists, use --force to overwrite it",
977 file);
978 }
dp-arm4972ec52016-05-25 16:20:20 +0100979 }
980
dp-arm4972ec52016-05-25 16:20:20 +0100981 return 0;
982}
983
984static void unpack_usage(void)
985{
986 toc_entry_t *toc_entry = toc_entries;
987
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900988 printf("fiptool unpack [opts] FIP_FILENAME\n");
989 printf("\n");
990 printf("Options:\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900991 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID to file.\n");
992 printf(" --force\t\t\tIf the output file already exists, use --force to overwrite it.\n");
dp-arm516dfcb2016-11-03 13:59:26 +0000993 printf(" --out path\t\t\tSet the output directory path.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +0900994 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100995 printf("Specific images are unpacked with the following options:\n");
996 for (; toc_entry->cmdline_name != NULL; toc_entry++)
997 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
998 toc_entry->name);
Masahiro Yamada1b900152017-02-02 16:34:14 +0900999 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001000 printf("If no options are provided, all images will be unpacked.\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001001 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001002}
1003
1004static int remove_cmd(int argc, char *argv[])
1005{
dp-arm90d2f0e2016-11-14 15:54:32 +00001006 struct option *opts = NULL;
1007 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +00001008 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +01001009 fip_toc_header_t toc_header;
dp-arm90d2f0e2016-11-14 15:54:32 +00001010 image_desc_t *desc;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001011 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +01001012 int fflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001013
1014 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +01001015 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001016
dp-arm90d2f0e2016-11-14 15:54:32 +00001017 opts = fill_common_opts(opts, &nr_opts, no_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001018 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +00001019 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +00001020 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
1021 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
1022 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +01001023
1024 while (1) {
dp-armfe92b892016-11-07 11:13:54 +00001025 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001026
dp-arm516dfcb2016-11-03 13:59:26 +00001027 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +01001028 if (c == -1)
1029 break;
1030
1031 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001032 case OPT_TOC_ENTRY: {
1033 image_desc_t *desc;
1034
1035 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +00001036 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm4972ec52016-05-25 16:20:20 +01001037 break;
dp-arm90d2f0e2016-11-14 15:54:32 +00001038 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001039 case OPT_ALIGN:
1040 align = get_image_align(optarg);
1041 break;
dp-arm516dfcb2016-11-03 13:59:26 +00001042 case 'b': {
1043 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
1044 uuid_t uuid = { 0 };
1045 image_desc_t *desc;
1046
1047 parse_blob_opt(optarg, &uuid,
1048 filename, sizeof(filename));
1049
1050 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
1051 remove_usage();
1052
1053 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +00001054 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +00001055 uuid_to_str(name, sizeof(name), &uuid);
1056 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +00001057 add_image_desc(desc);
1058 }
dp-armfb732312016-12-30 09:55:48 +00001059 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm516dfcb2016-11-03 13:59:26 +00001060 break;
1061 }
dp-arm4972ec52016-05-25 16:20:20 +01001062 case 'f':
1063 fflag = 1;
1064 break;
1065 case 'o':
1066 snprintf(outfile, sizeof(outfile), "%s", optarg);
1067 break;
1068 default:
dp-arm29f1b5c2016-09-15 09:58:50 +01001069 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001070 }
1071 }
1072 argc -= optind;
1073 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001074 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001075
1076 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001077 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001078
1079 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
1080 log_errx("File %s already exists, use --force to overwrite it",
1081 outfile);
1082
1083 if (outfile[0] == '\0')
1084 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
1085
1086 parse_fip(argv[0], &toc_header);
1087
dp-arm90d2f0e2016-11-14 15:54:32 +00001088 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001089 if (desc->action != DO_REMOVE)
dp-arm4972ec52016-05-25 16:20:20 +01001090 continue;
dp-arm516dfcb2016-11-03 13:59:26 +00001091
dp-armafa1efa2017-02-14 15:22:13 +00001092 if (desc->image != NULL) {
dp-arm4972ec52016-05-25 16:20:20 +01001093 if (verbose)
dp-arm516dfcb2016-11-03 13:59:26 +00001094 log_dbgx("Removing %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001095 desc->cmdline_name);
dp-armafa1efa2017-02-14 15:22:13 +00001096 free(desc->image);
1097 desc->image = NULL;
dp-arm4972ec52016-05-25 16:20:20 +01001098 } else {
dp-arm516dfcb2016-11-03 13:59:26 +00001099 log_warnx("%s does not exist in %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001100 desc->cmdline_name, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001101 }
1102 }
1103
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001104 pack_images(outfile, toc_header.flags, align);
dp-arm4972ec52016-05-25 16:20:20 +01001105 return 0;
1106}
1107
1108static void remove_usage(void)
1109{
1110 toc_entry_t *toc_entry = toc_entries;
1111
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001112 printf("fiptool remove [opts] FIP_FILENAME\n");
1113 printf("\n");
1114 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001115 printf(" --align <value>\tEach image is aligned to <value> (default: 1).\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001116 printf(" --blob uuid=...\tRemove an image with the given UUID.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +09001117 printf(" --force\t\tIf the output FIP file already exists, use --force to overwrite it.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001118 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001119 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001120 printf("Specific images are removed with the following options:\n");
1121 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1122 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
1123 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +01001124 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001125}
1126
1127static int version_cmd(int argc, char *argv[])
1128{
1129#ifdef VERSION
1130 puts(VERSION);
1131#else
1132 /* If built from fiptool directory, VERSION is not set. */
1133 puts("Unknown version");
1134#endif
1135 return 0;
1136}
1137
1138static void version_usage(void)
1139{
1140 printf("fiptool version\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001141 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001142}
1143
1144static int help_cmd(int argc, char *argv[])
1145{
1146 int i;
1147
1148 if (argc < 2)
1149 usage();
1150 argc--, argv++;
1151
1152 for (i = 0; i < NELEM(cmds); i++) {
1153 if (strcmp(cmds[i].name, argv[0]) == 0 &&
dp-arm29f1b5c2016-09-15 09:58:50 +01001154 cmds[i].usage != NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001155 cmds[i].usage();
dp-arm4972ec52016-05-25 16:20:20 +01001156 }
1157 if (i == NELEM(cmds))
1158 printf("No help for subcommand '%s'\n", argv[0]);
1159 return 0;
1160}
1161
1162static void usage(void)
1163{
Masahiro Yamada252c3362017-01-13 02:13:06 +09001164 printf("usage: fiptool [--verbose] <command> [<args>]\n");
dp-arm4972ec52016-05-25 16:20:20 +01001165 printf("Global options supported:\n");
1166 printf(" --verbose\tEnable verbose output for all commands.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001167 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001168 printf("Commands supported:\n");
1169 printf(" info\t\tList images contained in FIP.\n");
1170 printf(" create\tCreate a new FIP with the given images.\n");
1171 printf(" update\tUpdate an existing FIP with the given images.\n");
1172 printf(" unpack\tUnpack images from FIP.\n");
1173 printf(" remove\tRemove images from FIP.\n");
1174 printf(" version\tShow fiptool version.\n");
1175 printf(" help\t\tShow help for given command.\n");
1176 exit(1);
1177}
1178
1179int main(int argc, char *argv[])
1180{
1181 int i, ret = 0;
1182
dp-arm5cd10ae2016-11-07 10:45:59 +00001183 while (1) {
1184 int c, opt_index = 0;
1185 static struct option opts[] = {
1186 { "verbose", no_argument, NULL, 'v' },
1187 { NULL, no_argument, NULL, 0 }
1188 };
1189
1190 /*
1191 * Set POSIX mode so getopt stops at the first non-option
1192 * which is the subcommand.
1193 */
1194 c = getopt_long(argc, argv, "+v", opts, &opt_index);
1195 if (c == -1)
1196 break;
dp-arm4972ec52016-05-25 16:20:20 +01001197
dp-arm5cd10ae2016-11-07 10:45:59 +00001198 switch (c) {
1199 case 'v':
1200 verbose = 1;
1201 break;
1202 default:
Masahiro Yamada48a24972016-10-26 13:24:26 +09001203 usage();
dp-arm5cd10ae2016-11-07 10:45:59 +00001204 }
dp-arm4972ec52016-05-25 16:20:20 +01001205 }
dp-arm5cd10ae2016-11-07 10:45:59 +00001206 argc -= optind;
1207 argv += optind;
1208 /* Reset optind for subsequent getopt processing. */
1209 optind = 0;
1210
1211 if (argc == 0)
1212 usage();
dp-arm4972ec52016-05-25 16:20:20 +01001213
dp-arm90d2f0e2016-11-14 15:54:32 +00001214 fill_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001215 for (i = 0; i < NELEM(cmds); i++) {
1216 if (strcmp(cmds[i].name, argv[0]) == 0) {
1217 ret = cmds[i].handler(argc, argv);
1218 break;
1219 }
1220 }
1221 if (i == NELEM(cmds))
1222 usage();
dp-arm90d2f0e2016-11-14 15:54:32 +00001223 free_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001224 return ret;
1225}