blob: fc0c8d6632c0bceb10dae2a4312f001f8bf3bb73 [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
dp-arm90d2f0e2016-11-14 15:54:32 +0000158static image_desc_t *new_image_desc(const uuid_t *uuid,
159 const char *name, const char *cmdline_name)
dp-arm4972ec52016-05-25 16:20:20 +0100160{
dp-arm90d2f0e2016-11-14 15:54:32 +0000161 image_desc_t *desc;
162
163 desc = xmalloc(sizeof(*desc),
164 "failed to allocate memory for image descriptor");
165 memcpy(&desc->uuid, uuid, sizeof(uuid_t));
166 desc->name = xstrdup(name,
167 "failed to allocate memory for image name");
168 desc->cmdline_name = xstrdup(cmdline_name,
169 "failed to allocate memory for image command line name");
170 desc->action = DO_UNSPEC;
171 desc->action_arg = NULL;
172 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100173}
174
dp-armfb732312016-12-30 09:55:48 +0000175static void set_image_desc_action(image_desc_t *desc, int action,
176 const char *arg)
177{
178 assert(desc != NULL);
179
180 if (desc->action_arg != DO_UNSPEC)
181 free(desc->action_arg);
182 desc->action = action;
183 desc->action_arg = NULL;
184 if (arg != NULL)
185 desc->action_arg = xstrdup(arg,
186 "failed to allocate memory for argument");
187}
188
dp-arm90d2f0e2016-11-14 15:54:32 +0000189static void free_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100190{
dp-arm90d2f0e2016-11-14 15:54:32 +0000191 free(desc->name);
192 free(desc->cmdline_name);
193 free(desc->action_arg);
194 free(desc);
dp-arm4972ec52016-05-25 16:20:20 +0100195}
196
dp-arm90d2f0e2016-11-14 15:54:32 +0000197static void add_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100198{
dp-arm90d2f0e2016-11-14 15:54:32 +0000199 assert(lookup_image_desc_from_uuid(&desc->uuid) == NULL);
200 desc->next = image_desc_head;
201 image_desc_head = desc;
202 nr_image_descs++;
203}
dp-arm4972ec52016-05-25 16:20:20 +0100204
dp-arm90d2f0e2016-11-14 15:54:32 +0000205static void free_image_descs(void)
206{
207 image_desc_t *desc = image_desc_head, *tmp;
208
209 while (desc != NULL) {
210 tmp = desc->next;
211 free_image_desc(desc);
212 desc = tmp;
213 nr_image_descs--;
dp-arm4972ec52016-05-25 16:20:20 +0100214 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000215 assert(nr_image_descs == 0);
dp-arm4972ec52016-05-25 16:20:20 +0100216}
217
dp-arm90d2f0e2016-11-14 15:54:32 +0000218static void fill_image_descs(void)
219{
220 toc_entry_t *toc_entry;
221
222 for (toc_entry = toc_entries;
223 toc_entry->cmdline_name != NULL;
224 toc_entry++) {
225 image_desc_t *desc;
226
227 desc = new_image_desc(&toc_entry->uuid,
228 toc_entry->name,
229 toc_entry->cmdline_name);
230 add_image_desc(desc);
231 }
232}
233
234static void add_image(image_t *image)
235{
236 assert(lookup_image_from_uuid(&image->uuid) == NULL);
237 image->next = image_head;
238 image_head = image;
239 nr_images++;
240}
241
242static void free_image(image_t *image)
243{
244 free(image->buffer);
245 free(image);
246}
247
dp-arm4972ec52016-05-25 16:20:20 +0100248static void remove_image(image_t *image)
249{
dp-arm90d2f0e2016-11-14 15:54:32 +0000250 image_t *tmp = image_head, *prev;
dp-arm4972ec52016-05-25 16:20:20 +0100251
dp-arm90d2f0e2016-11-14 15:54:32 +0000252 if (tmp == image) {
253 image_head = tmp->next;
254 free_image(tmp);
255 } else {
256 while (tmp != NULL && tmp != image) {
257 prev = tmp;
258 tmp = tmp->next;
dp-arm4972ec52016-05-25 16:20:20 +0100259 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000260 assert(tmp != NULL);
261 prev->next = tmp->next;
262 free_image(tmp);
dp-arm4972ec52016-05-25 16:20:20 +0100263 }
dp-arm4972ec52016-05-25 16:20:20 +0100264 nr_images--;
265}
266
267static void free_images(void)
268{
dp-arm90d2f0e2016-11-14 15:54:32 +0000269 image_t *image = image_head, *tmp;
dp-arm4972ec52016-05-25 16:20:20 +0100270
dp-arm90d2f0e2016-11-14 15:54:32 +0000271 while (image != NULL) {
272 tmp = image->next;
273 free_image(image);
274 image = tmp;
275 nr_images--;
dp-arm4972ec52016-05-25 16:20:20 +0100276 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000277 assert(nr_images == 0);
dp-arm4972ec52016-05-25 16:20:20 +0100278}
279
dp-arm90d2f0e2016-11-14 15:54:32 +0000280static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid)
dp-arm4972ec52016-05-25 16:20:20 +0100281{
dp-arm90d2f0e2016-11-14 15:54:32 +0000282 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100283
dp-arm90d2f0e2016-11-14 15:54:32 +0000284 for (desc = image_desc_head; desc != NULL; desc = desc->next)
285 if (memcmp(&desc->uuid, uuid, sizeof(uuid_t)) == 0)
286 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100287 return NULL;
288}
289
dp-arm90d2f0e2016-11-14 15:54:32 +0000290static image_desc_t *lookup_image_desc_from_opt(const char *opt)
291{
292 image_desc_t *desc;
293
294 for (desc = image_desc_head; desc != NULL; desc = desc->next)
295 if (strcmp(desc->cmdline_name, opt) == 0)
296 return desc;
297 return NULL;
298}
299
300static image_t *lookup_image_from_uuid(const uuid_t *uuid)
dp-arm715ef422016-08-30 14:18:58 +0100301{
302 image_t *image;
dp-arm715ef422016-08-30 14:18:58 +0100303
dp-arm90d2f0e2016-11-14 15:54:32 +0000304 for (image = image_head; image != NULL; image = image->next)
dp-arm715ef422016-08-30 14:18:58 +0100305 if (memcmp(&image->uuid, uuid, sizeof(uuid_t)) == 0)
306 return image;
dp-arm715ef422016-08-30 14:18:58 +0100307 return NULL;
308}
309
dp-arm516dfcb2016-11-03 13:59:26 +0000310static void uuid_to_str(char *s, size_t len, const uuid_t *u)
311{
312 assert(len >= (_UUID_STR_LEN + 1));
313
314 snprintf(s, len, "%08X-%04X-%04X-%04X-%04X%04X%04X",
315 u->time_low,
316 u->time_mid,
317 u->time_hi_and_version,
318 ((uint16_t)u->clock_seq_hi_and_reserved << 8) | u->clock_seq_low,
319 ((uint16_t)u->node[0] << 8) | u->node[1],
320 ((uint16_t)u->node[2] << 8) | u->node[3],
321 ((uint16_t)u->node[4] << 8) | u->node[5]);
322}
323
324static void uuid_from_str(uuid_t *u, const char *s)
325{
326 int n;
327
328 if (s == NULL)
329 log_errx("UUID cannot be NULL");
330 if (strlen(s) != _UUID_STR_LEN)
331 log_errx("Invalid UUID: %s", s);
332
333 n = sscanf(s,
334 "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
335 &u->time_low, &u->time_mid, &u->time_hi_and_version,
336 &u->clock_seq_hi_and_reserved, &u->clock_seq_low, &u->node[0],
337 &u->node[1], &u->node[2], &u->node[3], &u->node[4], &u->node[5]);
338 /*
339 * Given the format specifier above, we expect 11 items to be scanned
340 * for a properly formatted UUID.
341 */
342 if (n != 11)
343 log_errx("Invalid UUID: %s", s);
344}
345
dp-armc1f8e772016-11-04 10:52:25 +0000346static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
dp-arm4972ec52016-05-25 16:20:20 +0100347{
348 struct stat st;
349 FILE *fp;
350 char *buf, *bufend;
351 fip_toc_header_t *toc_header;
352 fip_toc_entry_t *toc_entry;
dp-arm4972ec52016-05-25 16:20:20 +0100353 int terminated = 0;
354
355 fp = fopen(filename, "r");
356 if (fp == NULL)
357 log_err("fopen %s", filename);
358
359 if (fstat(fileno(fp), &st) == -1)
360 log_err("fstat %s", filename);
361
dp-armdb0f5e92016-11-04 10:56:25 +0000362 buf = xmalloc(st.st_size, "failed to load file into memory");
dp-arm4972ec52016-05-25 16:20:20 +0100363 if (fread(buf, 1, st.st_size, fp) != st.st_size)
364 log_errx("Failed to read %s", filename);
365 bufend = buf + st.st_size;
366 fclose(fp);
367
368 if (st.st_size < sizeof(fip_toc_header_t))
369 log_errx("FIP %s is truncated", filename);
370
371 toc_header = (fip_toc_header_t *)buf;
372 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
373
374 if (toc_header->name != TOC_HEADER_NAME)
375 log_errx("%s is not a FIP file", filename);
376
377 /* Return the ToC header if the caller wants it. */
378 if (toc_header_out != NULL)
379 *toc_header_out = *toc_header;
380
381 /* Walk through each ToC entry in the file. */
382 while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
dp-arm516dfcb2016-11-03 13:59:26 +0000383 image_t *image;
384 image_desc_t *desc;
385
dp-arm4972ec52016-05-25 16:20:20 +0100386 /* Found the ToC terminator, we are done. */
387 if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
388 terminated = 1;
389 break;
390 }
391
392 /*
393 * Build a new image out of the ToC entry and add it to the
394 * table of images.
395 */
dp-armdb0f5e92016-11-04 10:56:25 +0000396 image = xmalloc(sizeof(*image),
397 "failed to allocate memory for image");
dp-arm4972ec52016-05-25 16:20:20 +0100398 memcpy(&image->uuid, &toc_entry->uuid, sizeof(uuid_t));
dp-armdb0f5e92016-11-04 10:56:25 +0000399 image->buffer = xmalloc(toc_entry->size,
400 "failed to allocate image buffer, is FIP file corrupted?");
dp-arm4972ec52016-05-25 16:20:20 +0100401 /* Overflow checks before memory copy. */
402 if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
403 log_errx("FIP %s is corrupted", filename);
404 if (toc_entry->size + toc_entry->offset_address > st.st_size)
405 log_errx("FIP %s is corrupted", filename);
406
407 memcpy(image->buffer, buf + toc_entry->offset_address,
408 toc_entry->size);
409 image->size = toc_entry->size;
410
dp-arm516dfcb2016-11-03 13:59:26 +0000411 /* If this is an unknown image, create a descriptor for it. */
412 desc = lookup_image_desc_from_uuid(&image->uuid);
413 if (desc == NULL) {
414 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
415
416 uuid_to_str(name, sizeof(name), &image->uuid);
417 snprintf(filename, sizeof(filename), "%s%s",
418 name, ".bin");
419 desc = new_image_desc(&image->uuid, name, "blob");
420 desc->action = DO_UNPACK;
421 desc->action_arg = xstrdup(filename,
422 "failed to allocate memory for blob filename");
423 add_image_desc(desc);
424 }
425
dp-arm4972ec52016-05-25 16:20:20 +0100426 add_image(image);
427
428 toc_entry++;
429 }
430
431 if (terminated == 0)
432 log_errx("FIP %s does not have a ToC terminator entry",
433 filename);
434 free(buf);
435 return 0;
436}
437
dp-armc1f8e772016-11-04 10:52:25 +0000438static image_t *read_image_from_file(const uuid_t *uuid, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100439{
440 struct stat st;
441 image_t *image;
442 FILE *fp;
443
dp-arm715ef422016-08-30 14:18:58 +0100444 assert(uuid != NULL);
445
dp-arm4972ec52016-05-25 16:20:20 +0100446 fp = fopen(filename, "r");
447 if (fp == NULL)
448 log_err("fopen %s", filename);
449
450 if (fstat(fileno(fp), &st) == -1)
451 log_errx("fstat %s", filename);
452
dp-armdb0f5e92016-11-04 10:56:25 +0000453 image = xmalloc(sizeof(*image), "failed to allocate memory for image");
dp-arm715ef422016-08-30 14:18:58 +0100454 memcpy(&image->uuid, uuid, sizeof(uuid_t));
dp-armdb0f5e92016-11-04 10:56:25 +0000455 image->buffer = xmalloc(st.st_size, "failed to allocate image buffer");
dp-arm4972ec52016-05-25 16:20:20 +0100456 if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
457 log_errx("Failed to read %s", filename);
458 image->size = st.st_size;
dp-arm4972ec52016-05-25 16:20:20 +0100459
460 fclose(fp);
461 return image;
462}
463
dp-armc1f8e772016-11-04 10:52:25 +0000464static int write_image_to_file(const image_t *image, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100465{
466 FILE *fp;
467
468 fp = fopen(filename, "w");
469 if (fp == NULL)
470 log_err("fopen");
471 if (fwrite(image->buffer, 1, image->size, fp) != image->size)
472 log_errx("Failed to write %s", filename);
473 fclose(fp);
474 return 0;
475}
476
dp-arm90d2f0e2016-11-14 15:54:32 +0000477static struct option *add_opt(struct option *opts, size_t *nr_opts,
478 const char *name, int has_arg, int val)
dp-arm4972ec52016-05-25 16:20:20 +0100479{
dp-arm90d2f0e2016-11-14 15:54:32 +0000480 opts = realloc(opts, (*nr_opts + 1) * sizeof(*opts));
481 if (opts == NULL)
482 log_err("realloc");
483 opts[*nr_opts].name = name;
484 opts[*nr_opts].has_arg = has_arg;
485 opts[*nr_opts].flag = NULL;
486 opts[*nr_opts].val = val;
487 ++*nr_opts;
488 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100489}
490
dp-arm90d2f0e2016-11-14 15:54:32 +0000491static struct option *fill_common_opts(struct option *opts, size_t *nr_opts,
492 int has_arg)
dp-arm4972ec52016-05-25 16:20:20 +0100493{
dp-arm90d2f0e2016-11-14 15:54:32 +0000494 image_desc_t *desc;
495
496 for (desc = image_desc_head; desc != NULL; desc = desc->next)
497 opts = add_opt(opts, nr_opts, desc->cmdline_name, has_arg,
498 OPT_TOC_ENTRY);
499 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100500}
501
dp-arm90d2f0e2016-11-14 15:54:32 +0000502static void md_print(const unsigned char *md, size_t len)
dp-arm12e893b2016-08-24 13:21:08 +0100503{
504 size_t i;
505
506 for (i = 0; i < len; i++)
507 printf("%02x", md[i]);
508}
509
dp-arm4972ec52016-05-25 16:20:20 +0100510static int info_cmd(int argc, char *argv[])
511{
512 image_t *image;
513 uint64_t image_offset;
dp-arm516dfcb2016-11-03 13:59:26 +0000514 uint64_t image_size;
dp-arm4972ec52016-05-25 16:20:20 +0100515 fip_toc_header_t toc_header;
dp-arm4972ec52016-05-25 16:20:20 +0100516
517 if (argc != 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100518 info_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100519 argc--, argv++;
520
521 parse_fip(argv[0], &toc_header);
522
523 if (verbose) {
524 log_dbgx("toc_header[name]: 0x%llX",
525 (unsigned long long)toc_header.name);
526 log_dbgx("toc_header[serial_number]: 0x%llX",
527 (unsigned long long)toc_header.serial_number);
528 log_dbgx("toc_header[flags]: 0x%llX",
529 (unsigned long long)toc_header.flags);
530 }
531
532 image_offset = sizeof(fip_toc_header_t) +
533 (sizeof(fip_toc_entry_t) * (nr_images + 1));
534
dp-arm90d2f0e2016-11-14 15:54:32 +0000535 for (image = image_head; image != NULL; image = image->next) {
536 image_desc_t *desc;
dp-arm715ef422016-08-30 14:18:58 +0100537
dp-arm90d2f0e2016-11-14 15:54:32 +0000538 desc = lookup_image_desc_from_uuid(&image->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000539 assert(desc != NULL);
540 printf("%s: ", desc->name);
dp-arm4972ec52016-05-25 16:20:20 +0100541 image_size = image->size;
542 printf("offset=0x%llX, size=0x%llX",
543 (unsigned long long)image_offset,
544 (unsigned long long)image_size);
dp-arm90d2f0e2016-11-14 15:54:32 +0000545 if (desc != NULL)
dp-arm12e893b2016-08-24 13:21:08 +0100546 printf(", cmdline=\"--%s\"",
dp-arm90d2f0e2016-11-14 15:54:32 +0000547 desc->cmdline_name);
dp-arm12e893b2016-08-24 13:21:08 +0100548 if (verbose) {
549 unsigned char md[SHA256_DIGEST_LENGTH];
550
551 SHA256(image->buffer, image_size, md);
552 printf(", sha256=");
553 md_print(md, sizeof(md));
554 }
555 putchar('\n');
dp-arm4972ec52016-05-25 16:20:20 +0100556 image_offset += image_size;
557 }
558
559 free_images();
560 return 0;
561}
562
563static void info_usage(void)
564{
565 printf("fiptool info FIP_FILENAME\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100566 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100567}
568
dp-arm90d2f0e2016-11-14 15:54:32 +0000569static int pack_images(const char *filename, uint64_t toc_flags)
dp-arm4972ec52016-05-25 16:20:20 +0100570{
571 FILE *fp;
572 image_t *image;
573 fip_toc_header_t *toc_header;
574 fip_toc_entry_t *toc_entry;
575 char *buf;
576 uint64_t entry_offset, buf_size, payload_size;
dp-arm4972ec52016-05-25 16:20:20 +0100577
578 /* Calculate total payload size and allocate scratch buffer. */
579 payload_size = 0;
dp-arm90d2f0e2016-11-14 15:54:32 +0000580 for (image = image_head; image != NULL; image = image->next)
581 payload_size += image->size;
dp-arm4972ec52016-05-25 16:20:20 +0100582
583 buf_size = sizeof(fip_toc_header_t) +
584 sizeof(fip_toc_entry_t) * (nr_images + 1);
585 buf = calloc(1, buf_size);
586 if (buf == NULL)
587 log_err("calloc");
588
589 /* Build up header and ToC entries from the image table. */
590 toc_header = (fip_toc_header_t *)buf;
591 toc_header->name = TOC_HEADER_NAME;
592 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
593 toc_header->flags = toc_flags;
594
595 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
596
597 entry_offset = buf_size;
dp-arm90d2f0e2016-11-14 15:54:32 +0000598 for (image = image_head; image != NULL; image = image->next) {
dp-arm4972ec52016-05-25 16:20:20 +0100599 memcpy(&toc_entry->uuid, &image->uuid, sizeof(uuid_t));
600 toc_entry->offset_address = entry_offset;
601 toc_entry->size = image->size;
602 toc_entry->flags = 0;
603 entry_offset += toc_entry->size;
604 toc_entry++;
605 }
606
607 /* Append a null uuid entry to mark the end of ToC entries. */
608 memcpy(&toc_entry->uuid, &uuid_null, sizeof(uuid_t));
609 toc_entry->offset_address = entry_offset;
610 toc_entry->size = 0;
611 toc_entry->flags = 0;
612
613 /* Generate the FIP file. */
614 fp = fopen(filename, "w");
615 if (fp == NULL)
616 log_err("fopen %s", filename);
617
618 if (verbose)
619 log_dbgx("Metadata size: %zu bytes", buf_size);
620
621 if (fwrite(buf, 1, buf_size, fp) != buf_size)
622 log_errx("Failed to write image to %s", filename);
623 free(buf);
624
625 if (verbose)
626 log_dbgx("Payload size: %zu bytes", payload_size);
627
dp-arm90d2f0e2016-11-14 15:54:32 +0000628 for (image = image_head; image != NULL; image = image->next)
dp-arm4972ec52016-05-25 16:20:20 +0100629 if (fwrite(image->buffer, 1, image->size, fp) != image->size)
630 log_errx("Failed to write image to %s", filename);
dp-arm4972ec52016-05-25 16:20:20 +0100631
632 fclose(fp);
633 return 0;
634}
635
636/*
637 * This function is shared between the create and update subcommands.
638 * The difference between the two subcommands is that when the FIP file
639 * is created, the parsing of an existing FIP is skipped. This results
640 * in update_fip() creating the new FIP file from scratch because the
641 * internal image table is not populated.
642 */
643static void update_fip(void)
644{
dp-arm90d2f0e2016-11-14 15:54:32 +0000645 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100646
647 /* Add or replace images in the FIP file. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000648 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +0000649 image_t *new_image, *old_image;
650
dp-arm90d2f0e2016-11-14 15:54:32 +0000651 if (desc->action != DO_PACK)
dp-arm4972ec52016-05-25 16:20:20 +0100652 continue;
653
dp-arm90d2f0e2016-11-14 15:54:32 +0000654 new_image = read_image_from_file(&desc->uuid,
655 desc->action_arg);
656 old_image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +0100657 if (old_image != NULL) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000658 if (verbose) {
dp-arm516dfcb2016-11-03 13:59:26 +0000659 log_dbgx("Replacing %s with %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000660 desc->cmdline_name,
661 desc->action_arg);
662 }
663 remove_image(old_image);
664 add_image(new_image);
dp-arm4972ec52016-05-25 16:20:20 +0100665 } else {
666 if (verbose)
667 log_dbgx("Adding image %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000668 desc->action_arg);
dp-arm715ef422016-08-30 14:18:58 +0100669 add_image(new_image);
dp-arm4972ec52016-05-25 16:20:20 +0100670 }
dp-arm4972ec52016-05-25 16:20:20 +0100671 }
672}
673
dp-arm90d2f0e2016-11-14 15:54:32 +0000674static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
dp-arm4972ec52016-05-25 16:20:20 +0100675{
676 unsigned long long flags;
677 char *endptr;
678
679 errno = 0;
680 flags = strtoull(arg, &endptr, 16);
681 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
682 log_errx("Invalid platform ToC flags: %s", arg);
683 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
684 *toc_flags |= flags << 32;
685}
686
dp-arm516dfcb2016-11-03 13:59:26 +0000687static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
688{
689 char *p;
690
691 for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
692 if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
693 p += strlen("uuid=");
694 uuid_from_str(uuid, p);
695 } else if (strncmp(p, "file=", strlen("file=")) == 0) {
696 p += strlen("file=");
697 snprintf(filename, len, "%s", p);
698 }
699 }
700}
701
dp-arm4972ec52016-05-25 16:20:20 +0100702static int create_cmd(int argc, char *argv[])
703{
dp-arm90d2f0e2016-11-14 15:54:32 +0000704 struct option *opts = NULL;
705 size_t nr_opts = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100706 unsigned long long toc_flags = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100707
708 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100709 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100710
dp-arm90d2f0e2016-11-14 15:54:32 +0000711 opts = fill_common_opts(opts, &nr_opts, required_argument);
712 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100713 OPT_PLAT_TOC_FLAGS);
dp-arm516dfcb2016-11-03 13:59:26 +0000714 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000715 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100716
717 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000718 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100719
dp-arm516dfcb2016-11-03 13:59:26 +0000720 c = getopt_long(argc, argv, "b:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100721 if (c == -1)
722 break;
723
724 switch (c) {
725 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000726 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100727
dp-arm90d2f0e2016-11-14 15:54:32 +0000728 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000729 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100730 break;
731 }
732 case OPT_PLAT_TOC_FLAGS:
733 parse_plat_toc_flags(optarg, &toc_flags);
734 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000735 case 'b': {
736 char name[_UUID_STR_LEN + 1];
737 char filename[PATH_MAX] = { 0 };
738 uuid_t uuid = { 0 };
739 image_desc_t *desc;
740
741 parse_blob_opt(optarg, &uuid,
742 filename, sizeof(filename));
743
744 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
745 filename[0] == '\0')
746 create_usage();
747
748 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000749 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000750 uuid_to_str(name, sizeof(name), &uuid);
751 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000752 add_image_desc(desc);
753 }
dp-armfb732312016-12-30 09:55:48 +0000754 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000755 break;
756 }
dp-arm4972ec52016-05-25 16:20:20 +0100757 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100758 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100759 }
760 }
761 argc -= optind;
762 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000763 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100764
765 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100766 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100767
768 update_fip();
769
770 pack_images(argv[0], toc_flags);
771 free_images();
772 return 0;
773}
774
775static void create_usage(void)
776{
777 toc_entry_t *toc_entry = toc_entries;
778
dp-arm516dfcb2016-11-03 13:59:26 +0000779 printf("fiptool create [--blob uuid=...,file=...] "
780 "[--plat-toc-flags <value>] [opts] FIP_FILENAME\n");
781 printf(" --blob uuid=...,file=...\tAdd an image with the given UUID "
782 "pointed to by file.\n");
dp-arm4972ec52016-05-25 16:20:20 +0100783 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field "
784 "occupying bits 32-47 in 64-bit ToC header.\n");
785 fputc('\n', stderr);
786 printf("Specific images are packed with the following options:\n");
787 for (; toc_entry->cmdline_name != NULL; toc_entry++)
788 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
789 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100790 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100791}
792
793static int update_cmd(int argc, char *argv[])
794{
dp-arm90d2f0e2016-11-14 15:54:32 +0000795 struct option *opts = NULL;
796 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000797 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100798 fip_toc_header_t toc_header = { 0 };
799 unsigned long long toc_flags = 0;
800 int pflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100801
802 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100803 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100804
dp-arm90d2f0e2016-11-14 15:54:32 +0000805 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000806 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000807 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
808 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100809 OPT_PLAT_TOC_FLAGS);
dp-arm90d2f0e2016-11-14 15:54:32 +0000810 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100811
812 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000813 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100814
dp-arm516dfcb2016-11-03 13:59:26 +0000815 c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100816 if (c == -1)
817 break;
818
819 switch (c) {
820 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000821 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100822
dp-arm90d2f0e2016-11-14 15:54:32 +0000823 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000824 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100825 break;
826 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000827 case OPT_PLAT_TOC_FLAGS:
dp-arm4972ec52016-05-25 16:20:20 +0100828 parse_plat_toc_flags(optarg, &toc_flags);
829 pflag = 1;
830 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000831 case 'b': {
832 char name[_UUID_STR_LEN + 1];
833 char filename[PATH_MAX] = { 0 };
834 uuid_t uuid = { 0 };
835 image_desc_t *desc;
836
837 parse_blob_opt(optarg, &uuid,
838 filename, sizeof(filename));
839
840 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
841 filename[0] == '\0')
842 update_usage();
843
844 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000845 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000846 uuid_to_str(name, sizeof(name), &uuid);
847 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000848 add_image_desc(desc);
849 }
dp-armfb732312016-12-30 09:55:48 +0000850 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000851 break;
852 }
dp-arm4972ec52016-05-25 16:20:20 +0100853 case 'o':
854 snprintf(outfile, sizeof(outfile), "%s", optarg);
855 break;
856 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100857 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100858 }
859 }
860 argc -= optind;
861 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000862 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100863
864 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100865 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100866
867 if (outfile[0] == '\0')
868 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
869
870 if (access(outfile, F_OK) == 0)
871 parse_fip(argv[0], &toc_header);
872
873 if (pflag)
874 toc_header.flags &= ~(0xffffULL << 32);
875 toc_flags = (toc_header.flags |= toc_flags);
876
877 update_fip();
878
879 pack_images(outfile, toc_flags);
880 free_images();
881 return 0;
882}
883
884static void update_usage(void)
885{
886 toc_entry_t *toc_entry = toc_entries;
887
dp-arm516dfcb2016-11-03 13:59:26 +0000888 printf("fiptool update [--blob uuid=...,file=...] [--out FIP_FILENAME] "
dp-arm4972ec52016-05-25 16:20:20 +0100889 "[--plat-toc-flags <value>] [opts] FIP_FILENAME\n");
dp-arm516dfcb2016-11-03 13:59:26 +0000890 printf(" --blob uuid=...,file=...\tAdd or update an image "
891 "with the given UUID pointed to by file.\n");
dp-arm4972ec52016-05-25 16:20:20 +0100892 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
893 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field "
894 "occupying bits 32-47 in 64-bit ToC header.\n");
895 fputc('\n', stderr);
896 printf("Specific images are packed with the following options:\n");
897 for (; toc_entry->cmdline_name != NULL; toc_entry++)
898 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
899 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100900 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100901}
902
903static int unpack_cmd(int argc, char *argv[])
904{
dp-arm90d2f0e2016-11-14 15:54:32 +0000905 struct option *opts = NULL;
906 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000907 char outdir[PATH_MAX] = { 0 };
dp-arm90d2f0e2016-11-14 15:54:32 +0000908 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100909 int fflag = 0;
910 int unpack_all = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100911
912 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100913 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100914
dp-arm90d2f0e2016-11-14 15:54:32 +0000915 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000916 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000917 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
918 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
919 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100920
921 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000922 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100923
dp-arm516dfcb2016-11-03 13:59:26 +0000924 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100925 if (c == -1)
926 break;
927
928 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000929 case OPT_TOC_ENTRY: {
930 image_desc_t *desc;
931
932 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000933 set_image_desc_action(desc, DO_UNPACK, optarg);
dp-arm90d2f0e2016-11-14 15:54:32 +0000934 unpack_all = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100935 break;
dp-arm90d2f0e2016-11-14 15:54:32 +0000936 }
dp-arm516dfcb2016-11-03 13:59:26 +0000937 case 'b': {
938 char name[_UUID_STR_LEN + 1];
939 char filename[PATH_MAX] = { 0 };
940 uuid_t uuid = { 0 };
941 image_desc_t *desc;
942
943 parse_blob_opt(optarg, &uuid,
944 filename, sizeof(filename));
945
946 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
947 filename[0] == '\0')
948 unpack_usage();
949
950 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000951 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000952 uuid_to_str(name, sizeof(name), &uuid);
953 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000954 add_image_desc(desc);
955 }
dp-armfb732312016-12-30 09:55:48 +0000956 set_image_desc_action(desc, DO_UNPACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000957 unpack_all = 0;
958 break;
959 }
dp-arm4972ec52016-05-25 16:20:20 +0100960 case 'f':
961 fflag = 1;
962 break;
963 case 'o':
964 snprintf(outdir, sizeof(outdir), "%s", optarg);
965 break;
966 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100967 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100968 }
969 }
970 argc -= optind;
971 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000972 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100973
974 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100975 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100976
977 parse_fip(argv[0], NULL);
978
979 if (outdir[0] != '\0')
980 if (chdir(outdir) == -1)
981 log_err("chdir %s", outdir);
982
dp-arm4972ec52016-05-25 16:20:20 +0100983 /* Unpack all specified images. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000984 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +0000985 char file[PATH_MAX];
dp-arm715ef422016-08-30 14:18:58 +0100986 image_t *image;
987
dp-arm90d2f0e2016-11-14 15:54:32 +0000988 if (!unpack_all && desc->action != DO_UNPACK)
dp-arm4972ec52016-05-25 16:20:20 +0100989 continue;
990
991 /* Build filename. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000992 if (desc->action_arg == NULL)
dp-arm4972ec52016-05-25 16:20:20 +0100993 snprintf(file, sizeof(file), "%s.bin",
dp-arm90d2f0e2016-11-14 15:54:32 +0000994 desc->cmdline_name);
dp-arm4972ec52016-05-25 16:20:20 +0100995 else
996 snprintf(file, sizeof(file), "%s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000997 desc->action_arg);
dp-arm4972ec52016-05-25 16:20:20 +0100998
dp-arm90d2f0e2016-11-14 15:54:32 +0000999 image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +01001000 if (image == NULL) {
1001 if (!unpack_all)
dp-arm516dfcb2016-11-03 13:59:26 +00001002 log_warnx("%s does not exist in %s",
dp-arm715ef422016-08-30 14:18:58 +01001003 file, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001004 continue;
1005 }
1006
1007 if (access(file, F_OK) != 0 || fflag) {
1008 if (verbose)
1009 log_dbgx("Unpacking %s", file);
dp-arm715ef422016-08-30 14:18:58 +01001010 write_image_to_file(image, file);
dp-arm4972ec52016-05-25 16:20:20 +01001011 } else {
1012 log_warnx("File %s already exists, use --force to overwrite it",
1013 file);
1014 }
dp-arm4972ec52016-05-25 16:20:20 +01001015 }
1016
1017 free_images();
1018 return 0;
1019}
1020
1021static void unpack_usage(void)
1022{
1023 toc_entry_t *toc_entry = toc_entries;
1024
dp-arm516dfcb2016-11-03 13:59:26 +00001025 printf("fiptool unpack [--blob uuid=...,file=...] [--force] "
1026 "[--out <path>] [opts] FIP_FILENAME\n");
1027 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID "
1028 "to file.\n");
1029 printf(" --force\t\t\tIf the output file already exists, use --force to "
dp-arm4972ec52016-05-25 16:20:20 +01001030 "overwrite it.\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001031 printf(" --out path\t\t\tSet the output directory path.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001032 fputc('\n', stderr);
1033 printf("Specific images are unpacked with the following options:\n");
1034 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1035 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
1036 toc_entry->name);
1037 fputc('\n', stderr);
1038 printf("If no options are provided, all images will be unpacked.\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001039 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001040}
1041
1042static int remove_cmd(int argc, char *argv[])
1043{
dp-arm90d2f0e2016-11-14 15:54:32 +00001044 struct option *opts = NULL;
1045 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +00001046 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +01001047 fip_toc_header_t toc_header;
dp-arm90d2f0e2016-11-14 15:54:32 +00001048 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +01001049 int fflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001050
1051 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +01001052 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001053
dp-arm90d2f0e2016-11-14 15:54:32 +00001054 opts = fill_common_opts(opts, &nr_opts, no_argument);
dp-arm516dfcb2016-11-03 13:59:26 +00001055 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +00001056 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
1057 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
1058 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +01001059
1060 while (1) {
dp-armfe92b892016-11-07 11:13:54 +00001061 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001062
dp-arm516dfcb2016-11-03 13:59:26 +00001063 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +01001064 if (c == -1)
1065 break;
1066
1067 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001068 case OPT_TOC_ENTRY: {
1069 image_desc_t *desc;
1070
1071 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +00001072 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm4972ec52016-05-25 16:20:20 +01001073 break;
dp-arm90d2f0e2016-11-14 15:54:32 +00001074 }
dp-arm516dfcb2016-11-03 13:59:26 +00001075 case 'b': {
1076 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
1077 uuid_t uuid = { 0 };
1078 image_desc_t *desc;
1079
1080 parse_blob_opt(optarg, &uuid,
1081 filename, sizeof(filename));
1082
1083 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
1084 remove_usage();
1085
1086 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +00001087 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +00001088 uuid_to_str(name, sizeof(name), &uuid);
1089 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +00001090 add_image_desc(desc);
1091 }
dp-armfb732312016-12-30 09:55:48 +00001092 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm516dfcb2016-11-03 13:59:26 +00001093 break;
1094 }
dp-arm4972ec52016-05-25 16:20:20 +01001095 case 'f':
1096 fflag = 1;
1097 break;
1098 case 'o':
1099 snprintf(outfile, sizeof(outfile), "%s", optarg);
1100 break;
1101 default:
dp-arm29f1b5c2016-09-15 09:58:50 +01001102 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001103 }
1104 }
1105 argc -= optind;
1106 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001107 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001108
1109 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001110 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001111
1112 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
1113 log_errx("File %s already exists, use --force to overwrite it",
1114 outfile);
1115
1116 if (outfile[0] == '\0')
1117 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
1118
1119 parse_fip(argv[0], &toc_header);
1120
dp-arm90d2f0e2016-11-14 15:54:32 +00001121 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm715ef422016-08-30 14:18:58 +01001122 image_t *image;
1123
dp-arm90d2f0e2016-11-14 15:54:32 +00001124 if (desc->action != DO_REMOVE)
dp-arm4972ec52016-05-25 16:20:20 +01001125 continue;
dp-arm516dfcb2016-11-03 13:59:26 +00001126
dp-arm90d2f0e2016-11-14 15:54:32 +00001127 image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +01001128 if (image != NULL) {
dp-arm4972ec52016-05-25 16:20:20 +01001129 if (verbose)
dp-arm516dfcb2016-11-03 13:59:26 +00001130 log_dbgx("Removing %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001131 desc->cmdline_name);
dp-arm715ef422016-08-30 14:18:58 +01001132 remove_image(image);
dp-arm4972ec52016-05-25 16:20:20 +01001133 } else {
dp-arm516dfcb2016-11-03 13:59:26 +00001134 log_warnx("%s does not exist in %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001135 desc->cmdline_name, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001136 }
1137 }
1138
1139 pack_images(outfile, toc_header.flags);
1140 free_images();
1141 return 0;
1142}
1143
1144static void remove_usage(void)
1145{
1146 toc_entry_t *toc_entry = toc_entries;
1147
dp-arm516dfcb2016-11-03 13:59:26 +00001148 printf("fiptool remove [--blob uuid=...] [--force] "
1149 "[--out FIP_FILENAME] [opts] FIP_FILENAME\n");
1150 printf(" --blob uuid=...\tRemove an image with the given UUID.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001151 printf(" --force\t\tIf the output FIP file already exists, use --force to "
1152 "overwrite it.\n");
1153 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
1154 fputc('\n', stderr);
1155 printf("Specific images are removed with the following options:\n");
1156 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1157 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
1158 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +01001159 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001160}
1161
1162static int version_cmd(int argc, char *argv[])
1163{
1164#ifdef VERSION
1165 puts(VERSION);
1166#else
1167 /* If built from fiptool directory, VERSION is not set. */
1168 puts("Unknown version");
1169#endif
1170 return 0;
1171}
1172
1173static void version_usage(void)
1174{
1175 printf("fiptool version\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001176 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001177}
1178
1179static int help_cmd(int argc, char *argv[])
1180{
1181 int i;
1182
1183 if (argc < 2)
1184 usage();
1185 argc--, argv++;
1186
1187 for (i = 0; i < NELEM(cmds); i++) {
1188 if (strcmp(cmds[i].name, argv[0]) == 0 &&
dp-arm29f1b5c2016-09-15 09:58:50 +01001189 cmds[i].usage != NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001190 cmds[i].usage();
dp-arm4972ec52016-05-25 16:20:20 +01001191 }
1192 if (i == NELEM(cmds))
1193 printf("No help for subcommand '%s'\n", argv[0]);
1194 return 0;
1195}
1196
1197static void usage(void)
1198{
1199 printf("usage: [--verbose] fiptool <command> [<args>]\n");
1200 printf("Global options supported:\n");
1201 printf(" --verbose\tEnable verbose output for all commands.\n");
1202 fputc('\n', stderr);
1203 printf("Commands supported:\n");
1204 printf(" info\t\tList images contained in FIP.\n");
1205 printf(" create\tCreate a new FIP with the given images.\n");
1206 printf(" update\tUpdate an existing FIP with the given images.\n");
1207 printf(" unpack\tUnpack images from FIP.\n");
1208 printf(" remove\tRemove images from FIP.\n");
1209 printf(" version\tShow fiptool version.\n");
1210 printf(" help\t\tShow help for given command.\n");
1211 exit(1);
1212}
1213
1214int main(int argc, char *argv[])
1215{
1216 int i, ret = 0;
1217
dp-arm5cd10ae2016-11-07 10:45:59 +00001218 while (1) {
1219 int c, opt_index = 0;
1220 static struct option opts[] = {
1221 { "verbose", no_argument, NULL, 'v' },
1222 { NULL, no_argument, NULL, 0 }
1223 };
1224
1225 /*
1226 * Set POSIX mode so getopt stops at the first non-option
1227 * which is the subcommand.
1228 */
1229 c = getopt_long(argc, argv, "+v", opts, &opt_index);
1230 if (c == -1)
1231 break;
dp-arm4972ec52016-05-25 16:20:20 +01001232
dp-arm5cd10ae2016-11-07 10:45:59 +00001233 switch (c) {
1234 case 'v':
1235 verbose = 1;
1236 break;
1237 default:
Masahiro Yamada48a24972016-10-26 13:24:26 +09001238 usage();
dp-arm5cd10ae2016-11-07 10:45:59 +00001239 }
dp-arm4972ec52016-05-25 16:20:20 +01001240 }
dp-arm5cd10ae2016-11-07 10:45:59 +00001241 argc -= optind;
1242 argv += optind;
1243 /* Reset optind for subsequent getopt processing. */
1244 optind = 0;
1245
1246 if (argc == 0)
1247 usage();
dp-arm4972ec52016-05-25 16:20:20 +01001248
dp-arm90d2f0e2016-11-14 15:54:32 +00001249 fill_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001250 for (i = 0; i < NELEM(cmds); i++) {
1251 if (strcmp(cmds[i].name, argv[0]) == 0) {
1252 ret = cmds[i].handler(argc, argv);
1253 break;
1254 }
1255 }
1256 if (i == NELEM(cmds))
1257 usage();
dp-arm90d2f0e2016-11-14 15:54:32 +00001258 free_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001259 return ret;
1260}