blob: b02635f67158b90fa450477888a0af2c8771824f [file] [log] [blame]
dp-arm4972ec52016-05-25 16:20:20 +01001/*
2 * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * Neither the name of ARM nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without specific
16 * prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <sys/types.h>
32#include <sys/stat.h>
33
34#include <assert.h>
35#include <errno.h>
36#include <getopt.h>
37#include <limits.h>
38#include <stdarg.h>
39#include <stdint.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44
dp-arm12e893b2016-08-24 13:21:08 +010045#include <openssl/sha.h>
46
dp-arm4972ec52016-05-25 16:20:20 +010047#include "fiptool.h"
48#include "firmware_image_package.h"
49#include "tbbr_config.h"
50
51#define OPT_TOC_ENTRY 0
52#define OPT_PLAT_TOC_FLAGS 1
53
dp-arm90d2f0e2016-11-14 15:54:32 +000054static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid);
55static image_t *lookup_image_from_uuid(const uuid_t *uuid);
dp-arm4972ec52016-05-25 16:20:20 +010056static int info_cmd(int argc, char *argv[]);
57static void info_usage(void);
58static int create_cmd(int argc, char *argv[]);
59static void create_usage(void);
60static int update_cmd(int argc, char *argv[]);
61static void update_usage(void);
62static int unpack_cmd(int argc, char *argv[]);
63static void unpack_usage(void);
64static int remove_cmd(int argc, char *argv[]);
65static void remove_usage(void);
66static int version_cmd(int argc, char *argv[]);
67static void version_usage(void);
68static int help_cmd(int argc, char *argv[]);
69static void usage(void);
70
71/* Available subcommands. */
72static cmd_t cmds[] = {
73 { .name = "info", .handler = info_cmd, .usage = info_usage },
74 { .name = "create", .handler = create_cmd, .usage = create_usage },
75 { .name = "update", .handler = update_cmd, .usage = update_usage },
76 { .name = "unpack", .handler = unpack_cmd, .usage = unpack_usage },
77 { .name = "remove", .handler = remove_cmd, .usage = remove_usage },
78 { .name = "version", .handler = version_cmd, .usage = version_usage },
79 { .name = "help", .handler = help_cmd, .usage = NULL },
80};
81
dp-arm90d2f0e2016-11-14 15:54:32 +000082static image_desc_t *image_desc_head;
83static size_t nr_image_descs;
84static image_t *image_head;
dp-arm4972ec52016-05-25 16:20:20 +010085static size_t nr_images;
86static uuid_t uuid_null = { 0 };
87static int verbose;
88
dp-armc1f8e772016-11-04 10:52:25 +000089static void vlog(int prio, const char *msg, va_list ap)
dp-arm4972ec52016-05-25 16:20:20 +010090{
91 char *prefix[] = { "DEBUG", "WARN", "ERROR" };
92
93 fprintf(stderr, "%s: ", prefix[prio]);
94 vfprintf(stderr, msg, ap);
95 fputc('\n', stderr);
96}
97
dp-armc1f8e772016-11-04 10:52:25 +000098static void log_dbgx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010099{
100 va_list ap;
101
102 va_start(ap, msg);
103 vlog(LOG_DBG, msg, ap);
104 va_end(ap);
105}
106
dp-armc1f8e772016-11-04 10:52:25 +0000107static void log_warnx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +0100108{
109 va_list ap;
110
111 va_start(ap, msg);
112 vlog(LOG_WARN, msg, ap);
113 va_end(ap);
114}
115
dp-armc1f8e772016-11-04 10:52:25 +0000116static void log_err(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +0100117{
118 char buf[512];
119 va_list ap;
120
121 va_start(ap, msg);
122 snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
123 vlog(LOG_ERR, buf, ap);
124 va_end(ap);
125 exit(1);
126}
127
dp-armc1f8e772016-11-04 10:52:25 +0000128static void log_errx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +0100129{
130 va_list ap;
131
132 va_start(ap, msg);
133 vlog(LOG_ERR, msg, ap);
134 va_end(ap);
135 exit(1);
136}
137
dp-armdb0f5e92016-11-04 10:56:25 +0000138static char *xstrdup(const char *s, const char *msg)
139{
140 char *d;
141
142 d = strdup(s);
143 if (d == NULL)
dp-arm5ba38542016-12-21 14:59:30 +0000144 log_errx("strdup: %s", msg);
dp-armdb0f5e92016-11-04 10:56:25 +0000145 return d;
146}
147
148static void *xmalloc(size_t size, const char *msg)
149{
150 void *d;
151
152 d = malloc(size);
153 if (d == NULL)
dp-arm5ba38542016-12-21 14:59:30 +0000154 log_errx("malloc: %s", msg);
dp-armdb0f5e92016-11-04 10:56:25 +0000155 return d;
156}
157
Masahiro Yamada03fdf692017-01-15 00:50:41 +0900158static void *xzalloc(size_t size, const char *msg)
159{
160 return memset(xmalloc(size, msg), 0, size);
161}
162
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900163static void xfwrite(void *buf, size_t size, FILE *fp, const char *filename)
164{
165 if (fwrite(buf, 1, size, fp) != size)
166 log_errx("Failed to write %s", filename);
167}
168
dp-arm90d2f0e2016-11-14 15:54:32 +0000169static image_desc_t *new_image_desc(const uuid_t *uuid,
170 const char *name, const char *cmdline_name)
dp-arm4972ec52016-05-25 16:20:20 +0100171{
dp-arm90d2f0e2016-11-14 15:54:32 +0000172 image_desc_t *desc;
173
Masahiro Yamada03fdf692017-01-15 00:50:41 +0900174 desc = xzalloc(sizeof(*desc),
dp-arm90d2f0e2016-11-14 15:54:32 +0000175 "failed to allocate memory for image descriptor");
176 memcpy(&desc->uuid, uuid, sizeof(uuid_t));
177 desc->name = xstrdup(name,
178 "failed to allocate memory for image name");
179 desc->cmdline_name = xstrdup(cmdline_name,
180 "failed to allocate memory for image command line name");
181 desc->action = DO_UNSPEC;
dp-arm90d2f0e2016-11-14 15:54:32 +0000182 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100183}
184
dp-armfb732312016-12-30 09:55:48 +0000185static void set_image_desc_action(image_desc_t *desc, int action,
186 const char *arg)
187{
188 assert(desc != NULL);
189
190 if (desc->action_arg != DO_UNSPEC)
191 free(desc->action_arg);
192 desc->action = action;
193 desc->action_arg = NULL;
194 if (arg != NULL)
195 desc->action_arg = xstrdup(arg,
196 "failed to allocate memory for argument");
197}
198
dp-arm90d2f0e2016-11-14 15:54:32 +0000199static void free_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100200{
dp-arm90d2f0e2016-11-14 15:54:32 +0000201 free(desc->name);
202 free(desc->cmdline_name);
203 free(desc->action_arg);
204 free(desc);
dp-arm4972ec52016-05-25 16:20:20 +0100205}
206
dp-arm90d2f0e2016-11-14 15:54:32 +0000207static void add_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100208{
Masahiro Yamadad224b452017-01-14 23:22:02 +0900209 image_desc_t **p = &image_desc_head;
210
Masahiro Yamadad224b452017-01-14 23:22:02 +0900211 while (*p)
212 p = &(*p)->next;
213
Masahiro Yamadac2a7d9c2017-01-27 12:53:13 +0900214 assert(*p == NULL);
Masahiro Yamadad224b452017-01-14 23:22:02 +0900215 *p = desc;
dp-arm90d2f0e2016-11-14 15:54:32 +0000216 nr_image_descs++;
217}
dp-arm4972ec52016-05-25 16:20:20 +0100218
dp-arm90d2f0e2016-11-14 15:54:32 +0000219static void free_image_descs(void)
220{
221 image_desc_t *desc = image_desc_head, *tmp;
222
223 while (desc != NULL) {
224 tmp = desc->next;
225 free_image_desc(desc);
226 desc = tmp;
227 nr_image_descs--;
dp-arm4972ec52016-05-25 16:20:20 +0100228 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000229 assert(nr_image_descs == 0);
dp-arm4972ec52016-05-25 16:20:20 +0100230}
231
dp-arm90d2f0e2016-11-14 15:54:32 +0000232static void fill_image_descs(void)
233{
234 toc_entry_t *toc_entry;
235
236 for (toc_entry = toc_entries;
237 toc_entry->cmdline_name != NULL;
238 toc_entry++) {
239 image_desc_t *desc;
240
241 desc = new_image_desc(&toc_entry->uuid,
242 toc_entry->name,
243 toc_entry->cmdline_name);
244 add_image_desc(desc);
245 }
246}
247
248static void add_image(image_t *image)
249{
Masahiro Yamadad224b452017-01-14 23:22:02 +0900250 image_t **p = &image_head;
251
Masahiro Yamadad224b452017-01-14 23:22:02 +0900252 while (*p)
253 p = &(*p)->next;
254
Masahiro Yamadac2a7d9c2017-01-27 12:53:13 +0900255 assert(*p == NULL);
Masahiro Yamadad224b452017-01-14 23:22:02 +0900256 *p = image;
257
dp-arm90d2f0e2016-11-14 15:54:32 +0000258 nr_images++;
259}
260
Masahiro Yamadad65e7fd2017-01-27 11:57:54 +0900261static void replace_image(image_t *image)
262{
263 image_t **p = &image_head;
264
265 while (*p) {
266 if (!memcmp(&(*p)->uuid, &image->uuid, sizeof(image->uuid)))
267 break;
268 p = &(*p)->next;
269 }
270
271 assert(*p != NULL);
272
273 image->next = (*p)->next;
274 *p = image;
275}
276
dp-arm90d2f0e2016-11-14 15:54:32 +0000277static void free_image(image_t *image)
278{
279 free(image->buffer);
280 free(image);
281}
282
dp-arm4972ec52016-05-25 16:20:20 +0100283static void remove_image(image_t *image)
284{
Masahiro Yamadac0d2d982017-01-27 13:31:40 +0900285 image_t *tmp, **p = &image_head;
dp-arm4972ec52016-05-25 16:20:20 +0100286
Masahiro Yamadac0d2d982017-01-27 13:31:40 +0900287 while (*p) {
288 if (*p == image)
289 break;
290 p = &(*p)->next;
dp-arm4972ec52016-05-25 16:20:20 +0100291 }
Masahiro Yamadac0d2d982017-01-27 13:31:40 +0900292
293 assert(*p != NULL);
294
295 tmp = *p;
296 *p = tmp->next;
297 free_image(tmp);
298
dp-arm4972ec52016-05-25 16:20:20 +0100299 nr_images--;
300}
301
302static void free_images(void)
303{
dp-arm90d2f0e2016-11-14 15:54:32 +0000304 image_t *image = image_head, *tmp;
dp-arm4972ec52016-05-25 16:20:20 +0100305
dp-arm90d2f0e2016-11-14 15:54:32 +0000306 while (image != NULL) {
307 tmp = image->next;
308 free_image(image);
309 image = tmp;
310 nr_images--;
dp-arm4972ec52016-05-25 16:20:20 +0100311 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000312 assert(nr_images == 0);
dp-arm4972ec52016-05-25 16:20:20 +0100313}
314
dp-arm90d2f0e2016-11-14 15:54:32 +0000315static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid)
dp-arm4972ec52016-05-25 16:20:20 +0100316{
dp-arm90d2f0e2016-11-14 15:54:32 +0000317 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100318
dp-arm90d2f0e2016-11-14 15:54:32 +0000319 for (desc = image_desc_head; desc != NULL; desc = desc->next)
320 if (memcmp(&desc->uuid, uuid, sizeof(uuid_t)) == 0)
321 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100322 return NULL;
323}
324
dp-arm90d2f0e2016-11-14 15:54:32 +0000325static image_desc_t *lookup_image_desc_from_opt(const char *opt)
326{
327 image_desc_t *desc;
328
329 for (desc = image_desc_head; desc != NULL; desc = desc->next)
330 if (strcmp(desc->cmdline_name, opt) == 0)
331 return desc;
332 return NULL;
333}
334
335static image_t *lookup_image_from_uuid(const uuid_t *uuid)
dp-arm715ef422016-08-30 14:18:58 +0100336{
337 image_t *image;
dp-arm715ef422016-08-30 14:18:58 +0100338
dp-arm90d2f0e2016-11-14 15:54:32 +0000339 for (image = image_head; image != NULL; image = image->next)
dp-arm715ef422016-08-30 14:18:58 +0100340 if (memcmp(&image->uuid, uuid, sizeof(uuid_t)) == 0)
341 return image;
dp-arm715ef422016-08-30 14:18:58 +0100342 return NULL;
343}
344
dp-arm516dfcb2016-11-03 13:59:26 +0000345static void uuid_to_str(char *s, size_t len, const uuid_t *u)
346{
347 assert(len >= (_UUID_STR_LEN + 1));
348
349 snprintf(s, len, "%08X-%04X-%04X-%04X-%04X%04X%04X",
350 u->time_low,
351 u->time_mid,
352 u->time_hi_and_version,
353 ((uint16_t)u->clock_seq_hi_and_reserved << 8) | u->clock_seq_low,
354 ((uint16_t)u->node[0] << 8) | u->node[1],
355 ((uint16_t)u->node[2] << 8) | u->node[3],
356 ((uint16_t)u->node[4] << 8) | u->node[5]);
357}
358
359static void uuid_from_str(uuid_t *u, const char *s)
360{
361 int n;
362
363 if (s == NULL)
364 log_errx("UUID cannot be NULL");
365 if (strlen(s) != _UUID_STR_LEN)
366 log_errx("Invalid UUID: %s", s);
367
368 n = sscanf(s,
369 "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
370 &u->time_low, &u->time_mid, &u->time_hi_and_version,
371 &u->clock_seq_hi_and_reserved, &u->clock_seq_low, &u->node[0],
372 &u->node[1], &u->node[2], &u->node[3], &u->node[4], &u->node[5]);
373 /*
374 * Given the format specifier above, we expect 11 items to be scanned
375 * for a properly formatted UUID.
376 */
377 if (n != 11)
378 log_errx("Invalid UUID: %s", s);
379}
380
dp-armc1f8e772016-11-04 10:52:25 +0000381static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
dp-arm4972ec52016-05-25 16:20:20 +0100382{
383 struct stat st;
384 FILE *fp;
385 char *buf, *bufend;
386 fip_toc_header_t *toc_header;
387 fip_toc_entry_t *toc_entry;
dp-arm4972ec52016-05-25 16:20:20 +0100388 int terminated = 0;
389
390 fp = fopen(filename, "r");
391 if (fp == NULL)
392 log_err("fopen %s", filename);
393
394 if (fstat(fileno(fp), &st) == -1)
395 log_err("fstat %s", filename);
396
dp-armdb0f5e92016-11-04 10:56:25 +0000397 buf = xmalloc(st.st_size, "failed to load file into memory");
dp-arm4972ec52016-05-25 16:20:20 +0100398 if (fread(buf, 1, st.st_size, fp) != st.st_size)
399 log_errx("Failed to read %s", filename);
400 bufend = buf + st.st_size;
401 fclose(fp);
402
403 if (st.st_size < sizeof(fip_toc_header_t))
404 log_errx("FIP %s is truncated", filename);
405
406 toc_header = (fip_toc_header_t *)buf;
407 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
408
409 if (toc_header->name != TOC_HEADER_NAME)
410 log_errx("%s is not a FIP file", filename);
411
412 /* Return the ToC header if the caller wants it. */
413 if (toc_header_out != NULL)
414 *toc_header_out = *toc_header;
415
416 /* Walk through each ToC entry in the file. */
417 while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
dp-arm516dfcb2016-11-03 13:59:26 +0000418 image_t *image;
419 image_desc_t *desc;
420
dp-arm4972ec52016-05-25 16:20:20 +0100421 /* Found the ToC terminator, we are done. */
422 if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
423 terminated = 1;
424 break;
425 }
426
427 /*
428 * Build a new image out of the ToC entry and add it to the
429 * table of images.
430 */
Masahiro Yamadad224b452017-01-14 23:22:02 +0900431 image = xzalloc(sizeof(*image),
dp-armdb0f5e92016-11-04 10:56:25 +0000432 "failed to allocate memory for image");
dp-arm4972ec52016-05-25 16:20:20 +0100433 memcpy(&image->uuid, &toc_entry->uuid, sizeof(uuid_t));
dp-armdb0f5e92016-11-04 10:56:25 +0000434 image->buffer = xmalloc(toc_entry->size,
435 "failed to allocate image buffer, is FIP file corrupted?");
dp-arm4972ec52016-05-25 16:20:20 +0100436 /* Overflow checks before memory copy. */
437 if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
438 log_errx("FIP %s is corrupted", filename);
439 if (toc_entry->size + toc_entry->offset_address > st.st_size)
440 log_errx("FIP %s is corrupted", filename);
441
442 memcpy(image->buffer, buf + toc_entry->offset_address,
443 toc_entry->size);
444 image->size = toc_entry->size;
445
dp-arm516dfcb2016-11-03 13:59:26 +0000446 /* If this is an unknown image, create a descriptor for it. */
447 desc = lookup_image_desc_from_uuid(&image->uuid);
448 if (desc == NULL) {
449 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
450
451 uuid_to_str(name, sizeof(name), &image->uuid);
452 snprintf(filename, sizeof(filename), "%s%s",
453 name, ".bin");
454 desc = new_image_desc(&image->uuid, name, "blob");
455 desc->action = DO_UNPACK;
456 desc->action_arg = xstrdup(filename,
457 "failed to allocate memory for blob filename");
458 add_image_desc(desc);
459 }
460
dp-arm4972ec52016-05-25 16:20:20 +0100461 add_image(image);
462
463 toc_entry++;
464 }
465
466 if (terminated == 0)
467 log_errx("FIP %s does not have a ToC terminator entry",
468 filename);
469 free(buf);
470 return 0;
471}
472
dp-armc1f8e772016-11-04 10:52:25 +0000473static image_t *read_image_from_file(const uuid_t *uuid, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100474{
475 struct stat st;
476 image_t *image;
477 FILE *fp;
478
dp-arm715ef422016-08-30 14:18:58 +0100479 assert(uuid != NULL);
480
dp-arm4972ec52016-05-25 16:20:20 +0100481 fp = fopen(filename, "r");
482 if (fp == NULL)
483 log_err("fopen %s", filename);
484
485 if (fstat(fileno(fp), &st) == -1)
486 log_errx("fstat %s", filename);
487
Masahiro Yamadad224b452017-01-14 23:22:02 +0900488 image = xzalloc(sizeof(*image), "failed to allocate memory for image");
dp-arm715ef422016-08-30 14:18:58 +0100489 memcpy(&image->uuid, uuid, sizeof(uuid_t));
dp-armdb0f5e92016-11-04 10:56:25 +0000490 image->buffer = xmalloc(st.st_size, "failed to allocate image buffer");
dp-arm4972ec52016-05-25 16:20:20 +0100491 if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
492 log_errx("Failed to read %s", filename);
493 image->size = st.st_size;
dp-arm4972ec52016-05-25 16:20:20 +0100494
495 fclose(fp);
496 return image;
497}
498
dp-armc1f8e772016-11-04 10:52:25 +0000499static int write_image_to_file(const image_t *image, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100500{
501 FILE *fp;
502
503 fp = fopen(filename, "w");
504 if (fp == NULL)
505 log_err("fopen");
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900506 xfwrite(image->buffer, image->size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100507 fclose(fp);
508 return 0;
509}
510
dp-arm90d2f0e2016-11-14 15:54:32 +0000511static struct option *add_opt(struct option *opts, size_t *nr_opts,
512 const char *name, int has_arg, int val)
dp-arm4972ec52016-05-25 16:20:20 +0100513{
dp-arm90d2f0e2016-11-14 15:54:32 +0000514 opts = realloc(opts, (*nr_opts + 1) * sizeof(*opts));
515 if (opts == NULL)
516 log_err("realloc");
517 opts[*nr_opts].name = name;
518 opts[*nr_opts].has_arg = has_arg;
519 opts[*nr_opts].flag = NULL;
520 opts[*nr_opts].val = val;
521 ++*nr_opts;
522 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100523}
524
dp-arm90d2f0e2016-11-14 15:54:32 +0000525static struct option *fill_common_opts(struct option *opts, size_t *nr_opts,
526 int has_arg)
dp-arm4972ec52016-05-25 16:20:20 +0100527{
dp-arm90d2f0e2016-11-14 15:54:32 +0000528 image_desc_t *desc;
529
530 for (desc = image_desc_head; desc != NULL; desc = desc->next)
531 opts = add_opt(opts, nr_opts, desc->cmdline_name, has_arg,
532 OPT_TOC_ENTRY);
533 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100534}
535
dp-arm90d2f0e2016-11-14 15:54:32 +0000536static void md_print(const unsigned char *md, size_t len)
dp-arm12e893b2016-08-24 13:21:08 +0100537{
538 size_t i;
539
540 for (i = 0; i < len; i++)
541 printf("%02x", md[i]);
542}
543
dp-arm4972ec52016-05-25 16:20:20 +0100544static int info_cmd(int argc, char *argv[])
545{
546 image_t *image;
547 uint64_t image_offset;
dp-arm516dfcb2016-11-03 13:59:26 +0000548 uint64_t image_size;
dp-arm4972ec52016-05-25 16:20:20 +0100549 fip_toc_header_t toc_header;
dp-arm4972ec52016-05-25 16:20:20 +0100550
551 if (argc != 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100552 info_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100553 argc--, argv++;
554
555 parse_fip(argv[0], &toc_header);
556
557 if (verbose) {
558 log_dbgx("toc_header[name]: 0x%llX",
559 (unsigned long long)toc_header.name);
560 log_dbgx("toc_header[serial_number]: 0x%llX",
561 (unsigned long long)toc_header.serial_number);
562 log_dbgx("toc_header[flags]: 0x%llX",
563 (unsigned long long)toc_header.flags);
564 }
565
566 image_offset = sizeof(fip_toc_header_t) +
567 (sizeof(fip_toc_entry_t) * (nr_images + 1));
568
dp-arm90d2f0e2016-11-14 15:54:32 +0000569 for (image = image_head; image != NULL; image = image->next) {
570 image_desc_t *desc;
dp-arm715ef422016-08-30 14:18:58 +0100571
dp-arm90d2f0e2016-11-14 15:54:32 +0000572 desc = lookup_image_desc_from_uuid(&image->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000573 assert(desc != NULL);
574 printf("%s: ", desc->name);
dp-arm4972ec52016-05-25 16:20:20 +0100575 image_size = image->size;
Masahiro Yamadaeee39312017-01-15 23:20:00 +0900576 printf("offset=0x%llX, size=0x%llX, cmdline=\"--%s\"",
577 (unsigned long long)image_offset,
578 (unsigned long long)image_size,
579 desc->cmdline_name);
dp-arm12e893b2016-08-24 13:21:08 +0100580 if (verbose) {
581 unsigned char md[SHA256_DIGEST_LENGTH];
582
583 SHA256(image->buffer, image_size, md);
584 printf(", sha256=");
585 md_print(md, sizeof(md));
586 }
587 putchar('\n');
dp-arm4972ec52016-05-25 16:20:20 +0100588 image_offset += image_size;
589 }
590
591 free_images();
592 return 0;
593}
594
595static void info_usage(void)
596{
597 printf("fiptool info FIP_FILENAME\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100598 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100599}
600
dp-arm90d2f0e2016-11-14 15:54:32 +0000601static int pack_images(const char *filename, uint64_t toc_flags)
dp-arm4972ec52016-05-25 16:20:20 +0100602{
603 FILE *fp;
604 image_t *image;
605 fip_toc_header_t *toc_header;
606 fip_toc_entry_t *toc_entry;
607 char *buf;
608 uint64_t entry_offset, buf_size, payload_size;
dp-arm4972ec52016-05-25 16:20:20 +0100609
610 /* Calculate total payload size and allocate scratch buffer. */
611 payload_size = 0;
dp-arm90d2f0e2016-11-14 15:54:32 +0000612 for (image = image_head; image != NULL; image = image->next)
613 payload_size += image->size;
dp-arm4972ec52016-05-25 16:20:20 +0100614
615 buf_size = sizeof(fip_toc_header_t) +
616 sizeof(fip_toc_entry_t) * (nr_images + 1);
617 buf = calloc(1, buf_size);
618 if (buf == NULL)
619 log_err("calloc");
620
621 /* Build up header and ToC entries from the image table. */
622 toc_header = (fip_toc_header_t *)buf;
623 toc_header->name = TOC_HEADER_NAME;
624 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
625 toc_header->flags = toc_flags;
626
627 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
628
629 entry_offset = buf_size;
dp-arm90d2f0e2016-11-14 15:54:32 +0000630 for (image = image_head; image != NULL; image = image->next) {
dp-arm4972ec52016-05-25 16:20:20 +0100631 memcpy(&toc_entry->uuid, &image->uuid, sizeof(uuid_t));
632 toc_entry->offset_address = entry_offset;
633 toc_entry->size = image->size;
634 toc_entry->flags = 0;
635 entry_offset += toc_entry->size;
636 toc_entry++;
637 }
638
639 /* Append a null uuid entry to mark the end of ToC entries. */
640 memcpy(&toc_entry->uuid, &uuid_null, sizeof(uuid_t));
641 toc_entry->offset_address = entry_offset;
642 toc_entry->size = 0;
643 toc_entry->flags = 0;
644
645 /* Generate the FIP file. */
646 fp = fopen(filename, "w");
647 if (fp == NULL)
648 log_err("fopen %s", filename);
649
650 if (verbose)
651 log_dbgx("Metadata size: %zu bytes", buf_size);
652
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900653 xfwrite(buf, buf_size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100654 free(buf);
655
656 if (verbose)
657 log_dbgx("Payload size: %zu bytes", payload_size);
658
dp-arm90d2f0e2016-11-14 15:54:32 +0000659 for (image = image_head; image != NULL; image = image->next)
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900660 xfwrite(image->buffer, image->size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100661
662 fclose(fp);
663 return 0;
664}
665
666/*
667 * This function is shared between the create and update subcommands.
668 * The difference between the two subcommands is that when the FIP file
669 * is created, the parsing of an existing FIP is skipped. This results
670 * in update_fip() creating the new FIP file from scratch because the
671 * internal image table is not populated.
672 */
673static void update_fip(void)
674{
dp-arm90d2f0e2016-11-14 15:54:32 +0000675 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100676
677 /* Add or replace images in the FIP file. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000678 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +0000679 image_t *new_image, *old_image;
680
dp-arm90d2f0e2016-11-14 15:54:32 +0000681 if (desc->action != DO_PACK)
dp-arm4972ec52016-05-25 16:20:20 +0100682 continue;
683
dp-arm90d2f0e2016-11-14 15:54:32 +0000684 new_image = read_image_from_file(&desc->uuid,
685 desc->action_arg);
686 old_image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +0100687 if (old_image != NULL) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000688 if (verbose) {
dp-arm516dfcb2016-11-03 13:59:26 +0000689 log_dbgx("Replacing %s with %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000690 desc->cmdline_name,
691 desc->action_arg);
692 }
Masahiro Yamadad65e7fd2017-01-27 11:57:54 +0900693 replace_image(new_image);
dp-arm4972ec52016-05-25 16:20:20 +0100694 } else {
695 if (verbose)
696 log_dbgx("Adding image %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000697 desc->action_arg);
dp-arm715ef422016-08-30 14:18:58 +0100698 add_image(new_image);
dp-arm4972ec52016-05-25 16:20:20 +0100699 }
dp-arm4972ec52016-05-25 16:20:20 +0100700 }
701}
702
dp-arm90d2f0e2016-11-14 15:54:32 +0000703static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
dp-arm4972ec52016-05-25 16:20:20 +0100704{
705 unsigned long long flags;
706 char *endptr;
707
708 errno = 0;
709 flags = strtoull(arg, &endptr, 16);
710 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
711 log_errx("Invalid platform ToC flags: %s", arg);
712 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
713 *toc_flags |= flags << 32;
714}
715
dp-arm516dfcb2016-11-03 13:59:26 +0000716static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
717{
718 char *p;
719
720 for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
721 if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
722 p += strlen("uuid=");
723 uuid_from_str(uuid, p);
724 } else if (strncmp(p, "file=", strlen("file=")) == 0) {
725 p += strlen("file=");
726 snprintf(filename, len, "%s", p);
727 }
728 }
729}
730
dp-arm4972ec52016-05-25 16:20:20 +0100731static int create_cmd(int argc, char *argv[])
732{
dp-arm90d2f0e2016-11-14 15:54:32 +0000733 struct option *opts = NULL;
734 size_t nr_opts = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100735 unsigned long long toc_flags = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100736
737 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100738 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100739
dp-arm90d2f0e2016-11-14 15:54:32 +0000740 opts = fill_common_opts(opts, &nr_opts, required_argument);
741 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100742 OPT_PLAT_TOC_FLAGS);
dp-arm516dfcb2016-11-03 13:59:26 +0000743 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000744 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100745
746 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000747 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100748
dp-arm516dfcb2016-11-03 13:59:26 +0000749 c = getopt_long(argc, argv, "b:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100750 if (c == -1)
751 break;
752
753 switch (c) {
754 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000755 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100756
dp-arm90d2f0e2016-11-14 15:54:32 +0000757 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000758 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100759 break;
760 }
761 case OPT_PLAT_TOC_FLAGS:
762 parse_plat_toc_flags(optarg, &toc_flags);
763 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000764 case 'b': {
765 char name[_UUID_STR_LEN + 1];
766 char filename[PATH_MAX] = { 0 };
767 uuid_t uuid = { 0 };
768 image_desc_t *desc;
769
770 parse_blob_opt(optarg, &uuid,
771 filename, sizeof(filename));
772
773 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
774 filename[0] == '\0')
775 create_usage();
776
777 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000778 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000779 uuid_to_str(name, sizeof(name), &uuid);
780 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000781 add_image_desc(desc);
782 }
dp-armfb732312016-12-30 09:55:48 +0000783 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000784 break;
785 }
dp-arm4972ec52016-05-25 16:20:20 +0100786 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100787 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100788 }
789 }
790 argc -= optind;
791 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000792 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100793
794 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100795 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100796
797 update_fip();
798
799 pack_images(argv[0], toc_flags);
800 free_images();
801 return 0;
802}
803
804static void create_usage(void)
805{
806 toc_entry_t *toc_entry = toc_entries;
807
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900808 printf("fiptool create [opts] FIP_FILENAME\n");
809 printf("\n");
810 printf("Options:\n");
dp-arm516dfcb2016-11-03 13:59:26 +0000811 printf(" --blob uuid=...,file=...\tAdd an image with the given UUID "
812 "pointed to by file.\n");
dp-arm4972ec52016-05-25 16:20:20 +0100813 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field "
814 "occupying bits 32-47 in 64-bit ToC header.\n");
815 fputc('\n', stderr);
816 printf("Specific images are packed with the following options:\n");
817 for (; toc_entry->cmdline_name != NULL; toc_entry++)
818 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
819 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100820 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100821}
822
823static int update_cmd(int argc, char *argv[])
824{
dp-arm90d2f0e2016-11-14 15:54:32 +0000825 struct option *opts = NULL;
826 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000827 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100828 fip_toc_header_t toc_header = { 0 };
829 unsigned long long toc_flags = 0;
830 int pflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100831
832 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100833 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100834
dp-arm90d2f0e2016-11-14 15:54:32 +0000835 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000836 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000837 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
838 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100839 OPT_PLAT_TOC_FLAGS);
dp-arm90d2f0e2016-11-14 15:54:32 +0000840 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100841
842 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000843 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100844
dp-arm516dfcb2016-11-03 13:59:26 +0000845 c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100846 if (c == -1)
847 break;
848
849 switch (c) {
850 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000851 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100852
dp-arm90d2f0e2016-11-14 15:54:32 +0000853 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000854 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100855 break;
856 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000857 case OPT_PLAT_TOC_FLAGS:
dp-arm4972ec52016-05-25 16:20:20 +0100858 parse_plat_toc_flags(optarg, &toc_flags);
859 pflag = 1;
860 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000861 case 'b': {
862 char name[_UUID_STR_LEN + 1];
863 char filename[PATH_MAX] = { 0 };
864 uuid_t uuid = { 0 };
865 image_desc_t *desc;
866
867 parse_blob_opt(optarg, &uuid,
868 filename, sizeof(filename));
869
870 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
871 filename[0] == '\0')
872 update_usage();
873
874 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000875 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000876 uuid_to_str(name, sizeof(name), &uuid);
877 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000878 add_image_desc(desc);
879 }
dp-armfb732312016-12-30 09:55:48 +0000880 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000881 break;
882 }
dp-arm4972ec52016-05-25 16:20:20 +0100883 case 'o':
884 snprintf(outfile, sizeof(outfile), "%s", optarg);
885 break;
886 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100887 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100888 }
889 }
890 argc -= optind;
891 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000892 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100893
894 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100895 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100896
897 if (outfile[0] == '\0')
898 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
899
Masahiro Yamadaebb6e372016-12-25 12:41:41 +0900900 if (access(argv[0], F_OK) == 0)
dp-arm4972ec52016-05-25 16:20:20 +0100901 parse_fip(argv[0], &toc_header);
902
903 if (pflag)
904 toc_header.flags &= ~(0xffffULL << 32);
905 toc_flags = (toc_header.flags |= toc_flags);
906
907 update_fip();
908
909 pack_images(outfile, toc_flags);
910 free_images();
911 return 0;
912}
913
914static void update_usage(void)
915{
916 toc_entry_t *toc_entry = toc_entries;
917
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900918 printf("fiptool update [opts] FIP_FILENAME\n");
919 printf("\n");
920 printf("Options:\n");
dp-arm516dfcb2016-11-03 13:59:26 +0000921 printf(" --blob uuid=...,file=...\tAdd or update an image "
922 "with the given UUID pointed to by file.\n");
dp-arm4972ec52016-05-25 16:20:20 +0100923 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
924 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field "
925 "occupying bits 32-47 in 64-bit ToC header.\n");
926 fputc('\n', stderr);
927 printf("Specific images are packed with the following options:\n");
928 for (; toc_entry->cmdline_name != NULL; toc_entry++)
929 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
930 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100931 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100932}
933
934static int unpack_cmd(int argc, char *argv[])
935{
dp-arm90d2f0e2016-11-14 15:54:32 +0000936 struct option *opts = NULL;
937 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000938 char outdir[PATH_MAX] = { 0 };
dp-arm90d2f0e2016-11-14 15:54:32 +0000939 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100940 int fflag = 0;
941 int unpack_all = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100942
943 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100944 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100945
dp-arm90d2f0e2016-11-14 15:54:32 +0000946 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000947 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000948 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
949 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
950 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100951
952 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000953 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100954
dp-arm516dfcb2016-11-03 13:59:26 +0000955 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100956 if (c == -1)
957 break;
958
959 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000960 case OPT_TOC_ENTRY: {
961 image_desc_t *desc;
962
963 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000964 set_image_desc_action(desc, DO_UNPACK, optarg);
dp-arm90d2f0e2016-11-14 15:54:32 +0000965 unpack_all = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100966 break;
dp-arm90d2f0e2016-11-14 15:54:32 +0000967 }
dp-arm516dfcb2016-11-03 13:59:26 +0000968 case 'b': {
969 char name[_UUID_STR_LEN + 1];
970 char filename[PATH_MAX] = { 0 };
971 uuid_t uuid = { 0 };
972 image_desc_t *desc;
973
974 parse_blob_opt(optarg, &uuid,
975 filename, sizeof(filename));
976
977 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
978 filename[0] == '\0')
979 unpack_usage();
980
981 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000982 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000983 uuid_to_str(name, sizeof(name), &uuid);
984 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000985 add_image_desc(desc);
986 }
dp-armfb732312016-12-30 09:55:48 +0000987 set_image_desc_action(desc, DO_UNPACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000988 unpack_all = 0;
989 break;
990 }
dp-arm4972ec52016-05-25 16:20:20 +0100991 case 'f':
992 fflag = 1;
993 break;
994 case 'o':
995 snprintf(outdir, sizeof(outdir), "%s", optarg);
996 break;
997 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100998 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100999 }
1000 }
1001 argc -= optind;
1002 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001003 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001004
1005 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001006 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001007
1008 parse_fip(argv[0], NULL);
1009
1010 if (outdir[0] != '\0')
1011 if (chdir(outdir) == -1)
1012 log_err("chdir %s", outdir);
1013
dp-arm4972ec52016-05-25 16:20:20 +01001014 /* Unpack all specified images. */
dp-arm90d2f0e2016-11-14 15:54:32 +00001015 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +00001016 char file[PATH_MAX];
dp-arm715ef422016-08-30 14:18:58 +01001017 image_t *image;
1018
dp-arm90d2f0e2016-11-14 15:54:32 +00001019 if (!unpack_all && desc->action != DO_UNPACK)
dp-arm4972ec52016-05-25 16:20:20 +01001020 continue;
1021
1022 /* Build filename. */
dp-arm90d2f0e2016-11-14 15:54:32 +00001023 if (desc->action_arg == NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001024 snprintf(file, sizeof(file), "%s.bin",
dp-arm90d2f0e2016-11-14 15:54:32 +00001025 desc->cmdline_name);
dp-arm4972ec52016-05-25 16:20:20 +01001026 else
1027 snprintf(file, sizeof(file), "%s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001028 desc->action_arg);
dp-arm4972ec52016-05-25 16:20:20 +01001029
dp-arm90d2f0e2016-11-14 15:54:32 +00001030 image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +01001031 if (image == NULL) {
1032 if (!unpack_all)
dp-arm516dfcb2016-11-03 13:59:26 +00001033 log_warnx("%s does not exist in %s",
dp-arm715ef422016-08-30 14:18:58 +01001034 file, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001035 continue;
1036 }
1037
1038 if (access(file, F_OK) != 0 || fflag) {
1039 if (verbose)
1040 log_dbgx("Unpacking %s", file);
dp-arm715ef422016-08-30 14:18:58 +01001041 write_image_to_file(image, file);
dp-arm4972ec52016-05-25 16:20:20 +01001042 } else {
1043 log_warnx("File %s already exists, use --force to overwrite it",
1044 file);
1045 }
dp-arm4972ec52016-05-25 16:20:20 +01001046 }
1047
1048 free_images();
1049 return 0;
1050}
1051
1052static void unpack_usage(void)
1053{
1054 toc_entry_t *toc_entry = toc_entries;
1055
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001056 printf("fiptool unpack [opts] FIP_FILENAME\n");
1057 printf("\n");
1058 printf("Options:\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001059 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID "
1060 "to file.\n");
1061 printf(" --force\t\t\tIf the output file already exists, use --force to "
dp-arm4972ec52016-05-25 16:20:20 +01001062 "overwrite it.\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001063 printf(" --out path\t\t\tSet the output directory path.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001064 fputc('\n', stderr);
1065 printf("Specific images are unpacked with the following options:\n");
1066 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1067 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
1068 toc_entry->name);
1069 fputc('\n', stderr);
1070 printf("If no options are provided, all images will be unpacked.\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001071 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001072}
1073
1074static int remove_cmd(int argc, char *argv[])
1075{
dp-arm90d2f0e2016-11-14 15:54:32 +00001076 struct option *opts = NULL;
1077 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +00001078 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +01001079 fip_toc_header_t toc_header;
dp-arm90d2f0e2016-11-14 15:54:32 +00001080 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +01001081 int fflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001082
1083 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +01001084 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001085
dp-arm90d2f0e2016-11-14 15:54:32 +00001086 opts = fill_common_opts(opts, &nr_opts, no_argument);
dp-arm516dfcb2016-11-03 13:59:26 +00001087 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +00001088 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
1089 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
1090 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +01001091
1092 while (1) {
dp-armfe92b892016-11-07 11:13:54 +00001093 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001094
dp-arm516dfcb2016-11-03 13:59:26 +00001095 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +01001096 if (c == -1)
1097 break;
1098
1099 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001100 case OPT_TOC_ENTRY: {
1101 image_desc_t *desc;
1102
1103 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +00001104 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm4972ec52016-05-25 16:20:20 +01001105 break;
dp-arm90d2f0e2016-11-14 15:54:32 +00001106 }
dp-arm516dfcb2016-11-03 13:59:26 +00001107 case 'b': {
1108 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
1109 uuid_t uuid = { 0 };
1110 image_desc_t *desc;
1111
1112 parse_blob_opt(optarg, &uuid,
1113 filename, sizeof(filename));
1114
1115 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
1116 remove_usage();
1117
1118 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +00001119 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +00001120 uuid_to_str(name, sizeof(name), &uuid);
1121 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +00001122 add_image_desc(desc);
1123 }
dp-armfb732312016-12-30 09:55:48 +00001124 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm516dfcb2016-11-03 13:59:26 +00001125 break;
1126 }
dp-arm4972ec52016-05-25 16:20:20 +01001127 case 'f':
1128 fflag = 1;
1129 break;
1130 case 'o':
1131 snprintf(outfile, sizeof(outfile), "%s", optarg);
1132 break;
1133 default:
dp-arm29f1b5c2016-09-15 09:58:50 +01001134 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001135 }
1136 }
1137 argc -= optind;
1138 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001139 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001140
1141 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001142 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001143
1144 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
1145 log_errx("File %s already exists, use --force to overwrite it",
1146 outfile);
1147
1148 if (outfile[0] == '\0')
1149 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
1150
1151 parse_fip(argv[0], &toc_header);
1152
dp-arm90d2f0e2016-11-14 15:54:32 +00001153 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm715ef422016-08-30 14:18:58 +01001154 image_t *image;
1155
dp-arm90d2f0e2016-11-14 15:54:32 +00001156 if (desc->action != DO_REMOVE)
dp-arm4972ec52016-05-25 16:20:20 +01001157 continue;
dp-arm516dfcb2016-11-03 13:59:26 +00001158
dp-arm90d2f0e2016-11-14 15:54:32 +00001159 image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +01001160 if (image != NULL) {
dp-arm4972ec52016-05-25 16:20:20 +01001161 if (verbose)
dp-arm516dfcb2016-11-03 13:59:26 +00001162 log_dbgx("Removing %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001163 desc->cmdline_name);
dp-arm715ef422016-08-30 14:18:58 +01001164 remove_image(image);
dp-arm4972ec52016-05-25 16:20:20 +01001165 } else {
dp-arm516dfcb2016-11-03 13:59:26 +00001166 log_warnx("%s does not exist in %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001167 desc->cmdline_name, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001168 }
1169 }
1170
1171 pack_images(outfile, toc_header.flags);
1172 free_images();
1173 return 0;
1174}
1175
1176static void remove_usage(void)
1177{
1178 toc_entry_t *toc_entry = toc_entries;
1179
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001180 printf("fiptool remove [opts] FIP_FILENAME\n");
1181 printf("\n");
1182 printf("Options:\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001183 printf(" --blob uuid=...\tRemove an image with the given UUID.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001184 printf(" --force\t\tIf the output FIP file already exists, use --force to "
1185 "overwrite it.\n");
1186 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
1187 fputc('\n', stderr);
1188 printf("Specific images are removed with the following options:\n");
1189 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1190 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
1191 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +01001192 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001193}
1194
1195static int version_cmd(int argc, char *argv[])
1196{
1197#ifdef VERSION
1198 puts(VERSION);
1199#else
1200 /* If built from fiptool directory, VERSION is not set. */
1201 puts("Unknown version");
1202#endif
1203 return 0;
1204}
1205
1206static void version_usage(void)
1207{
1208 printf("fiptool version\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001209 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001210}
1211
1212static int help_cmd(int argc, char *argv[])
1213{
1214 int i;
1215
1216 if (argc < 2)
1217 usage();
1218 argc--, argv++;
1219
1220 for (i = 0; i < NELEM(cmds); i++) {
1221 if (strcmp(cmds[i].name, argv[0]) == 0 &&
dp-arm29f1b5c2016-09-15 09:58:50 +01001222 cmds[i].usage != NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001223 cmds[i].usage();
dp-arm4972ec52016-05-25 16:20:20 +01001224 }
1225 if (i == NELEM(cmds))
1226 printf("No help for subcommand '%s'\n", argv[0]);
1227 return 0;
1228}
1229
1230static void usage(void)
1231{
Masahiro Yamada252c3362017-01-13 02:13:06 +09001232 printf("usage: fiptool [--verbose] <command> [<args>]\n");
dp-arm4972ec52016-05-25 16:20:20 +01001233 printf("Global options supported:\n");
1234 printf(" --verbose\tEnable verbose output for all commands.\n");
1235 fputc('\n', stderr);
1236 printf("Commands supported:\n");
1237 printf(" info\t\tList images contained in FIP.\n");
1238 printf(" create\tCreate a new FIP with the given images.\n");
1239 printf(" update\tUpdate an existing FIP with the given images.\n");
1240 printf(" unpack\tUnpack images from FIP.\n");
1241 printf(" remove\tRemove images from FIP.\n");
1242 printf(" version\tShow fiptool version.\n");
1243 printf(" help\t\tShow help for given command.\n");
1244 exit(1);
1245}
1246
1247int main(int argc, char *argv[])
1248{
1249 int i, ret = 0;
1250
dp-arm5cd10ae2016-11-07 10:45:59 +00001251 while (1) {
1252 int c, opt_index = 0;
1253 static struct option opts[] = {
1254 { "verbose", no_argument, NULL, 'v' },
1255 { NULL, no_argument, NULL, 0 }
1256 };
1257
1258 /*
1259 * Set POSIX mode so getopt stops at the first non-option
1260 * which is the subcommand.
1261 */
1262 c = getopt_long(argc, argv, "+v", opts, &opt_index);
1263 if (c == -1)
1264 break;
dp-arm4972ec52016-05-25 16:20:20 +01001265
dp-arm5cd10ae2016-11-07 10:45:59 +00001266 switch (c) {
1267 case 'v':
1268 verbose = 1;
1269 break;
1270 default:
Masahiro Yamada48a24972016-10-26 13:24:26 +09001271 usage();
dp-arm5cd10ae2016-11-07 10:45:59 +00001272 }
dp-arm4972ec52016-05-25 16:20:20 +01001273 }
dp-arm5cd10ae2016-11-07 10:45:59 +00001274 argc -= optind;
1275 argv += optind;
1276 /* Reset optind for subsequent getopt processing. */
1277 optind = 0;
1278
1279 if (argc == 0)
1280 usage();
dp-arm4972ec52016-05-25 16:20:20 +01001281
dp-arm90d2f0e2016-11-14 15:54:32 +00001282 fill_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001283 for (i = 0; i < NELEM(cmds); i++) {
1284 if (strcmp(cmds[i].name, argv[0]) == 0) {
1285 ret = cmds[i].handler(argc, argv);
1286 break;
1287 }
1288 }
1289 if (i == NELEM(cmds))
1290 usage();
dp-arm90d2f0e2016-11-14 15:54:32 +00001291 free_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001292 return ret;
1293}