blob: 6288cbfd4e04a8739be8d3736e7cd17a2bb0eb2e [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) {
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900266 if (!memcmp(&(*p)->toc_e.uuid, &image->toc_e.uuid,
267 sizeof(image->toc_e.uuid)))
Masahiro Yamadad65e7fd2017-01-27 11:57:54 +0900268 break;
269 p = &(*p)->next;
270 }
271
272 assert(*p != NULL);
273
274 image->next = (*p)->next;
275 *p = image;
276}
277
dp-arm90d2f0e2016-11-14 15:54:32 +0000278static void free_image(image_t *image)
279{
280 free(image->buffer);
281 free(image);
282}
283
dp-arm4972ec52016-05-25 16:20:20 +0100284static void remove_image(image_t *image)
285{
Masahiro Yamadac0d2d982017-01-27 13:31:40 +0900286 image_t *tmp, **p = &image_head;
dp-arm4972ec52016-05-25 16:20:20 +0100287
Masahiro Yamadac0d2d982017-01-27 13:31:40 +0900288 while (*p) {
289 if (*p == image)
290 break;
291 p = &(*p)->next;
dp-arm4972ec52016-05-25 16:20:20 +0100292 }
Masahiro Yamadac0d2d982017-01-27 13:31:40 +0900293
294 assert(*p != NULL);
295
296 tmp = *p;
297 *p = tmp->next;
298 free_image(tmp);
299
dp-arm4972ec52016-05-25 16:20:20 +0100300 nr_images--;
301}
302
303static void free_images(void)
304{
dp-arm90d2f0e2016-11-14 15:54:32 +0000305 image_t *image = image_head, *tmp;
dp-arm4972ec52016-05-25 16:20:20 +0100306
dp-arm90d2f0e2016-11-14 15:54:32 +0000307 while (image != NULL) {
308 tmp = image->next;
309 free_image(image);
310 image = tmp;
311 nr_images--;
dp-arm4972ec52016-05-25 16:20:20 +0100312 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000313 assert(nr_images == 0);
dp-arm4972ec52016-05-25 16:20:20 +0100314}
315
dp-arm90d2f0e2016-11-14 15:54:32 +0000316static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid)
dp-arm4972ec52016-05-25 16:20:20 +0100317{
dp-arm90d2f0e2016-11-14 15:54:32 +0000318 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100319
dp-arm90d2f0e2016-11-14 15:54:32 +0000320 for (desc = image_desc_head; desc != NULL; desc = desc->next)
321 if (memcmp(&desc->uuid, uuid, sizeof(uuid_t)) == 0)
322 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100323 return NULL;
324}
325
dp-arm90d2f0e2016-11-14 15:54:32 +0000326static image_desc_t *lookup_image_desc_from_opt(const char *opt)
327{
328 image_desc_t *desc;
329
330 for (desc = image_desc_head; desc != NULL; desc = desc->next)
331 if (strcmp(desc->cmdline_name, opt) == 0)
332 return desc;
333 return NULL;
334}
335
336static image_t *lookup_image_from_uuid(const uuid_t *uuid)
dp-arm715ef422016-08-30 14:18:58 +0100337{
338 image_t *image;
dp-arm715ef422016-08-30 14:18:58 +0100339
dp-arm90d2f0e2016-11-14 15:54:32 +0000340 for (image = image_head; image != NULL; image = image->next)
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900341 if (!memcmp(&image->toc_e.uuid, uuid, sizeof(*uuid)))
dp-arm715ef422016-08-30 14:18:58 +0100342 return image;
dp-arm715ef422016-08-30 14:18:58 +0100343 return NULL;
344}
345
dp-arm516dfcb2016-11-03 13:59:26 +0000346static void uuid_to_str(char *s, size_t len, const uuid_t *u)
347{
348 assert(len >= (_UUID_STR_LEN + 1));
349
350 snprintf(s, len, "%08X-%04X-%04X-%04X-%04X%04X%04X",
351 u->time_low,
352 u->time_mid,
353 u->time_hi_and_version,
354 ((uint16_t)u->clock_seq_hi_and_reserved << 8) | u->clock_seq_low,
355 ((uint16_t)u->node[0] << 8) | u->node[1],
356 ((uint16_t)u->node[2] << 8) | u->node[3],
357 ((uint16_t)u->node[4] << 8) | u->node[5]);
358}
359
360static void uuid_from_str(uuid_t *u, const char *s)
361{
362 int n;
363
364 if (s == NULL)
365 log_errx("UUID cannot be NULL");
366 if (strlen(s) != _UUID_STR_LEN)
367 log_errx("Invalid UUID: %s", s);
368
369 n = sscanf(s,
370 "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
371 &u->time_low, &u->time_mid, &u->time_hi_and_version,
372 &u->clock_seq_hi_and_reserved, &u->clock_seq_low, &u->node[0],
373 &u->node[1], &u->node[2], &u->node[3], &u->node[4], &u->node[5]);
374 /*
375 * Given the format specifier above, we expect 11 items to be scanned
376 * for a properly formatted UUID.
377 */
378 if (n != 11)
379 log_errx("Invalid UUID: %s", s);
380}
381
dp-armc1f8e772016-11-04 10:52:25 +0000382static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
dp-arm4972ec52016-05-25 16:20:20 +0100383{
384 struct stat st;
385 FILE *fp;
386 char *buf, *bufend;
387 fip_toc_header_t *toc_header;
388 fip_toc_entry_t *toc_entry;
dp-arm4972ec52016-05-25 16:20:20 +0100389 int terminated = 0;
390
391 fp = fopen(filename, "r");
392 if (fp == NULL)
393 log_err("fopen %s", filename);
394
395 if (fstat(fileno(fp), &st) == -1)
396 log_err("fstat %s", filename);
397
dp-armdb0f5e92016-11-04 10:56:25 +0000398 buf = xmalloc(st.st_size, "failed to load file into memory");
dp-arm4972ec52016-05-25 16:20:20 +0100399 if (fread(buf, 1, st.st_size, fp) != st.st_size)
400 log_errx("Failed to read %s", filename);
401 bufend = buf + st.st_size;
402 fclose(fp);
403
404 if (st.st_size < sizeof(fip_toc_header_t))
405 log_errx("FIP %s is truncated", filename);
406
407 toc_header = (fip_toc_header_t *)buf;
408 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
409
410 if (toc_header->name != TOC_HEADER_NAME)
411 log_errx("%s is not a FIP file", filename);
412
413 /* Return the ToC header if the caller wants it. */
414 if (toc_header_out != NULL)
415 *toc_header_out = *toc_header;
416
417 /* Walk through each ToC entry in the file. */
418 while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
dp-arm516dfcb2016-11-03 13:59:26 +0000419 image_t *image;
420 image_desc_t *desc;
421
dp-arm4972ec52016-05-25 16:20:20 +0100422 /* Found the ToC terminator, we are done. */
423 if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
424 terminated = 1;
425 break;
426 }
427
428 /*
429 * Build a new image out of the ToC entry and add it to the
430 * table of images.
431 */
Masahiro Yamadad224b452017-01-14 23:22:02 +0900432 image = xzalloc(sizeof(*image),
dp-armdb0f5e92016-11-04 10:56:25 +0000433 "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900434 image->toc_e = *toc_entry;
dp-armdb0f5e92016-11-04 10:56:25 +0000435 image->buffer = xmalloc(toc_entry->size,
436 "failed to allocate image buffer, is FIP file corrupted?");
dp-arm4972ec52016-05-25 16:20:20 +0100437 /* Overflow checks before memory copy. */
438 if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
439 log_errx("FIP %s is corrupted", filename);
440 if (toc_entry->size + toc_entry->offset_address > st.st_size)
441 log_errx("FIP %s is corrupted", filename);
442
443 memcpy(image->buffer, buf + toc_entry->offset_address,
444 toc_entry->size);
dp-arm4972ec52016-05-25 16:20:20 +0100445
dp-arm516dfcb2016-11-03 13:59:26 +0000446 /* If this is an unknown image, create a descriptor for it. */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900447 desc = lookup_image_desc_from_uuid(&toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000448 if (desc == NULL) {
449 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
450
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900451 uuid_to_str(name, sizeof(name), &toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000452 snprintf(filename, sizeof(filename), "%s%s",
453 name, ".bin");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900454 desc = new_image_desc(&toc_entry->uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000455 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");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900489 image->toc_e.uuid = *uuid;
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);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900493 image->toc_e.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 Yamada2fe0dad2017-01-27 03:56:58 +0900506 xfwrite(image->buffer, image->toc_e.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;
dp-arm4972ec52016-05-25 16:20:20 +0100547 fip_toc_header_t toc_header;
dp-arm4972ec52016-05-25 16:20:20 +0100548
549 if (argc != 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100550 info_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100551 argc--, argv++;
552
553 parse_fip(argv[0], &toc_header);
554
555 if (verbose) {
556 log_dbgx("toc_header[name]: 0x%llX",
557 (unsigned long long)toc_header.name);
558 log_dbgx("toc_header[serial_number]: 0x%llX",
559 (unsigned long long)toc_header.serial_number);
560 log_dbgx("toc_header[flags]: 0x%llX",
561 (unsigned long long)toc_header.flags);
562 }
563
dp-arm90d2f0e2016-11-14 15:54:32 +0000564 for (image = image_head; image != NULL; image = image->next) {
565 image_desc_t *desc;
dp-arm715ef422016-08-30 14:18:58 +0100566
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900567 desc = lookup_image_desc_from_uuid(&image->toc_e.uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000568 assert(desc != NULL);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900569 printf("%s: offset=0x%llX, size=0x%llX, cmdline=\"--%s\"",
570 desc->name,
571 (unsigned long long)image->toc_e.offset_address,
572 (unsigned long long)image->toc_e.size,
Masahiro Yamadaeee39312017-01-15 23:20:00 +0900573 desc->cmdline_name);
dp-arm12e893b2016-08-24 13:21:08 +0100574 if (verbose) {
575 unsigned char md[SHA256_DIGEST_LENGTH];
576
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900577 SHA256(image->buffer, image->toc_e.size, md);
dp-arm12e893b2016-08-24 13:21:08 +0100578 printf(", sha256=");
579 md_print(md, sizeof(md));
580 }
581 putchar('\n');
dp-arm4972ec52016-05-25 16:20:20 +0100582 }
583
584 free_images();
585 return 0;
586}
587
588static void info_usage(void)
589{
590 printf("fiptool info FIP_FILENAME\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100591 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100592}
593
dp-arm90d2f0e2016-11-14 15:54:32 +0000594static int pack_images(const char *filename, uint64_t toc_flags)
dp-arm4972ec52016-05-25 16:20:20 +0100595{
596 FILE *fp;
597 image_t *image;
598 fip_toc_header_t *toc_header;
599 fip_toc_entry_t *toc_entry;
600 char *buf;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900601 uint64_t entry_offset, buf_size, payload_size = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100602
603 buf_size = sizeof(fip_toc_header_t) +
604 sizeof(fip_toc_entry_t) * (nr_images + 1);
605 buf = calloc(1, buf_size);
606 if (buf == NULL)
607 log_err("calloc");
608
609 /* Build up header and ToC entries from the image table. */
610 toc_header = (fip_toc_header_t *)buf;
611 toc_header->name = TOC_HEADER_NAME;
612 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
613 toc_header->flags = toc_flags;
614
615 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
616
617 entry_offset = buf_size;
dp-arm90d2f0e2016-11-14 15:54:32 +0000618 for (image = image_head; image != NULL; image = image->next) {
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900619 payload_size += image->toc_e.size;
620 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
dp-arm90d2f0e2016-11-14 15:54:32 +0000643 for (image = image_head; image != NULL; image = image->next)
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900644 xfwrite(image->buffer, image->toc_e.size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100645
646 fclose(fp);
647 return 0;
648}
649
650/*
651 * This function is shared between the create and update subcommands.
652 * The difference between the two subcommands is that when the FIP file
653 * is created, the parsing of an existing FIP is skipped. This results
654 * in update_fip() creating the new FIP file from scratch because the
655 * internal image table is not populated.
656 */
657static void update_fip(void)
658{
dp-arm90d2f0e2016-11-14 15:54:32 +0000659 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100660
661 /* Add or replace images in the FIP file. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000662 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +0000663 image_t *new_image, *old_image;
664
dp-arm90d2f0e2016-11-14 15:54:32 +0000665 if (desc->action != DO_PACK)
dp-arm4972ec52016-05-25 16:20:20 +0100666 continue;
667
dp-arm90d2f0e2016-11-14 15:54:32 +0000668 new_image = read_image_from_file(&desc->uuid,
669 desc->action_arg);
670 old_image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +0100671 if (old_image != NULL) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000672 if (verbose) {
dp-arm516dfcb2016-11-03 13:59:26 +0000673 log_dbgx("Replacing %s with %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000674 desc->cmdline_name,
675 desc->action_arg);
676 }
Masahiro Yamadad65e7fd2017-01-27 11:57:54 +0900677 replace_image(new_image);
dp-arm4972ec52016-05-25 16:20:20 +0100678 } else {
679 if (verbose)
680 log_dbgx("Adding image %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000681 desc->action_arg);
dp-arm715ef422016-08-30 14:18:58 +0100682 add_image(new_image);
dp-arm4972ec52016-05-25 16:20:20 +0100683 }
dp-arm4972ec52016-05-25 16:20:20 +0100684 }
685}
686
dp-arm90d2f0e2016-11-14 15:54:32 +0000687static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
dp-arm4972ec52016-05-25 16:20:20 +0100688{
689 unsigned long long flags;
690 char *endptr;
691
692 errno = 0;
693 flags = strtoull(arg, &endptr, 16);
694 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
695 log_errx("Invalid platform ToC flags: %s", arg);
696 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
697 *toc_flags |= flags << 32;
698}
699
dp-arm516dfcb2016-11-03 13:59:26 +0000700static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
701{
702 char *p;
703
704 for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
705 if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
706 p += strlen("uuid=");
707 uuid_from_str(uuid, p);
708 } else if (strncmp(p, "file=", strlen("file=")) == 0) {
709 p += strlen("file=");
710 snprintf(filename, len, "%s", p);
711 }
712 }
713}
714
dp-arm4972ec52016-05-25 16:20:20 +0100715static int create_cmd(int argc, char *argv[])
716{
dp-arm90d2f0e2016-11-14 15:54:32 +0000717 struct option *opts = NULL;
718 size_t nr_opts = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100719 unsigned long long toc_flags = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100720
721 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100722 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100723
dp-arm90d2f0e2016-11-14 15:54:32 +0000724 opts = fill_common_opts(opts, &nr_opts, required_argument);
725 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100726 OPT_PLAT_TOC_FLAGS);
dp-arm516dfcb2016-11-03 13:59:26 +0000727 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000728 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100729
730 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000731 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100732
dp-arm516dfcb2016-11-03 13:59:26 +0000733 c = getopt_long(argc, argv, "b:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100734 if (c == -1)
735 break;
736
737 switch (c) {
738 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000739 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100740
dp-arm90d2f0e2016-11-14 15:54:32 +0000741 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000742 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100743 break;
744 }
745 case OPT_PLAT_TOC_FLAGS:
746 parse_plat_toc_flags(optarg, &toc_flags);
747 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000748 case 'b': {
749 char name[_UUID_STR_LEN + 1];
750 char filename[PATH_MAX] = { 0 };
751 uuid_t uuid = { 0 };
752 image_desc_t *desc;
753
754 parse_blob_opt(optarg, &uuid,
755 filename, sizeof(filename));
756
757 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
758 filename[0] == '\0')
759 create_usage();
760
761 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000762 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000763 uuid_to_str(name, sizeof(name), &uuid);
764 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000765 add_image_desc(desc);
766 }
dp-armfb732312016-12-30 09:55:48 +0000767 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000768 break;
769 }
dp-arm4972ec52016-05-25 16:20:20 +0100770 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100771 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100772 }
773 }
774 argc -= optind;
775 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000776 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100777
778 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100779 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100780
781 update_fip();
782
783 pack_images(argv[0], toc_flags);
784 free_images();
785 return 0;
786}
787
788static void create_usage(void)
789{
790 toc_entry_t *toc_entry = toc_entries;
791
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900792 printf("fiptool create [opts] FIP_FILENAME\n");
793 printf("\n");
794 printf("Options:\n");
dp-arm516dfcb2016-11-03 13:59:26 +0000795 printf(" --blob uuid=...,file=...\tAdd an image with the given UUID "
796 "pointed to by file.\n");
dp-arm4972ec52016-05-25 16:20:20 +0100797 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field "
798 "occupying bits 32-47 in 64-bit ToC header.\n");
799 fputc('\n', stderr);
800 printf("Specific images are packed with the following options:\n");
801 for (; toc_entry->cmdline_name != NULL; toc_entry++)
802 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
803 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100804 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100805}
806
807static int update_cmd(int argc, char *argv[])
808{
dp-arm90d2f0e2016-11-14 15:54:32 +0000809 struct option *opts = NULL;
810 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000811 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100812 fip_toc_header_t toc_header = { 0 };
813 unsigned long long toc_flags = 0;
814 int pflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100815
816 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100817 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100818
dp-arm90d2f0e2016-11-14 15:54:32 +0000819 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000820 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000821 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
822 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100823 OPT_PLAT_TOC_FLAGS);
dp-arm90d2f0e2016-11-14 15:54:32 +0000824 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100825
826 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000827 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100828
dp-arm516dfcb2016-11-03 13:59:26 +0000829 c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100830 if (c == -1)
831 break;
832
833 switch (c) {
834 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000835 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100836
dp-arm90d2f0e2016-11-14 15:54:32 +0000837 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000838 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100839 break;
840 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000841 case OPT_PLAT_TOC_FLAGS:
dp-arm4972ec52016-05-25 16:20:20 +0100842 parse_plat_toc_flags(optarg, &toc_flags);
843 pflag = 1;
844 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000845 case 'b': {
846 char name[_UUID_STR_LEN + 1];
847 char filename[PATH_MAX] = { 0 };
848 uuid_t uuid = { 0 };
849 image_desc_t *desc;
850
851 parse_blob_opt(optarg, &uuid,
852 filename, sizeof(filename));
853
854 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
855 filename[0] == '\0')
856 update_usage();
857
858 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000859 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000860 uuid_to_str(name, sizeof(name), &uuid);
861 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000862 add_image_desc(desc);
863 }
dp-armfb732312016-12-30 09:55:48 +0000864 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000865 break;
866 }
dp-arm4972ec52016-05-25 16:20:20 +0100867 case 'o':
868 snprintf(outfile, sizeof(outfile), "%s", optarg);
869 break;
870 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100871 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100872 }
873 }
874 argc -= optind;
875 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000876 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100877
878 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100879 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100880
881 if (outfile[0] == '\0')
882 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
883
Masahiro Yamadaebb6e372016-12-25 12:41:41 +0900884 if (access(argv[0], F_OK) == 0)
dp-arm4972ec52016-05-25 16:20:20 +0100885 parse_fip(argv[0], &toc_header);
886
887 if (pflag)
888 toc_header.flags &= ~(0xffffULL << 32);
889 toc_flags = (toc_header.flags |= toc_flags);
890
891 update_fip();
892
893 pack_images(outfile, toc_flags);
894 free_images();
895 return 0;
896}
897
898static void update_usage(void)
899{
900 toc_entry_t *toc_entry = toc_entries;
901
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900902 printf("fiptool update [opts] FIP_FILENAME\n");
903 printf("\n");
904 printf("Options:\n");
dp-arm516dfcb2016-11-03 13:59:26 +0000905 printf(" --blob uuid=...,file=...\tAdd or update an image "
906 "with the given UUID pointed to by file.\n");
dp-arm4972ec52016-05-25 16:20:20 +0100907 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
908 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field "
909 "occupying bits 32-47 in 64-bit ToC header.\n");
910 fputc('\n', stderr);
911 printf("Specific images are packed with the following options:\n");
912 for (; toc_entry->cmdline_name != NULL; toc_entry++)
913 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
914 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100915 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100916}
917
918static int unpack_cmd(int argc, char *argv[])
919{
dp-arm90d2f0e2016-11-14 15:54:32 +0000920 struct option *opts = NULL;
921 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000922 char outdir[PATH_MAX] = { 0 };
dp-arm90d2f0e2016-11-14 15:54:32 +0000923 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100924 int fflag = 0;
925 int unpack_all = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100926
927 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100928 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100929
dp-arm90d2f0e2016-11-14 15:54:32 +0000930 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000931 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000932 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
933 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
934 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100935
936 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000937 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100938
dp-arm516dfcb2016-11-03 13:59:26 +0000939 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100940 if (c == -1)
941 break;
942
943 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000944 case OPT_TOC_ENTRY: {
945 image_desc_t *desc;
946
947 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000948 set_image_desc_action(desc, DO_UNPACK, optarg);
dp-arm90d2f0e2016-11-14 15:54:32 +0000949 unpack_all = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100950 break;
dp-arm90d2f0e2016-11-14 15:54:32 +0000951 }
dp-arm516dfcb2016-11-03 13:59:26 +0000952 case 'b': {
953 char name[_UUID_STR_LEN + 1];
954 char filename[PATH_MAX] = { 0 };
955 uuid_t uuid = { 0 };
956 image_desc_t *desc;
957
958 parse_blob_opt(optarg, &uuid,
959 filename, sizeof(filename));
960
961 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
962 filename[0] == '\0')
963 unpack_usage();
964
965 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000966 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000967 uuid_to_str(name, sizeof(name), &uuid);
968 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000969 add_image_desc(desc);
970 }
dp-armfb732312016-12-30 09:55:48 +0000971 set_image_desc_action(desc, DO_UNPACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000972 unpack_all = 0;
973 break;
974 }
dp-arm4972ec52016-05-25 16:20:20 +0100975 case 'f':
976 fflag = 1;
977 break;
978 case 'o':
979 snprintf(outdir, sizeof(outdir), "%s", optarg);
980 break;
981 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100982 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100983 }
984 }
985 argc -= optind;
986 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000987 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100988
989 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100990 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100991
992 parse_fip(argv[0], NULL);
993
994 if (outdir[0] != '\0')
995 if (chdir(outdir) == -1)
996 log_err("chdir %s", outdir);
997
dp-arm4972ec52016-05-25 16:20:20 +0100998 /* Unpack all specified images. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000999 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +00001000 char file[PATH_MAX];
dp-arm715ef422016-08-30 14:18:58 +01001001 image_t *image;
1002
dp-arm90d2f0e2016-11-14 15:54:32 +00001003 if (!unpack_all && desc->action != DO_UNPACK)
dp-arm4972ec52016-05-25 16:20:20 +01001004 continue;
1005
1006 /* Build filename. */
dp-arm90d2f0e2016-11-14 15:54:32 +00001007 if (desc->action_arg == NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001008 snprintf(file, sizeof(file), "%s.bin",
dp-arm90d2f0e2016-11-14 15:54:32 +00001009 desc->cmdline_name);
dp-arm4972ec52016-05-25 16:20:20 +01001010 else
1011 snprintf(file, sizeof(file), "%s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001012 desc->action_arg);
dp-arm4972ec52016-05-25 16:20:20 +01001013
dp-arm90d2f0e2016-11-14 15:54:32 +00001014 image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +01001015 if (image == NULL) {
1016 if (!unpack_all)
dp-arm516dfcb2016-11-03 13:59:26 +00001017 log_warnx("%s does not exist in %s",
dp-arm715ef422016-08-30 14:18:58 +01001018 file, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001019 continue;
1020 }
1021
1022 if (access(file, F_OK) != 0 || fflag) {
1023 if (verbose)
1024 log_dbgx("Unpacking %s", file);
dp-arm715ef422016-08-30 14:18:58 +01001025 write_image_to_file(image, file);
dp-arm4972ec52016-05-25 16:20:20 +01001026 } else {
1027 log_warnx("File %s already exists, use --force to overwrite it",
1028 file);
1029 }
dp-arm4972ec52016-05-25 16:20:20 +01001030 }
1031
1032 free_images();
1033 return 0;
1034}
1035
1036static void unpack_usage(void)
1037{
1038 toc_entry_t *toc_entry = toc_entries;
1039
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001040 printf("fiptool unpack [opts] FIP_FILENAME\n");
1041 printf("\n");
1042 printf("Options:\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001043 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID "
1044 "to file.\n");
1045 printf(" --force\t\t\tIf the output file already exists, use --force to "
dp-arm4972ec52016-05-25 16:20:20 +01001046 "overwrite it.\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001047 printf(" --out path\t\t\tSet the output directory path.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001048 fputc('\n', stderr);
1049 printf("Specific images are unpacked with the following options:\n");
1050 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1051 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
1052 toc_entry->name);
1053 fputc('\n', stderr);
1054 printf("If no options are provided, all images will be unpacked.\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001055 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001056}
1057
1058static int remove_cmd(int argc, char *argv[])
1059{
dp-arm90d2f0e2016-11-14 15:54:32 +00001060 struct option *opts = NULL;
1061 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +00001062 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +01001063 fip_toc_header_t toc_header;
dp-arm90d2f0e2016-11-14 15:54:32 +00001064 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +01001065 int fflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001066
1067 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +01001068 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001069
dp-arm90d2f0e2016-11-14 15:54:32 +00001070 opts = fill_common_opts(opts, &nr_opts, no_argument);
dp-arm516dfcb2016-11-03 13:59:26 +00001071 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +00001072 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
1073 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
1074 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +01001075
1076 while (1) {
dp-armfe92b892016-11-07 11:13:54 +00001077 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001078
dp-arm516dfcb2016-11-03 13:59:26 +00001079 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +01001080 if (c == -1)
1081 break;
1082
1083 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001084 case OPT_TOC_ENTRY: {
1085 image_desc_t *desc;
1086
1087 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +00001088 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm4972ec52016-05-25 16:20:20 +01001089 break;
dp-arm90d2f0e2016-11-14 15:54:32 +00001090 }
dp-arm516dfcb2016-11-03 13:59:26 +00001091 case 'b': {
1092 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
1093 uuid_t uuid = { 0 };
1094 image_desc_t *desc;
1095
1096 parse_blob_opt(optarg, &uuid,
1097 filename, sizeof(filename));
1098
1099 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
1100 remove_usage();
1101
1102 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +00001103 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +00001104 uuid_to_str(name, sizeof(name), &uuid);
1105 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +00001106 add_image_desc(desc);
1107 }
dp-armfb732312016-12-30 09:55:48 +00001108 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm516dfcb2016-11-03 13:59:26 +00001109 break;
1110 }
dp-arm4972ec52016-05-25 16:20:20 +01001111 case 'f':
1112 fflag = 1;
1113 break;
1114 case 'o':
1115 snprintf(outfile, sizeof(outfile), "%s", optarg);
1116 break;
1117 default:
dp-arm29f1b5c2016-09-15 09:58:50 +01001118 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001119 }
1120 }
1121 argc -= optind;
1122 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001123 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001124
1125 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001126 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001127
1128 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
1129 log_errx("File %s already exists, use --force to overwrite it",
1130 outfile);
1131
1132 if (outfile[0] == '\0')
1133 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
1134
1135 parse_fip(argv[0], &toc_header);
1136
dp-arm90d2f0e2016-11-14 15:54:32 +00001137 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm715ef422016-08-30 14:18:58 +01001138 image_t *image;
1139
dp-arm90d2f0e2016-11-14 15:54:32 +00001140 if (desc->action != DO_REMOVE)
dp-arm4972ec52016-05-25 16:20:20 +01001141 continue;
dp-arm516dfcb2016-11-03 13:59:26 +00001142
dp-arm90d2f0e2016-11-14 15:54:32 +00001143 image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +01001144 if (image != NULL) {
dp-arm4972ec52016-05-25 16:20:20 +01001145 if (verbose)
dp-arm516dfcb2016-11-03 13:59:26 +00001146 log_dbgx("Removing %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001147 desc->cmdline_name);
dp-arm715ef422016-08-30 14:18:58 +01001148 remove_image(image);
dp-arm4972ec52016-05-25 16:20:20 +01001149 } else {
dp-arm516dfcb2016-11-03 13:59:26 +00001150 log_warnx("%s does not exist in %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001151 desc->cmdline_name, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001152 }
1153 }
1154
1155 pack_images(outfile, toc_header.flags);
1156 free_images();
1157 return 0;
1158}
1159
1160static void remove_usage(void)
1161{
1162 toc_entry_t *toc_entry = toc_entries;
1163
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001164 printf("fiptool remove [opts] FIP_FILENAME\n");
1165 printf("\n");
1166 printf("Options:\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001167 printf(" --blob uuid=...\tRemove an image with the given UUID.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001168 printf(" --force\t\tIf the output FIP file already exists, use --force to "
1169 "overwrite it.\n");
1170 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
1171 fputc('\n', stderr);
1172 printf("Specific images are removed with the following options:\n");
1173 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1174 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
1175 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +01001176 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001177}
1178
1179static int version_cmd(int argc, char *argv[])
1180{
1181#ifdef VERSION
1182 puts(VERSION);
1183#else
1184 /* If built from fiptool directory, VERSION is not set. */
1185 puts("Unknown version");
1186#endif
1187 return 0;
1188}
1189
1190static void version_usage(void)
1191{
1192 printf("fiptool version\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001193 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001194}
1195
1196static int help_cmd(int argc, char *argv[])
1197{
1198 int i;
1199
1200 if (argc < 2)
1201 usage();
1202 argc--, argv++;
1203
1204 for (i = 0; i < NELEM(cmds); i++) {
1205 if (strcmp(cmds[i].name, argv[0]) == 0 &&
dp-arm29f1b5c2016-09-15 09:58:50 +01001206 cmds[i].usage != NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001207 cmds[i].usage();
dp-arm4972ec52016-05-25 16:20:20 +01001208 }
1209 if (i == NELEM(cmds))
1210 printf("No help for subcommand '%s'\n", argv[0]);
1211 return 0;
1212}
1213
1214static void usage(void)
1215{
Masahiro Yamada252c3362017-01-13 02:13:06 +09001216 printf("usage: fiptool [--verbose] <command> [<args>]\n");
dp-arm4972ec52016-05-25 16:20:20 +01001217 printf("Global options supported:\n");
1218 printf(" --verbose\tEnable verbose output for all commands.\n");
1219 fputc('\n', stderr);
1220 printf("Commands supported:\n");
1221 printf(" info\t\tList images contained in FIP.\n");
1222 printf(" create\tCreate a new FIP with the given images.\n");
1223 printf(" update\tUpdate an existing FIP with the given images.\n");
1224 printf(" unpack\tUnpack images from FIP.\n");
1225 printf(" remove\tRemove images from FIP.\n");
1226 printf(" version\tShow fiptool version.\n");
1227 printf(" help\t\tShow help for given command.\n");
1228 exit(1);
1229}
1230
1231int main(int argc, char *argv[])
1232{
1233 int i, ret = 0;
1234
dp-arm5cd10ae2016-11-07 10:45:59 +00001235 while (1) {
1236 int c, opt_index = 0;
1237 static struct option opts[] = {
1238 { "verbose", no_argument, NULL, 'v' },
1239 { NULL, no_argument, NULL, 0 }
1240 };
1241
1242 /*
1243 * Set POSIX mode so getopt stops at the first non-option
1244 * which is the subcommand.
1245 */
1246 c = getopt_long(argc, argv, "+v", opts, &opt_index);
1247 if (c == -1)
1248 break;
dp-arm4972ec52016-05-25 16:20:20 +01001249
dp-arm5cd10ae2016-11-07 10:45:59 +00001250 switch (c) {
1251 case 'v':
1252 verbose = 1;
1253 break;
1254 default:
Masahiro Yamada48a24972016-10-26 13:24:26 +09001255 usage();
dp-arm5cd10ae2016-11-07 10:45:59 +00001256 }
dp-arm4972ec52016-05-25 16:20:20 +01001257 }
dp-arm5cd10ae2016-11-07 10:45:59 +00001258 argc -= optind;
1259 argv += optind;
1260 /* Reset optind for subsequent getopt processing. */
1261 optind = 0;
1262
1263 if (argc == 0)
1264 usage();
dp-arm4972ec52016-05-25 16:20:20 +01001265
dp-arm90d2f0e2016-11-14 15:54:32 +00001266 fill_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001267 for (i = 0; i < NELEM(cmds); i++) {
1268 if (strcmp(cmds[i].name, argv[0]) == 0) {
1269 ret = cmds[i].handler(argc, argv);
1270 break;
1271 }
1272 }
1273 if (i == NELEM(cmds))
1274 usage();
dp-arm90d2f0e2016-11-14 15:54:32 +00001275 free_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001276 return ret;
1277}