blob: f3f831bd208ac3a46b239a3bb490b5df6d86496d [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
Masahiro Yamada4d87eb42016-12-25 13:52:22 +090053#define OPT_ALIGN 2
dp-arm4972ec52016-05-25 16:20:20 +010054
55static int info_cmd(int argc, char *argv[]);
56static void info_usage(void);
57static int create_cmd(int argc, char *argv[]);
58static void create_usage(void);
59static int update_cmd(int argc, char *argv[]);
60static void update_usage(void);
61static int unpack_cmd(int argc, char *argv[]);
62static void unpack_usage(void);
63static int remove_cmd(int argc, char *argv[]);
64static void remove_usage(void);
65static int version_cmd(int argc, char *argv[]);
66static void version_usage(void);
67static int help_cmd(int argc, char *argv[]);
68static void usage(void);
69
70/* Available subcommands. */
71static cmd_t cmds[] = {
72 { .name = "info", .handler = info_cmd, .usage = info_usage },
73 { .name = "create", .handler = create_cmd, .usage = create_usage },
74 { .name = "update", .handler = update_cmd, .usage = update_usage },
75 { .name = "unpack", .handler = unpack_cmd, .usage = unpack_usage },
76 { .name = "remove", .handler = remove_cmd, .usage = remove_usage },
77 { .name = "version", .handler = version_cmd, .usage = version_usage },
78 { .name = "help", .handler = help_cmd, .usage = NULL },
79};
80
dp-arm90d2f0e2016-11-14 15:54:32 +000081static image_desc_t *image_desc_head;
82static size_t nr_image_descs;
83static image_t *image_head;
dp-arm4972ec52016-05-25 16:20:20 +010084static size_t nr_images;
85static uuid_t uuid_null = { 0 };
86static int verbose;
87
dp-armc1f8e772016-11-04 10:52:25 +000088static void vlog(int prio, const char *msg, va_list ap)
dp-arm4972ec52016-05-25 16:20:20 +010089{
90 char *prefix[] = { "DEBUG", "WARN", "ERROR" };
91
92 fprintf(stderr, "%s: ", prefix[prio]);
93 vfprintf(stderr, msg, ap);
94 fputc('\n', stderr);
95}
96
dp-armc1f8e772016-11-04 10:52:25 +000097static void log_dbgx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010098{
99 va_list ap;
100
101 va_start(ap, msg);
102 vlog(LOG_DBG, msg, ap);
103 va_end(ap);
104}
105
dp-armc1f8e772016-11-04 10:52:25 +0000106static void log_warnx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +0100107{
108 va_list ap;
109
110 va_start(ap, msg);
111 vlog(LOG_WARN, msg, ap);
112 va_end(ap);
113}
114
dp-armc1f8e772016-11-04 10:52:25 +0000115static void log_err(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +0100116{
117 char buf[512];
118 va_list ap;
119
120 va_start(ap, msg);
121 snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
122 vlog(LOG_ERR, buf, ap);
123 va_end(ap);
124 exit(1);
125}
126
dp-armc1f8e772016-11-04 10:52:25 +0000127static void log_errx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +0100128{
129 va_list ap;
130
131 va_start(ap, msg);
132 vlog(LOG_ERR, msg, ap);
133 va_end(ap);
134 exit(1);
135}
136
dp-armdb0f5e92016-11-04 10:56:25 +0000137static char *xstrdup(const char *s, const char *msg)
138{
139 char *d;
140
141 d = strdup(s);
142 if (d == NULL)
dp-arm5ba38542016-12-21 14:59:30 +0000143 log_errx("strdup: %s", msg);
dp-armdb0f5e92016-11-04 10:56:25 +0000144 return d;
145}
146
147static void *xmalloc(size_t size, const char *msg)
148{
149 void *d;
150
151 d = malloc(size);
152 if (d == NULL)
dp-arm5ba38542016-12-21 14:59:30 +0000153 log_errx("malloc: %s", msg);
dp-armdb0f5e92016-11-04 10:56:25 +0000154 return d;
155}
156
Masahiro Yamada03fdf692017-01-15 00:50:41 +0900157static void *xzalloc(size_t size, const char *msg)
158{
159 return memset(xmalloc(size, msg), 0, size);
160}
161
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900162static void xfwrite(void *buf, size_t size, FILE *fp, const char *filename)
163{
164 if (fwrite(buf, 1, size, fp) != size)
165 log_errx("Failed to write %s", filename);
166}
167
dp-arm90d2f0e2016-11-14 15:54:32 +0000168static image_desc_t *new_image_desc(const uuid_t *uuid,
169 const char *name, const char *cmdline_name)
dp-arm4972ec52016-05-25 16:20:20 +0100170{
dp-arm90d2f0e2016-11-14 15:54:32 +0000171 image_desc_t *desc;
172
Masahiro Yamada03fdf692017-01-15 00:50:41 +0900173 desc = xzalloc(sizeof(*desc),
dp-arm90d2f0e2016-11-14 15:54:32 +0000174 "failed to allocate memory for image descriptor");
175 memcpy(&desc->uuid, uuid, sizeof(uuid_t));
176 desc->name = xstrdup(name,
177 "failed to allocate memory for image name");
178 desc->cmdline_name = xstrdup(cmdline_name,
179 "failed to allocate memory for image command line name");
180 desc->action = DO_UNSPEC;
dp-arm90d2f0e2016-11-14 15:54:32 +0000181 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100182}
183
dp-armfb732312016-12-30 09:55:48 +0000184static void set_image_desc_action(image_desc_t *desc, int action,
185 const char *arg)
186{
187 assert(desc != NULL);
188
189 if (desc->action_arg != DO_UNSPEC)
190 free(desc->action_arg);
191 desc->action = action;
192 desc->action_arg = NULL;
193 if (arg != NULL)
194 desc->action_arg = xstrdup(arg,
195 "failed to allocate memory for argument");
196}
197
dp-arm90d2f0e2016-11-14 15:54:32 +0000198static void free_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100199{
dp-arm90d2f0e2016-11-14 15:54:32 +0000200 free(desc->name);
201 free(desc->cmdline_name);
202 free(desc->action_arg);
203 free(desc);
dp-arm4972ec52016-05-25 16:20:20 +0100204}
205
dp-arm90d2f0e2016-11-14 15:54:32 +0000206static void add_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100207{
Masahiro Yamadad224b452017-01-14 23:22:02 +0900208 image_desc_t **p = &image_desc_head;
209
Masahiro Yamadad224b452017-01-14 23:22:02 +0900210 while (*p)
211 p = &(*p)->next;
212
Masahiro Yamadac2a7d9c2017-01-27 12:53:13 +0900213 assert(*p == NULL);
Masahiro Yamadad224b452017-01-14 23:22:02 +0900214 *p = desc;
dp-arm90d2f0e2016-11-14 15:54:32 +0000215 nr_image_descs++;
216}
dp-arm4972ec52016-05-25 16:20:20 +0100217
dp-arm90d2f0e2016-11-14 15:54:32 +0000218static void free_image_descs(void)
219{
220 image_desc_t *desc = image_desc_head, *tmp;
221
222 while (desc != NULL) {
223 tmp = desc->next;
224 free_image_desc(desc);
225 desc = tmp;
226 nr_image_descs--;
dp-arm4972ec52016-05-25 16:20:20 +0100227 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000228 assert(nr_image_descs == 0);
dp-arm4972ec52016-05-25 16:20:20 +0100229}
230
dp-arm90d2f0e2016-11-14 15:54:32 +0000231static void fill_image_descs(void)
232{
233 toc_entry_t *toc_entry;
234
235 for (toc_entry = toc_entries;
236 toc_entry->cmdline_name != NULL;
237 toc_entry++) {
238 image_desc_t *desc;
239
240 desc = new_image_desc(&toc_entry->uuid,
241 toc_entry->name,
242 toc_entry->cmdline_name);
243 add_image_desc(desc);
244 }
245}
246
247static void add_image(image_t *image)
248{
Masahiro Yamadad224b452017-01-14 23:22:02 +0900249 image_t **p = &image_head;
250
Masahiro Yamadad224b452017-01-14 23:22:02 +0900251 while (*p)
252 p = &(*p)->next;
253
Masahiro Yamadac2a7d9c2017-01-27 12:53:13 +0900254 assert(*p == NULL);
Masahiro Yamadad224b452017-01-14 23:22:02 +0900255 *p = image;
256
dp-arm90d2f0e2016-11-14 15:54:32 +0000257 nr_images++;
258}
259
Masahiro Yamadad65e7fd2017-01-27 11:57:54 +0900260static void replace_image(image_t *image)
261{
262 image_t **p = &image_head;
263
264 while (*p) {
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900265 if (!memcmp(&(*p)->toc_e.uuid, &image->toc_e.uuid,
266 sizeof(image->toc_e.uuid)))
Masahiro Yamadad65e7fd2017-01-27 11:57:54 +0900267 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)
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900340 if (!memcmp(&image->toc_e.uuid, uuid, sizeof(*uuid)))
dp-arm715ef422016-08-30 14:18:58 +0100341 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");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900433 image->toc_e = *toc_entry;
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);
dp-arm4972ec52016-05-25 16:20:20 +0100444
dp-arm516dfcb2016-11-03 13:59:26 +0000445 /* If this is an unknown image, create a descriptor for it. */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900446 desc = lookup_image_desc_from_uuid(&toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000447 if (desc == NULL) {
448 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
449
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900450 uuid_to_str(name, sizeof(name), &toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000451 snprintf(filename, sizeof(filename), "%s%s",
452 name, ".bin");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900453 desc = new_image_desc(&toc_entry->uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000454 desc->action = DO_UNPACK;
455 desc->action_arg = xstrdup(filename,
456 "failed to allocate memory for blob filename");
457 add_image_desc(desc);
458 }
459
dp-arm4972ec52016-05-25 16:20:20 +0100460 add_image(image);
461
462 toc_entry++;
463 }
464
465 if (terminated == 0)
466 log_errx("FIP %s does not have a ToC terminator entry",
467 filename);
468 free(buf);
469 return 0;
470}
471
dp-armc1f8e772016-11-04 10:52:25 +0000472static image_t *read_image_from_file(const uuid_t *uuid, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100473{
474 struct stat st;
475 image_t *image;
476 FILE *fp;
477
dp-arm715ef422016-08-30 14:18:58 +0100478 assert(uuid != NULL);
479
dp-arm4972ec52016-05-25 16:20:20 +0100480 fp = fopen(filename, "r");
481 if (fp == NULL)
482 log_err("fopen %s", filename);
483
484 if (fstat(fileno(fp), &st) == -1)
485 log_errx("fstat %s", filename);
486
Masahiro Yamadad224b452017-01-14 23:22:02 +0900487 image = xzalloc(sizeof(*image), "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900488 image->toc_e.uuid = *uuid;
dp-armdb0f5e92016-11-04 10:56:25 +0000489 image->buffer = xmalloc(st.st_size, "failed to allocate image buffer");
dp-arm4972ec52016-05-25 16:20:20 +0100490 if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
491 log_errx("Failed to read %s", filename);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900492 image->toc_e.size = st.st_size;
dp-arm4972ec52016-05-25 16:20:20 +0100493
494 fclose(fp);
495 return image;
496}
497
dp-armc1f8e772016-11-04 10:52:25 +0000498static int write_image_to_file(const image_t *image, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100499{
500 FILE *fp;
501
502 fp = fopen(filename, "w");
503 if (fp == NULL)
504 log_err("fopen");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900505 xfwrite(image->buffer, image->toc_e.size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100506 fclose(fp);
507 return 0;
508}
509
dp-arm90d2f0e2016-11-14 15:54:32 +0000510static struct option *add_opt(struct option *opts, size_t *nr_opts,
511 const char *name, int has_arg, int val)
dp-arm4972ec52016-05-25 16:20:20 +0100512{
dp-arm90d2f0e2016-11-14 15:54:32 +0000513 opts = realloc(opts, (*nr_opts + 1) * sizeof(*opts));
514 if (opts == NULL)
515 log_err("realloc");
516 opts[*nr_opts].name = name;
517 opts[*nr_opts].has_arg = has_arg;
518 opts[*nr_opts].flag = NULL;
519 opts[*nr_opts].val = val;
520 ++*nr_opts;
521 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100522}
523
dp-arm90d2f0e2016-11-14 15:54:32 +0000524static struct option *fill_common_opts(struct option *opts, size_t *nr_opts,
525 int has_arg)
dp-arm4972ec52016-05-25 16:20:20 +0100526{
dp-arm90d2f0e2016-11-14 15:54:32 +0000527 image_desc_t *desc;
528
529 for (desc = image_desc_head; desc != NULL; desc = desc->next)
530 opts = add_opt(opts, nr_opts, desc->cmdline_name, has_arg,
531 OPT_TOC_ENTRY);
532 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100533}
534
dp-arm90d2f0e2016-11-14 15:54:32 +0000535static void md_print(const unsigned char *md, size_t len)
dp-arm12e893b2016-08-24 13:21:08 +0100536{
537 size_t i;
538
539 for (i = 0; i < len; i++)
540 printf("%02x", md[i]);
541}
542
dp-arm4972ec52016-05-25 16:20:20 +0100543static int info_cmd(int argc, char *argv[])
544{
545 image_t *image;
dp-arm4972ec52016-05-25 16:20:20 +0100546 fip_toc_header_t toc_header;
dp-arm4972ec52016-05-25 16:20:20 +0100547
548 if (argc != 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100549 info_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100550 argc--, argv++;
551
552 parse_fip(argv[0], &toc_header);
553
554 if (verbose) {
555 log_dbgx("toc_header[name]: 0x%llX",
556 (unsigned long long)toc_header.name);
557 log_dbgx("toc_header[serial_number]: 0x%llX",
558 (unsigned long long)toc_header.serial_number);
559 log_dbgx("toc_header[flags]: 0x%llX",
560 (unsigned long long)toc_header.flags);
561 }
562
dp-arm90d2f0e2016-11-14 15:54:32 +0000563 for (image = image_head; image != NULL; image = image->next) {
564 image_desc_t *desc;
dp-arm715ef422016-08-30 14:18:58 +0100565
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900566 desc = lookup_image_desc_from_uuid(&image->toc_e.uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000567 assert(desc != NULL);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900568 printf("%s: offset=0x%llX, size=0x%llX, cmdline=\"--%s\"",
569 desc->name,
570 (unsigned long long)image->toc_e.offset_address,
571 (unsigned long long)image->toc_e.size,
Masahiro Yamadaeee39312017-01-15 23:20:00 +0900572 desc->cmdline_name);
dp-arm12e893b2016-08-24 13:21:08 +0100573 if (verbose) {
574 unsigned char md[SHA256_DIGEST_LENGTH];
575
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900576 SHA256(image->buffer, image->toc_e.size, md);
dp-arm12e893b2016-08-24 13:21:08 +0100577 printf(", sha256=");
578 md_print(md, sizeof(md));
579 }
580 putchar('\n');
dp-arm4972ec52016-05-25 16:20:20 +0100581 }
582
583 free_images();
584 return 0;
585}
586
587static void info_usage(void)
588{
589 printf("fiptool info FIP_FILENAME\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100590 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100591}
592
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900593static int pack_images(const char *filename, uint64_t toc_flags, unsigned long align)
dp-arm4972ec52016-05-25 16:20:20 +0100594{
595 FILE *fp;
596 image_t *image;
597 fip_toc_header_t *toc_header;
598 fip_toc_entry_t *toc_entry;
599 char *buf;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900600 uint64_t entry_offset, buf_size, payload_size = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100601
602 buf_size = sizeof(fip_toc_header_t) +
603 sizeof(fip_toc_entry_t) * (nr_images + 1);
604 buf = calloc(1, buf_size);
605 if (buf == NULL)
606 log_err("calloc");
607
608 /* Build up header and ToC entries from the image table. */
609 toc_header = (fip_toc_header_t *)buf;
610 toc_header->name = TOC_HEADER_NAME;
611 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
612 toc_header->flags = toc_flags;
613
614 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
615
616 entry_offset = buf_size;
dp-arm90d2f0e2016-11-14 15:54:32 +0000617 for (image = image_head; image != NULL; image = image->next) {
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900618 payload_size += image->toc_e.size;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900619 entry_offset = (entry_offset + align - 1) & ~(align - 1);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900620 image->toc_e.offset_address = entry_offset;
621 *toc_entry++ = image->toc_e;
622 entry_offset += image->toc_e.size;
dp-arm4972ec52016-05-25 16:20:20 +0100623 }
624
625 /* Append a null uuid entry to mark the end of ToC entries. */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900626 memset(toc_entry, 0, sizeof(*toc_entry));
dp-arm4972ec52016-05-25 16:20:20 +0100627 toc_entry->offset_address = entry_offset;
dp-arm4972ec52016-05-25 16:20:20 +0100628
629 /* Generate the FIP file. */
630 fp = fopen(filename, "w");
631 if (fp == NULL)
632 log_err("fopen %s", filename);
633
634 if (verbose)
635 log_dbgx("Metadata size: %zu bytes", buf_size);
636
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900637 xfwrite(buf, buf_size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100638 free(buf);
639
640 if (verbose)
641 log_dbgx("Payload size: %zu bytes", payload_size);
642
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900643 for (image = image_head; image != NULL; image = image->next) {
644 if (fseek(fp, image->toc_e.offset_address, SEEK_SET))
645 log_errx("Failed to set file position");
646
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900647 xfwrite(image->buffer, image->toc_e.size, fp, filename);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900648 }
dp-arm4972ec52016-05-25 16:20:20 +0100649
650 fclose(fp);
651 return 0;
652}
653
654/*
655 * This function is shared between the create and update subcommands.
656 * The difference between the two subcommands is that when the FIP file
657 * is created, the parsing of an existing FIP is skipped. This results
658 * in update_fip() creating the new FIP file from scratch because the
659 * internal image table is not populated.
660 */
661static void update_fip(void)
662{
dp-arm90d2f0e2016-11-14 15:54:32 +0000663 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100664
665 /* Add or replace images in the FIP file. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000666 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +0000667 image_t *new_image, *old_image;
668
dp-arm90d2f0e2016-11-14 15:54:32 +0000669 if (desc->action != DO_PACK)
dp-arm4972ec52016-05-25 16:20:20 +0100670 continue;
671
dp-arm90d2f0e2016-11-14 15:54:32 +0000672 new_image = read_image_from_file(&desc->uuid,
673 desc->action_arg);
674 old_image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +0100675 if (old_image != NULL) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000676 if (verbose) {
dp-arm516dfcb2016-11-03 13:59:26 +0000677 log_dbgx("Replacing %s with %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000678 desc->cmdline_name,
679 desc->action_arg);
680 }
Masahiro Yamadad65e7fd2017-01-27 11:57:54 +0900681 replace_image(new_image);
dp-arm4972ec52016-05-25 16:20:20 +0100682 } else {
683 if (verbose)
684 log_dbgx("Adding image %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000685 desc->action_arg);
dp-arm715ef422016-08-30 14:18:58 +0100686 add_image(new_image);
dp-arm4972ec52016-05-25 16:20:20 +0100687 }
dp-arm4972ec52016-05-25 16:20:20 +0100688 }
689}
690
dp-arm90d2f0e2016-11-14 15:54:32 +0000691static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
dp-arm4972ec52016-05-25 16:20:20 +0100692{
693 unsigned long long flags;
694 char *endptr;
695
696 errno = 0;
697 flags = strtoull(arg, &endptr, 16);
698 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
699 log_errx("Invalid platform ToC flags: %s", arg);
700 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
701 *toc_flags |= flags << 32;
702}
703
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900704static int is_power_of_2(unsigned long x)
705{
706 return x && !(x & (x - 1));
707}
708
709static unsigned long get_image_align(char *arg)
710{
711 char *endptr;
712 unsigned long align;
713
714 errno = 0;
715 align = strtoul(arg, &endptr, 10);
716 if (*endptr != '\0' || !is_power_of_2(align) || errno != 0)
717 log_errx("Invalid alignment: %s", arg);
718
719 return align;
720}
721
dp-arm516dfcb2016-11-03 13:59:26 +0000722static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
723{
724 char *p;
725
726 for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
727 if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
728 p += strlen("uuid=");
729 uuid_from_str(uuid, p);
730 } else if (strncmp(p, "file=", strlen("file=")) == 0) {
731 p += strlen("file=");
732 snprintf(filename, len, "%s", p);
733 }
734 }
735}
736
dp-arm4972ec52016-05-25 16:20:20 +0100737static int create_cmd(int argc, char *argv[])
738{
dp-arm90d2f0e2016-11-14 15:54:32 +0000739 struct option *opts = NULL;
740 size_t nr_opts = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100741 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900742 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100743
744 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100745 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100746
dp-arm90d2f0e2016-11-14 15:54:32 +0000747 opts = fill_common_opts(opts, &nr_opts, required_argument);
748 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100749 OPT_PLAT_TOC_FLAGS);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900750 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000751 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000752 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100753
754 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000755 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100756
dp-arm516dfcb2016-11-03 13:59:26 +0000757 c = getopt_long(argc, argv, "b:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100758 if (c == -1)
759 break;
760
761 switch (c) {
762 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000763 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100764
dp-arm90d2f0e2016-11-14 15:54:32 +0000765 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000766 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100767 break;
768 }
769 case OPT_PLAT_TOC_FLAGS:
770 parse_plat_toc_flags(optarg, &toc_flags);
771 break;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900772 case OPT_ALIGN:
773 align = get_image_align(optarg);
774 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000775 case 'b': {
776 char name[_UUID_STR_LEN + 1];
777 char filename[PATH_MAX] = { 0 };
778 uuid_t uuid = { 0 };
779 image_desc_t *desc;
780
781 parse_blob_opt(optarg, &uuid,
782 filename, sizeof(filename));
783
784 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
785 filename[0] == '\0')
786 create_usage();
787
788 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000789 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000790 uuid_to_str(name, sizeof(name), &uuid);
791 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000792 add_image_desc(desc);
793 }
dp-armfb732312016-12-30 09:55:48 +0000794 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000795 break;
796 }
dp-arm4972ec52016-05-25 16:20:20 +0100797 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100798 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100799 }
800 }
801 argc -= optind;
802 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000803 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100804
805 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100806 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100807
808 update_fip();
809
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900810 pack_images(argv[0], toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100811 free_images();
812 return 0;
813}
814
815static void create_usage(void)
816{
817 toc_entry_t *toc_entry = toc_entries;
818
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900819 printf("fiptool create [opts] FIP_FILENAME\n");
820 printf("\n");
821 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900822 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900823 printf(" --blob uuid=...,file=...\tAdd an image with the given UUID pointed to by file.\n");
824 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 +0900825 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100826 printf("Specific images are packed with the following options:\n");
827 for (; toc_entry->cmdline_name != NULL; toc_entry++)
828 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
829 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100830 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100831}
832
833static int update_cmd(int argc, char *argv[])
834{
dp-arm90d2f0e2016-11-14 15:54:32 +0000835 struct option *opts = NULL;
836 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000837 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100838 fip_toc_header_t toc_header = { 0 };
839 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900840 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100841 int pflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100842
843 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100844 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100845
dp-arm90d2f0e2016-11-14 15:54:32 +0000846 opts = fill_common_opts(opts, &nr_opts, required_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900847 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000848 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000849 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
850 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100851 OPT_PLAT_TOC_FLAGS);
dp-arm90d2f0e2016-11-14 15:54:32 +0000852 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100853
854 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000855 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100856
dp-arm516dfcb2016-11-03 13:59:26 +0000857 c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100858 if (c == -1)
859 break;
860
861 switch (c) {
862 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000863 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100864
dp-arm90d2f0e2016-11-14 15:54:32 +0000865 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000866 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100867 break;
868 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000869 case OPT_PLAT_TOC_FLAGS:
dp-arm4972ec52016-05-25 16:20:20 +0100870 parse_plat_toc_flags(optarg, &toc_flags);
871 pflag = 1;
872 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000873 case 'b': {
874 char name[_UUID_STR_LEN + 1];
875 char filename[PATH_MAX] = { 0 };
876 uuid_t uuid = { 0 };
877 image_desc_t *desc;
878
879 parse_blob_opt(optarg, &uuid,
880 filename, sizeof(filename));
881
882 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
883 filename[0] == '\0')
884 update_usage();
885
886 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000887 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000888 uuid_to_str(name, sizeof(name), &uuid);
889 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000890 add_image_desc(desc);
891 }
dp-armfb732312016-12-30 09:55:48 +0000892 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000893 break;
894 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900895 case OPT_ALIGN:
896 align = get_image_align(optarg);
897 break;
dp-arm4972ec52016-05-25 16:20:20 +0100898 case 'o':
899 snprintf(outfile, sizeof(outfile), "%s", optarg);
900 break;
901 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100902 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100903 }
904 }
905 argc -= optind;
906 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000907 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100908
909 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100910 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100911
912 if (outfile[0] == '\0')
913 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
914
Masahiro Yamadaebb6e372016-12-25 12:41:41 +0900915 if (access(argv[0], F_OK) == 0)
dp-arm4972ec52016-05-25 16:20:20 +0100916 parse_fip(argv[0], &toc_header);
917
918 if (pflag)
919 toc_header.flags &= ~(0xffffULL << 32);
920 toc_flags = (toc_header.flags |= toc_flags);
921
922 update_fip();
923
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900924 pack_images(outfile, toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100925 free_images();
926 return 0;
927}
928
929static void update_usage(void)
930{
931 toc_entry_t *toc_entry = toc_entries;
932
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900933 printf("fiptool update [opts] FIP_FILENAME\n");
934 printf("\n");
935 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900936 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900937 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 +0100938 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900939 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 +0900940 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100941 printf("Specific images are packed with the following options:\n");
942 for (; toc_entry->cmdline_name != NULL; toc_entry++)
943 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
944 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100945 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100946}
947
948static int unpack_cmd(int argc, char *argv[])
949{
dp-arm90d2f0e2016-11-14 15:54:32 +0000950 struct option *opts = NULL;
951 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000952 char outdir[PATH_MAX] = { 0 };
dp-arm90d2f0e2016-11-14 15:54:32 +0000953 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100954 int fflag = 0;
955 int unpack_all = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100956
957 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100958 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100959
dp-arm90d2f0e2016-11-14 15:54:32 +0000960 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000961 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000962 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
963 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
964 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100965
966 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000967 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100968
dp-arm516dfcb2016-11-03 13:59:26 +0000969 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100970 if (c == -1)
971 break;
972
973 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000974 case OPT_TOC_ENTRY: {
975 image_desc_t *desc;
976
977 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000978 set_image_desc_action(desc, DO_UNPACK, optarg);
dp-arm90d2f0e2016-11-14 15:54:32 +0000979 unpack_all = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100980 break;
dp-arm90d2f0e2016-11-14 15:54:32 +0000981 }
dp-arm516dfcb2016-11-03 13:59:26 +0000982 case 'b': {
983 char name[_UUID_STR_LEN + 1];
984 char filename[PATH_MAX] = { 0 };
985 uuid_t uuid = { 0 };
986 image_desc_t *desc;
987
988 parse_blob_opt(optarg, &uuid,
989 filename, sizeof(filename));
990
991 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
992 filename[0] == '\0')
993 unpack_usage();
994
995 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000996 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000997 uuid_to_str(name, sizeof(name), &uuid);
998 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000999 add_image_desc(desc);
1000 }
dp-armfb732312016-12-30 09:55:48 +00001001 set_image_desc_action(desc, DO_UNPACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +00001002 unpack_all = 0;
1003 break;
1004 }
dp-arm4972ec52016-05-25 16:20:20 +01001005 case 'f':
1006 fflag = 1;
1007 break;
1008 case 'o':
1009 snprintf(outdir, sizeof(outdir), "%s", optarg);
1010 break;
1011 default:
dp-arm29f1b5c2016-09-15 09:58:50 +01001012 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001013 }
1014 }
1015 argc -= optind;
1016 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001017 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001018
1019 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001020 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001021
1022 parse_fip(argv[0], NULL);
1023
1024 if (outdir[0] != '\0')
1025 if (chdir(outdir) == -1)
1026 log_err("chdir %s", outdir);
1027
dp-arm4972ec52016-05-25 16:20:20 +01001028 /* Unpack all specified images. */
dp-arm90d2f0e2016-11-14 15:54:32 +00001029 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +00001030 char file[PATH_MAX];
dp-arm715ef422016-08-30 14:18:58 +01001031 image_t *image;
1032
dp-arm90d2f0e2016-11-14 15:54:32 +00001033 if (!unpack_all && desc->action != DO_UNPACK)
dp-arm4972ec52016-05-25 16:20:20 +01001034 continue;
1035
1036 /* Build filename. */
dp-arm90d2f0e2016-11-14 15:54:32 +00001037 if (desc->action_arg == NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001038 snprintf(file, sizeof(file), "%s.bin",
dp-arm90d2f0e2016-11-14 15:54:32 +00001039 desc->cmdline_name);
dp-arm4972ec52016-05-25 16:20:20 +01001040 else
1041 snprintf(file, sizeof(file), "%s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001042 desc->action_arg);
dp-arm4972ec52016-05-25 16:20:20 +01001043
dp-arm90d2f0e2016-11-14 15:54:32 +00001044 image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +01001045 if (image == NULL) {
1046 if (!unpack_all)
dp-arm516dfcb2016-11-03 13:59:26 +00001047 log_warnx("%s does not exist in %s",
dp-arm715ef422016-08-30 14:18:58 +01001048 file, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001049 continue;
1050 }
1051
1052 if (access(file, F_OK) != 0 || fflag) {
1053 if (verbose)
1054 log_dbgx("Unpacking %s", file);
dp-arm715ef422016-08-30 14:18:58 +01001055 write_image_to_file(image, file);
dp-arm4972ec52016-05-25 16:20:20 +01001056 } else {
1057 log_warnx("File %s already exists, use --force to overwrite it",
1058 file);
1059 }
dp-arm4972ec52016-05-25 16:20:20 +01001060 }
1061
1062 free_images();
1063 return 0;
1064}
1065
1066static void unpack_usage(void)
1067{
1068 toc_entry_t *toc_entry = toc_entries;
1069
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001070 printf("fiptool unpack [opts] FIP_FILENAME\n");
1071 printf("\n");
1072 printf("Options:\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +09001073 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID to file.\n");
1074 printf(" --force\t\t\tIf the output file already exists, use --force to overwrite it.\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001075 printf(" --out path\t\t\tSet the output directory path.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001076 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001077 printf("Specific images are unpacked with the following options:\n");
1078 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1079 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
1080 toc_entry->name);
Masahiro Yamada1b900152017-02-02 16:34:14 +09001081 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001082 printf("If no options are provided, all images will be unpacked.\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001083 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001084}
1085
1086static int remove_cmd(int argc, char *argv[])
1087{
dp-arm90d2f0e2016-11-14 15:54:32 +00001088 struct option *opts = NULL;
1089 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +00001090 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +01001091 fip_toc_header_t toc_header;
dp-arm90d2f0e2016-11-14 15:54:32 +00001092 image_desc_t *desc;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001093 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +01001094 int fflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001095
1096 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +01001097 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001098
dp-arm90d2f0e2016-11-14 15:54:32 +00001099 opts = fill_common_opts(opts, &nr_opts, no_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001100 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +00001101 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +00001102 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
1103 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
1104 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +01001105
1106 while (1) {
dp-armfe92b892016-11-07 11:13:54 +00001107 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001108
dp-arm516dfcb2016-11-03 13:59:26 +00001109 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +01001110 if (c == -1)
1111 break;
1112
1113 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001114 case OPT_TOC_ENTRY: {
1115 image_desc_t *desc;
1116
1117 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +00001118 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm4972ec52016-05-25 16:20:20 +01001119 break;
dp-arm90d2f0e2016-11-14 15:54:32 +00001120 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001121 case OPT_ALIGN:
1122 align = get_image_align(optarg);
1123 break;
dp-arm516dfcb2016-11-03 13:59:26 +00001124 case 'b': {
1125 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
1126 uuid_t uuid = { 0 };
1127 image_desc_t *desc;
1128
1129 parse_blob_opt(optarg, &uuid,
1130 filename, sizeof(filename));
1131
1132 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
1133 remove_usage();
1134
1135 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +00001136 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +00001137 uuid_to_str(name, sizeof(name), &uuid);
1138 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +00001139 add_image_desc(desc);
1140 }
dp-armfb732312016-12-30 09:55:48 +00001141 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm516dfcb2016-11-03 13:59:26 +00001142 break;
1143 }
dp-arm4972ec52016-05-25 16:20:20 +01001144 case 'f':
1145 fflag = 1;
1146 break;
1147 case 'o':
1148 snprintf(outfile, sizeof(outfile), "%s", optarg);
1149 break;
1150 default:
dp-arm29f1b5c2016-09-15 09:58:50 +01001151 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001152 }
1153 }
1154 argc -= optind;
1155 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001156 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001157
1158 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001159 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001160
1161 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
1162 log_errx("File %s already exists, use --force to overwrite it",
1163 outfile);
1164
1165 if (outfile[0] == '\0')
1166 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
1167
1168 parse_fip(argv[0], &toc_header);
1169
dp-arm90d2f0e2016-11-14 15:54:32 +00001170 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm715ef422016-08-30 14:18:58 +01001171 image_t *image;
1172
dp-arm90d2f0e2016-11-14 15:54:32 +00001173 if (desc->action != DO_REMOVE)
dp-arm4972ec52016-05-25 16:20:20 +01001174 continue;
dp-arm516dfcb2016-11-03 13:59:26 +00001175
dp-arm90d2f0e2016-11-14 15:54:32 +00001176 image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +01001177 if (image != NULL) {
dp-arm4972ec52016-05-25 16:20:20 +01001178 if (verbose)
dp-arm516dfcb2016-11-03 13:59:26 +00001179 log_dbgx("Removing %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001180 desc->cmdline_name);
dp-arm715ef422016-08-30 14:18:58 +01001181 remove_image(image);
dp-arm4972ec52016-05-25 16:20:20 +01001182 } else {
dp-arm516dfcb2016-11-03 13:59:26 +00001183 log_warnx("%s does not exist in %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001184 desc->cmdline_name, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001185 }
1186 }
1187
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001188 pack_images(outfile, toc_header.flags, align);
dp-arm4972ec52016-05-25 16:20:20 +01001189 free_images();
1190 return 0;
1191}
1192
1193static void remove_usage(void)
1194{
1195 toc_entry_t *toc_entry = toc_entries;
1196
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001197 printf("fiptool remove [opts] FIP_FILENAME\n");
1198 printf("\n");
1199 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001200 printf(" --align <value>\tEach image is aligned to <value> (default: 1).\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001201 printf(" --blob uuid=...\tRemove an image with the given UUID.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +09001202 printf(" --force\t\tIf the output FIP file already exists, use --force to overwrite it.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001203 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001204 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001205 printf("Specific images are removed with the following options:\n");
1206 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1207 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
1208 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +01001209 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001210}
1211
1212static int version_cmd(int argc, char *argv[])
1213{
1214#ifdef VERSION
1215 puts(VERSION);
1216#else
1217 /* If built from fiptool directory, VERSION is not set. */
1218 puts("Unknown version");
1219#endif
1220 return 0;
1221}
1222
1223static void version_usage(void)
1224{
1225 printf("fiptool version\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001226 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001227}
1228
1229static int help_cmd(int argc, char *argv[])
1230{
1231 int i;
1232
1233 if (argc < 2)
1234 usage();
1235 argc--, argv++;
1236
1237 for (i = 0; i < NELEM(cmds); i++) {
1238 if (strcmp(cmds[i].name, argv[0]) == 0 &&
dp-arm29f1b5c2016-09-15 09:58:50 +01001239 cmds[i].usage != NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001240 cmds[i].usage();
dp-arm4972ec52016-05-25 16:20:20 +01001241 }
1242 if (i == NELEM(cmds))
1243 printf("No help for subcommand '%s'\n", argv[0]);
1244 return 0;
1245}
1246
1247static void usage(void)
1248{
Masahiro Yamada252c3362017-01-13 02:13:06 +09001249 printf("usage: fiptool [--verbose] <command> [<args>]\n");
dp-arm4972ec52016-05-25 16:20:20 +01001250 printf("Global options supported:\n");
1251 printf(" --verbose\tEnable verbose output for all commands.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001252 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001253 printf("Commands supported:\n");
1254 printf(" info\t\tList images contained in FIP.\n");
1255 printf(" create\tCreate a new FIP with the given images.\n");
1256 printf(" update\tUpdate an existing FIP with the given images.\n");
1257 printf(" unpack\tUnpack images from FIP.\n");
1258 printf(" remove\tRemove images from FIP.\n");
1259 printf(" version\tShow fiptool version.\n");
1260 printf(" help\t\tShow help for given command.\n");
1261 exit(1);
1262}
1263
1264int main(int argc, char *argv[])
1265{
1266 int i, ret = 0;
1267
dp-arm5cd10ae2016-11-07 10:45:59 +00001268 while (1) {
1269 int c, opt_index = 0;
1270 static struct option opts[] = {
1271 { "verbose", no_argument, NULL, 'v' },
1272 { NULL, no_argument, NULL, 0 }
1273 };
1274
1275 /*
1276 * Set POSIX mode so getopt stops at the first non-option
1277 * which is the subcommand.
1278 */
1279 c = getopt_long(argc, argv, "+v", opts, &opt_index);
1280 if (c == -1)
1281 break;
dp-arm4972ec52016-05-25 16:20:20 +01001282
dp-arm5cd10ae2016-11-07 10:45:59 +00001283 switch (c) {
1284 case 'v':
1285 verbose = 1;
1286 break;
1287 default:
Masahiro Yamada48a24972016-10-26 13:24:26 +09001288 usage();
dp-arm5cd10ae2016-11-07 10:45:59 +00001289 }
dp-arm4972ec52016-05-25 16:20:20 +01001290 }
dp-arm5cd10ae2016-11-07 10:45:59 +00001291 argc -= optind;
1292 argv += optind;
1293 /* Reset optind for subsequent getopt processing. */
1294 optind = 0;
1295
1296 if (argc == 0)
1297 usage();
dp-arm4972ec52016-05-25 16:20:20 +01001298
dp-arm90d2f0e2016-11-14 15:54:32 +00001299 fill_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001300 for (i = 0; i < NELEM(cmds); i++) {
1301 if (strcmp(cmds[i].name, argv[0]) == 0) {
1302 ret = cmds[i].handler(argc, argv);
1303 break;
1304 }
1305 }
1306 if (i == NELEM(cmds))
1307 usage();
dp-arm90d2f0e2016-11-14 15:54:32 +00001308 free_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001309 return ret;
1310}