1 /*
2 * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <sys/types.h>
8 #include <sys/stat.h>
9
10 #include <assert.h>
11 #include <errno.h>
12 #include <limits.h>
13 #include <stdarg.h>
14 #include <stdint.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 #include "fiptool.h"
20 #include "tbbr_config.h"
21
22 #define OPT_TOC_ENTRY 0
23 #define OPT_PLAT_TOC_FLAGS 1
24 #define OPT_ALIGN 2
25
26 static int info_cmd(int argc, char *argv[]);
27 static void info_usage(int);
28 static int create_cmd(int argc, char *argv[]);
29 static void create_usage(int);
30 static int update_cmd(int argc, char *argv[]);
31 static void update_usage(int);
32 static int unpack_cmd(int argc, char *argv[]);
33 static void unpack_usage(int);
34 static int remove_cmd(int argc, char *argv[]);
35 static void remove_usage(int);
36 static int version_cmd(int argc, char *argv[]);
37 static void version_usage(int);
38 static int help_cmd(int argc, char *argv[]);
39 static void usage(void);
40
41 /* Available subcommands. */
42 static cmd_t cmds[] = {
43 { .name = "info", .handler = info_cmd, .usage = info_usage },
44 { .name = "create", .handler = create_cmd, .usage = create_usage },
45 { .name = "update", .handler = update_cmd, .usage = update_usage },
46 { .name = "unpack", .handler = unpack_cmd, .usage = unpack_usage },
47 { .name = "remove", .handler = remove_cmd, .usage = remove_usage },
48 { .name = "version", .handler = version_cmd, .usage = version_usage },
49 { .name = "help", .handler = help_cmd, .usage = NULL },
50 };
51
52 static image_desc_t *image_desc_head;
53 static size_t nr_image_descs;
54 static const uuid_t uuid_null;
55 static int verbose;
56
vlog(int prio,const char * msg,va_list ap)57 static void vlog(int prio, const char *msg, va_list ap)
58 {
59 char *prefix[] = { "DEBUG", "WARN", "ERROR" };
60
61 fprintf(stderr, "%s: ", prefix[prio]);
62 vfprintf(stderr, msg, ap);
63 fputc('\n', stderr);
64 }
65
log_dbgx(const char * msg,...)66 static void log_dbgx(const char *msg, ...)
67 {
68 va_list ap;
69
70 va_start(ap, msg);
71 vlog(LOG_DBG, msg, ap);
72 va_end(ap);
73 }
74
log_warnx(const char * msg,...)75 static void log_warnx(const char *msg, ...)
76 {
77 va_list ap;
78
79 va_start(ap, msg);
80 vlog(LOG_WARN, msg, ap);
81 va_end(ap);
82 }
83
log_err(const char * msg,...)84 static void log_err(const char *msg, ...)
85 {
86 char buf[512];
87 va_list ap;
88
89 va_start(ap, msg);
90 snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
91 vlog(LOG_ERR, buf, ap);
92 va_end(ap);
93 exit(1);
94 }
95
log_errx(const char * msg,...)96 static void log_errx(const char *msg, ...)
97 {
98 va_list ap;
99
100 va_start(ap, msg);
101 vlog(LOG_ERR, msg, ap);
102 va_end(ap);
103 exit(1);
104 }
105
xstrdup(const char * s,const char * msg)106 static char *xstrdup(const char *s, const char *msg)
107 {
108 char *d;
109
110 d = strdup(s);
111 if (d == NULL)
112 log_errx("strdup: %s", msg);
113 return d;
114 }
115
xmalloc(size_t size,const char * msg)116 static void *xmalloc(size_t size, const char *msg)
117 {
118 void *d;
119
120 d = malloc(size);
121 if (d == NULL)
122 log_errx("malloc: %s", msg);
123 return d;
124 }
125
xzalloc(size_t size,const char * msg)126 static void *xzalloc(size_t size, const char *msg)
127 {
128 return memset(xmalloc(size, msg), 0, size);
129 }
130
xfwrite(void * buf,size_t size,FILE * fp,const char * filename)131 static void xfwrite(void *buf, size_t size, FILE *fp, const char *filename)
132 {
133 if (fwrite(buf, 1, size, fp) != size)
134 log_errx("Failed to write %s", filename);
135 }
136
new_image_desc(const uuid_t * uuid,const char * name,const char * cmdline_name)137 static image_desc_t *new_image_desc(const uuid_t *uuid,
138 const char *name, const char *cmdline_name)
139 {
140 image_desc_t *desc;
141
142 desc = xzalloc(sizeof(*desc),
143 "failed to allocate memory for image descriptor");
144 memcpy(&desc->uuid, uuid, sizeof(uuid_t));
145 desc->name = xstrdup(name,
146 "failed to allocate memory for image name");
147 desc->cmdline_name = xstrdup(cmdline_name,
148 "failed to allocate memory for image command line name");
149 desc->action = DO_UNSPEC;
150 return desc;
151 }
152
set_image_desc_action(image_desc_t * desc,int action,const char * arg)153 static void set_image_desc_action(image_desc_t *desc, int action,
154 const char *arg)
155 {
156 assert(desc != NULL);
157
158 if (desc->action_arg != (char *)DO_UNSPEC)
159 free(desc->action_arg);
160 desc->action = action;
161 desc->action_arg = NULL;
162 if (arg != NULL)
163 desc->action_arg = xstrdup(arg,
164 "failed to allocate memory for argument");
165 }
166
free_image_desc(image_desc_t * desc)167 static void free_image_desc(image_desc_t *desc)
168 {
169 free(desc->name);
170 free(desc->cmdline_name);
171 free(desc->action_arg);
172 if (desc->image) {
173 free(desc->image->buffer);
174 free(desc->image);
175 }
176 free(desc);
177 }
178
add_image_desc(image_desc_t * desc)179 static void add_image_desc(image_desc_t *desc)
180 {
181 image_desc_t **p = &image_desc_head;
182
183 while (*p)
184 p = &(*p)->next;
185
186 assert(*p == NULL);
187 *p = desc;
188 nr_image_descs++;
189 }
190
free_image_descs(void)191 static void free_image_descs(void)
192 {
193 image_desc_t *desc = image_desc_head, *tmp;
194
195 while (desc != NULL) {
196 tmp = desc->next;
197 free_image_desc(desc);
198 desc = tmp;
199 nr_image_descs--;
200 }
201 assert(nr_image_descs == 0);
202 }
203
fill_image_descs(void)204 static void fill_image_descs(void)
205 {
206 toc_entry_t *toc_entry;
207
208 for (toc_entry = toc_entries;
209 toc_entry->cmdline_name != NULL;
210 toc_entry++) {
211 image_desc_t *desc;
212
213 desc = new_image_desc(&toc_entry->uuid,
214 toc_entry->name,
215 toc_entry->cmdline_name);
216 add_image_desc(desc);
217 }
218 #ifdef PLAT_DEF_FIP_UUID
219 for (toc_entry = plat_def_toc_entries;
220 toc_entry->cmdline_name != NULL;
221 toc_entry++) {
222 image_desc_t *desc;
223
224 desc = new_image_desc(&toc_entry->uuid,
225 toc_entry->name,
226 toc_entry->cmdline_name);
227 add_image_desc(desc);
228 }
229 #endif
230 }
231
lookup_image_desc_from_uuid(const uuid_t * uuid)232 static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid)
233 {
234 image_desc_t *desc;
235
236 for (desc = image_desc_head; desc != NULL; desc = desc->next)
237 if (memcmp(&desc->uuid, uuid, sizeof(uuid_t)) == 0)
238 return desc;
239 return NULL;
240 }
241
lookup_image_desc_from_opt(const char * opt)242 static image_desc_t *lookup_image_desc_from_opt(const char *opt)
243 {
244 image_desc_t *desc;
245
246 for (desc = image_desc_head; desc != NULL; desc = desc->next)
247 if (strcmp(desc->cmdline_name, opt) == 0)
248 return desc;
249 return NULL;
250 }
251
uuid_to_str(char * s,size_t len,const uuid_t * u)252 static void uuid_to_str(char *s, size_t len, const uuid_t *u)
253 {
254 assert(len >= (_UUID_STR_LEN + 1));
255
256 snprintf(s, len,
257 "%02X%02X%02X%02X-%02X%02X-%02X%02X-%04X-%04X%04X%04X",
258 u->time_low[0], u->time_low[1], u->time_low[2], u->time_low[3],
259 u->time_mid[0], u->time_mid[1],
260 u->time_hi_and_version[0], u->time_hi_and_version[1],
261 (u->clock_seq_hi_and_reserved << 8) | u->clock_seq_low,
262 (u->node[0] << 8) | u->node[1],
263 (u->node[2] << 8) | u->node[3],
264 (u->node[4] << 8) | u->node[5]);
265 }
266
uuid_from_str(uuid_t * u,const char * s)267 static void uuid_from_str(uuid_t *u, const char *s)
268 {
269 int n;
270
271 if (s == NULL)
272 log_errx("UUID cannot be NULL");
273 if (strlen(s) != _UUID_STR_LEN)
274 log_errx("Invalid UUID: %s", s);
275
276 n = sscanf(s,
277 "%2hhx%2hhx%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
278 &u->time_low[0], &u->time_low[1], &u->time_low[2], &u->time_low[3],
279 &u->time_mid[0], &u->time_mid[1],
280 &u->time_hi_and_version[0], &u->time_hi_and_version[1],
281 &u->clock_seq_hi_and_reserved, &u->clock_seq_low,
282 &u->node[0], &u->node[1],
283 &u->node[2], &u->node[3],
284 &u->node[4], &u->node[5]);
285 /*
286 * Given the format specifier above, we expect 16 items to be scanned
287 * for a properly formatted UUID.
288 */
289 if (n != 16)
290 log_errx("Invalid UUID: %s", s);
291 }
292
parse_fip(const char * filename,fip_toc_header_t * toc_header_out)293 static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
294 {
295 struct BLD_PLAT_STAT st;
296 FILE *fp;
297 char *buf, *bufend;
298 fip_toc_header_t *toc_header;
299 fip_toc_entry_t *toc_entry;
300 int terminated = 0;
301
302 fp = fopen(filename, "rb");
303 if (fp == NULL)
304 log_err("fopen %s", filename);
305
306 if (fstat(fileno(fp), &st) == -1)
307 log_err("fstat %s", filename);
308
309 buf = xmalloc(st.st_size, "failed to load file into memory");
310 if (fread(buf, 1, st.st_size, fp) != st.st_size)
311 log_errx("Failed to read %s", filename);
312 bufend = buf + st.st_size;
313 fclose(fp);
314
315 if (st.st_size < sizeof(fip_toc_header_t))
316 log_errx("FIP %s is truncated", filename);
317
318 toc_header = (fip_toc_header_t *)buf;
319 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
320
321 if (toc_header->name != TOC_HEADER_NAME)
322 log_errx("%s is not a FIP file", filename);
323
324 /* Return the ToC header if the caller wants it. */
325 if (toc_header_out != NULL)
326 *toc_header_out = *toc_header;
327
328 /* Walk through each ToC entry in the file. */
329 while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
330 image_t *image;
331 image_desc_t *desc;
332
333 /* Found the ToC terminator, we are done. */
334 if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
335 terminated = 1;
336 break;
337 }
338
339 /*
340 * Build a new image out of the ToC entry and add it to the
341 * table of images.
342 */
343 image = xzalloc(sizeof(*image),
344 "failed to allocate memory for image");
345 image->toc_e = *toc_entry;
346 image->buffer = xmalloc(toc_entry->size,
347 "failed to allocate image buffer, is FIP file corrupted?");
348 /* Overflow checks before memory copy. */
349 if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
350 log_errx("FIP %s is corrupted", filename);
351 if (toc_entry->size + toc_entry->offset_address > st.st_size)
352 log_errx("FIP %s is corrupted", filename);
353
354 memcpy(image->buffer, buf + toc_entry->offset_address,
355 toc_entry->size);
356
357 /* If this is an unknown image, create a descriptor for it. */
358 desc = lookup_image_desc_from_uuid(&toc_entry->uuid);
359 if (desc == NULL) {
360 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
361
362 uuid_to_str(name, sizeof(name), &toc_entry->uuid);
363 snprintf(filename, sizeof(filename), "%s%s",
364 name, ".bin");
365 desc = new_image_desc(&toc_entry->uuid, name, "blob");
366 desc->action = DO_UNPACK;
367 desc->action_arg = xstrdup(filename,
368 "failed to allocate memory for blob filename");
369 add_image_desc(desc);
370 }
371
372 assert(desc->image == NULL);
373 desc->image = image;
374
375 toc_entry++;
376 }
377
378 if (terminated == 0)
379 log_errx("FIP %s does not have a ToC terminator entry",
380 filename);
381 free(buf);
382 return 0;
383 }
384
read_image_from_file(const uuid_t * uuid,const char * filename)385 static image_t *read_image_from_file(const uuid_t *uuid, const char *filename)
386 {
387 struct BLD_PLAT_STAT st;
388 image_t *image;
389 FILE *fp;
390
391 assert(uuid != NULL);
392 assert(filename != NULL);
393
394 fp = fopen(filename, "rb");
395 if (fp == NULL)
396 log_err("fopen %s", filename);
397
398 if (fstat(fileno(fp), &st) == -1)
399 log_errx("fstat %s", filename);
400
401 image = xzalloc(sizeof(*image), "failed to allocate memory for image");
402 image->toc_e.uuid = *uuid;
403 image->buffer = xmalloc(st.st_size, "failed to allocate image buffer");
404 if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
405 log_errx("Failed to read %s", filename);
406 image->toc_e.size = st.st_size;
407
408 fclose(fp);
409 return image;
410 }
411
write_image_to_file(const image_t * image,const char * filename)412 static int write_image_to_file(const image_t *image, const char *filename)
413 {
414 FILE *fp;
415
416 fp = fopen(filename, "wb");
417 if (fp == NULL)
418 log_err("fopen");
419 xfwrite(image->buffer, image->toc_e.size, fp, filename);
420 fclose(fp);
421 return 0;
422 }
423
add_opt(struct option * opts,size_t * nr_opts,const char * name,int has_arg,int val)424 static struct option *add_opt(struct option *opts, size_t *nr_opts,
425 const char *name, int has_arg, int val)
426 {
427 opts = realloc(opts, (*nr_opts + 1) * sizeof(*opts));
428 if (opts == NULL)
429 log_err("realloc");
430 opts[*nr_opts].name = name;
431 opts[*nr_opts].has_arg = has_arg;
432 opts[*nr_opts].flag = NULL;
433 opts[*nr_opts].val = val;
434 ++*nr_opts;
435 return opts;
436 }
437
fill_common_opts(struct option * opts,size_t * nr_opts,int has_arg)438 static struct option *fill_common_opts(struct option *opts, size_t *nr_opts,
439 int has_arg)
440 {
441 image_desc_t *desc;
442
443 for (desc = image_desc_head; desc != NULL; desc = desc->next)
444 opts = add_opt(opts, nr_opts, desc->cmdline_name, has_arg,
445 OPT_TOC_ENTRY);
446 return opts;
447 }
448
md_print(const unsigned char * md,size_t len)449 static void md_print(const unsigned char *md, size_t len)
450 {
451 size_t i;
452
453 for (i = 0; i < len; i++)
454 printf("%02x", md[i]);
455 }
456
info_cmd(int argc,char * argv[])457 static int info_cmd(int argc, char *argv[])
458 {
459 image_desc_t *desc;
460 fip_toc_header_t toc_header;
461
462 if (argc != 2)
463 info_usage(EXIT_FAILURE);
464 argc--, argv++;
465
466 parse_fip(argv[0], &toc_header);
467
468 if (verbose) {
469 log_dbgx("toc_header[name]: 0x%llX",
470 (unsigned long long)toc_header.name);
471 log_dbgx("toc_header[serial_number]: 0x%llX",
472 (unsigned long long)toc_header.serial_number);
473 log_dbgx("toc_header[flags]: 0x%llX",
474 (unsigned long long)toc_header.flags);
475 }
476
477 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
478 image_t *image = desc->image;
479
480 if (image == NULL)
481 continue;
482 printf("%s: offset=0x%llX, size=0x%llX, cmdline=\"--%s\"",
483 desc->name,
484 (unsigned long long)image->toc_e.offset_address,
485 (unsigned long long)image->toc_e.size,
486 desc->cmdline_name);
487 #ifndef _MSC_VER /* We don't have SHA256 for Visual Studio. */
488 if (verbose) {
489 unsigned char md[SHA256_DIGEST_LENGTH];
490
491 SHA256(image->buffer, image->toc_e.size, md);
492 printf(", sha256=");
493 md_print(md, sizeof(md));
494 }
495 #endif
496 putchar('\n');
497 }
498
499 return 0;
500 }
501
info_usage(int exit_status)502 static void info_usage(int exit_status)
503 {
504 printf("fiptool info FIP_FILENAME\n");
505 exit(exit_status);
506 }
507
pack_images(const char * filename,uint64_t toc_flags,unsigned long align)508 static int pack_images(const char *filename, uint64_t toc_flags, unsigned long align)
509 {
510 FILE *fp;
511 image_desc_t *desc;
512 fip_toc_header_t *toc_header;
513 fip_toc_entry_t *toc_entry;
514 char *buf;
515 uint64_t entry_offset, buf_size, payload_size = 0, pad_size;
516 size_t nr_images = 0;
517
518 for (desc = image_desc_head; desc != NULL; desc = desc->next)
519 if (desc->image != NULL)
520 nr_images++;
521
522 buf_size = sizeof(fip_toc_header_t) +
523 sizeof(fip_toc_entry_t) * (nr_images + 1);
524 buf = calloc(1, buf_size);
525 if (buf == NULL)
526 log_err("calloc");
527
528 /* Build up header and ToC entries from the image table. */
529 toc_header = (fip_toc_header_t *)buf;
530 toc_header->name = TOC_HEADER_NAME;
531 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
532 toc_header->flags = toc_flags;
533
534 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
535
536 entry_offset = buf_size;
537 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
538 image_t *image = desc->image;
539
540 if (image == NULL || (image->toc_e.size == 0ULL))
541 continue;
542 payload_size += image->toc_e.size;
543 entry_offset = (entry_offset + align - 1) & ~(align - 1);
544 image->toc_e.offset_address = entry_offset;
545 *toc_entry++ = image->toc_e;
546 entry_offset += image->toc_e.size;
547 }
548
549 /*
550 * Append a null uuid entry to mark the end of ToC entries.
551 * NOTE the offset address for the last toc_entry must match the fip
552 * size.
553 */
554 memset(toc_entry, 0, sizeof(*toc_entry));
555 toc_entry->offset_address = (entry_offset + align - 1) & ~(align - 1);
556
557 /* Generate the FIP file. */
558 fp = fopen(filename, "wb");
559 if (fp == NULL)
560 log_err("fopen %s", filename);
561
562 if (verbose)
563 log_dbgx("Metadata size: %zu bytes", buf_size);
564
565 xfwrite(buf, buf_size, fp, filename);
566
567 if (verbose)
568 log_dbgx("Payload size: %zu bytes", payload_size);
569
570 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
571 image_t *image = desc->image;
572
573 if (image == NULL)
574 continue;
575 if (fseek(fp, image->toc_e.offset_address, SEEK_SET))
576 log_errx("Failed to set file position");
577
578 xfwrite(image->buffer, image->toc_e.size, fp, filename);
579 }
580
581 if (fseek(fp, entry_offset, SEEK_SET))
582 log_errx("Failed to set file position");
583
584 pad_size = toc_entry->offset_address - entry_offset;
585 while (pad_size--)
586 fputc(0x0, fp);
587
588 free(buf);
589 fclose(fp);
590 return 0;
591 }
592
593 /*
594 * This function is shared between the create and update subcommands.
595 * The difference between the two subcommands is that when the FIP file
596 * is created, the parsing of an existing FIP is skipped. This results
597 * in update_fip() creating the new FIP file from scratch because the
598 * internal image table is not populated.
599 */
update_fip(void)600 static void update_fip(void)
601 {
602 image_desc_t *desc;
603
604 /* Add or replace images in the FIP file. */
605 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
606 image_t *image;
607
608 if (desc->action != DO_PACK)
609 continue;
610
611 image = read_image_from_file(&desc->uuid,
612 desc->action_arg);
613 if (desc->image != NULL) {
614 if (verbose) {
615 log_dbgx("Replacing %s with %s",
616 desc->cmdline_name,
617 desc->action_arg);
618 }
619 free(desc->image);
620 desc->image = image;
621 } else {
622 if (verbose)
623 log_dbgx("Adding image %s",
624 desc->action_arg);
625 desc->image = image;
626 }
627 }
628 }
629
parse_plat_toc_flags(const char * arg,unsigned long long * toc_flags)630 static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
631 {
632 unsigned long long flags;
633 char *endptr;
634
635 errno = 0;
636 flags = strtoull(arg, &endptr, 16);
637 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
638 log_errx("Invalid platform ToC flags: %s", arg);
639 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
640 *toc_flags |= flags << 32;
641 }
642
is_power_of_2(unsigned long x)643 static int is_power_of_2(unsigned long x)
644 {
645 return x && !(x & (x - 1));
646 }
647
get_image_align(char * arg)648 static unsigned long get_image_align(char *arg)
649 {
650 char *endptr;
651 unsigned long align;
652
653 errno = 0;
654 align = strtoul(arg, &endptr, 0);
655 if (*endptr != '\0' || !is_power_of_2(align) || errno != 0)
656 log_errx("Invalid alignment: %s", arg);
657
658 return align;
659 }
660
parse_blob_opt(char * arg,uuid_t * uuid,char * filename,size_t len)661 static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
662 {
663 char *p;
664
665 for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
666 if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
667 p += strlen("uuid=");
668 uuid_from_str(uuid, p);
669 } else if (strncmp(p, "file=", strlen("file=")) == 0) {
670 p += strlen("file=");
671 snprintf(filename, len, "%s", p);
672 }
673 }
674 }
675
create_cmd(int argc,char * argv[])676 static int create_cmd(int argc, char *argv[])
677 {
678 struct option *opts = NULL;
679 size_t nr_opts = 0;
680 unsigned long long toc_flags = 0;
681 unsigned long align = 1;
682
683 if (argc < 2)
684 create_usage(EXIT_FAILURE);
685
686 opts = fill_common_opts(opts, &nr_opts, required_argument);
687 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
688 OPT_PLAT_TOC_FLAGS);
689 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
690 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
691 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
692
693 while (1) {
694 int c, opt_index = 0;
695
696 c = getopt_long(argc, argv, "b:", opts, &opt_index);
697 if (c == -1)
698 break;
699
700 switch (c) {
701 case OPT_TOC_ENTRY: {
702 image_desc_t *desc;
703
704 desc = lookup_image_desc_from_opt(opts[opt_index].name);
705 set_image_desc_action(desc, DO_PACK, optarg);
706 break;
707 }
708 case OPT_PLAT_TOC_FLAGS:
709 parse_plat_toc_flags(optarg, &toc_flags);
710 break;
711 case OPT_ALIGN:
712 align = get_image_align(optarg);
713 break;
714 case 'b': {
715 char name[_UUID_STR_LEN + 1];
716 char filename[PATH_MAX] = { 0 };
717 uuid_t uuid = uuid_null;
718 image_desc_t *desc;
719
720 parse_blob_opt(optarg, &uuid,
721 filename, sizeof(filename));
722
723 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
724 filename[0] == '\0')
725 create_usage(EXIT_FAILURE);
726
727 desc = lookup_image_desc_from_uuid(&uuid);
728 if (desc == NULL) {
729 uuid_to_str(name, sizeof(name), &uuid);
730 desc = new_image_desc(&uuid, name, "blob");
731 add_image_desc(desc);
732 }
733 set_image_desc_action(desc, DO_PACK, filename);
734 break;
735 }
736 default:
737 create_usage(EXIT_FAILURE);
738 }
739 }
740 argc -= optind;
741 argv += optind;
742 free(opts);
743
744 if (argc == 0)
745 create_usage(EXIT_SUCCESS);
746
747 update_fip();
748
749 pack_images(argv[0], toc_flags, align);
750 return 0;
751 }
752
create_usage(int exit_status)753 static void create_usage(int exit_status)
754 {
755 toc_entry_t *toc_entry = toc_entries;
756
757 printf("fiptool create [opts] FIP_FILENAME\n");
758 printf("\n");
759 printf("Options:\n");
760 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
761 printf(" --blob uuid=...,file=...\tAdd an image with the given UUID pointed to by file.\n");
762 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field occupying bits 32-47 in 64-bit ToC header.\n");
763 printf("\n");
764 printf("Specific images are packed with the following options:\n");
765 for (; toc_entry->cmdline_name != NULL; toc_entry++)
766 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
767 toc_entry->name);
768 #ifdef PLAT_DEF_FIP_UUID
769 toc_entry = plat_def_toc_entries;
770 for (; toc_entry->cmdline_name != NULL; toc_entry++)
771 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
772 toc_entry->name);
773 #endif
774 exit(exit_status);
775 }
776
update_cmd(int argc,char * argv[])777 static int update_cmd(int argc, char *argv[])
778 {
779 struct option *opts = NULL;
780 size_t nr_opts = 0;
781 char outfile[PATH_MAX] = { 0 };
782 fip_toc_header_t toc_header = { 0 };
783 unsigned long long toc_flags = 0;
784 unsigned long align = 1;
785 int pflag = 0;
786
787 if (argc < 2)
788 update_usage(EXIT_FAILURE);
789
790 opts = fill_common_opts(opts, &nr_opts, required_argument);
791 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
792 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
793 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
794 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
795 OPT_PLAT_TOC_FLAGS);
796 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
797
798 while (1) {
799 int c, opt_index = 0;
800
801 c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
802 if (c == -1)
803 break;
804
805 switch (c) {
806 case OPT_TOC_ENTRY: {
807 image_desc_t *desc;
808
809 desc = lookup_image_desc_from_opt(opts[opt_index].name);
810 set_image_desc_action(desc, DO_PACK, optarg);
811 break;
812 }
813 case OPT_PLAT_TOC_FLAGS:
814 parse_plat_toc_flags(optarg, &toc_flags);
815 pflag = 1;
816 break;
817 case 'b': {
818 char name[_UUID_STR_LEN + 1];
819 char filename[PATH_MAX] = { 0 };
820 uuid_t uuid = uuid_null;
821 image_desc_t *desc;
822
823 parse_blob_opt(optarg, &uuid,
824 filename, sizeof(filename));
825
826 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
827 filename[0] == '\0')
828 update_usage(EXIT_FAILURE);
829
830 desc = lookup_image_desc_from_uuid(&uuid);
831 if (desc == NULL) {
832 uuid_to_str(name, sizeof(name), &uuid);
833 desc = new_image_desc(&uuid, name, "blob");
834 add_image_desc(desc);
835 }
836 set_image_desc_action(desc, DO_PACK, filename);
837 break;
838 }
839 case OPT_ALIGN:
840 align = get_image_align(optarg);
841 break;
842 case 'o':
843 snprintf(outfile, sizeof(outfile), "%s", optarg);
844 break;
845 default:
846 update_usage(EXIT_FAILURE);
847 }
848 }
849 argc -= optind;
850 argv += optind;
851 free(opts);
852
853 if (argc == 0)
854 update_usage(EXIT_SUCCESS);
855
856 if (outfile[0] == '\0')
857 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
858
859 if (access(argv[0], F_OK) == 0)
860 parse_fip(argv[0], &toc_header);
861
862 if (pflag)
863 toc_header.flags &= ~(0xffffULL << 32);
864 toc_flags = (toc_header.flags |= toc_flags);
865
866 update_fip();
867
868 pack_images(outfile, toc_flags, align);
869 return 0;
870 }
871
update_usage(int exit_status)872 static void update_usage(int exit_status)
873 {
874 toc_entry_t *toc_entry = toc_entries;
875
876 printf("fiptool update [opts] FIP_FILENAME\n");
877 printf("\n");
878 printf("Options:\n");
879 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
880 printf(" --blob uuid=...,file=...\tAdd or update an image with the given UUID pointed to by file.\n");
881 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
882 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field occupying bits 32-47 in 64-bit ToC header.\n");
883 printf("\n");
884 printf("Specific images are packed with the following options:\n");
885 for (; toc_entry->cmdline_name != NULL; toc_entry++)
886 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
887 toc_entry->name);
888 #ifdef PLAT_DEF_FIP_UUID
889 toc_entry = plat_def_toc_entries;
890 for (; toc_entry->cmdline_name != NULL; toc_entry++)
891 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
892 toc_entry->name);
893 #endif
894 exit(exit_status);
895 }
896
unpack_cmd(int argc,char * argv[])897 static int unpack_cmd(int argc, char *argv[])
898 {
899 struct option *opts = NULL;
900 size_t nr_opts = 0;
901 char outdir[PATH_MAX] = { 0 };
902 image_desc_t *desc;
903 int fflag = 0;
904 int unpack_all = 1;
905
906 if (argc < 2)
907 unpack_usage(EXIT_FAILURE);
908
909 opts = fill_common_opts(opts, &nr_opts, required_argument);
910 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
911 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
912 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
913 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
914
915 while (1) {
916 int c, opt_index = 0;
917
918 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
919 if (c == -1)
920 break;
921
922 switch (c) {
923 case OPT_TOC_ENTRY: {
924 image_desc_t *desc;
925
926 desc = lookup_image_desc_from_opt(opts[opt_index].name);
927 set_image_desc_action(desc, DO_UNPACK, optarg);
928 unpack_all = 0;
929 break;
930 }
931 case 'b': {
932 char name[_UUID_STR_LEN + 1];
933 char filename[PATH_MAX] = { 0 };
934 uuid_t uuid = uuid_null;
935 image_desc_t *desc;
936
937 parse_blob_opt(optarg, &uuid,
938 filename, sizeof(filename));
939
940 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
941 filename[0] == '\0')
942 unpack_usage(EXIT_FAILURE);
943
944 desc = lookup_image_desc_from_uuid(&uuid);
945 if (desc == NULL) {
946 uuid_to_str(name, sizeof(name), &uuid);
947 desc = new_image_desc(&uuid, name, "blob");
948 add_image_desc(desc);
949 }
950 set_image_desc_action(desc, DO_UNPACK, filename);
951 unpack_all = 0;
952 break;
953 }
954 case 'f':
955 fflag = 1;
956 break;
957 case 'o':
958 snprintf(outdir, sizeof(outdir), "%s", optarg);
959 break;
960 default:
961 unpack_usage(EXIT_FAILURE);
962 }
963 }
964 argc -= optind;
965 argv += optind;
966 free(opts);
967
968 if (argc == 0)
969 unpack_usage(EXIT_SUCCESS);
970
971 parse_fip(argv[0], NULL);
972
973 if (outdir[0] != '\0')
974 if (chdir(outdir) == -1)
975 log_err("chdir %s", outdir);
976
977 /* Unpack all specified images. */
978 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
979 char file[PATH_MAX];
980 image_t *image = desc->image;
981
982 if (!unpack_all && desc->action != DO_UNPACK)
983 continue;
984
985 /* Build filename. */
986 if (desc->action_arg == NULL)
987 snprintf(file, sizeof(file), "%s.bin",
988 desc->cmdline_name);
989 else
990 snprintf(file, sizeof(file), "%s",
991 desc->action_arg);
992
993 if (image == NULL) {
994 if (!unpack_all)
995 log_warnx("%s does not exist in %s",
996 file, argv[0]);
997 continue;
998 }
999
1000 if (access(file, F_OK) != 0 || fflag) {
1001 if (verbose)
1002 log_dbgx("Unpacking %s", file);
1003 write_image_to_file(image, file);
1004 } else {
1005 log_warnx("File %s already exists, use --force to overwrite it",
1006 file);
1007 }
1008 }
1009
1010 return 0;
1011 }
1012
unpack_usage(int exit_status)1013 static void unpack_usage(int exit_status)
1014 {
1015 toc_entry_t *toc_entry = toc_entries;
1016
1017 printf("fiptool unpack [opts] FIP_FILENAME\n");
1018 printf("\n");
1019 printf("Options:\n");
1020 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID to file.\n");
1021 printf(" --force\t\t\tIf the output file already exists, use --force to overwrite it.\n");
1022 printf(" --out path\t\t\tSet the output directory path.\n");
1023 printf("\n");
1024 printf("Specific images are unpacked with the following options:\n");
1025 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1026 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
1027 toc_entry->name);
1028 #ifdef PLAT_DEF_FIP_UUID
1029 toc_entry = plat_def_toc_entries;
1030 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1031 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
1032 toc_entry->name);
1033 #endif
1034 printf("\n");
1035 printf("If no options are provided, all images will be unpacked.\n");
1036 exit(exit_status);
1037 }
1038
remove_cmd(int argc,char * argv[])1039 static int remove_cmd(int argc, char *argv[])
1040 {
1041 struct option *opts = NULL;
1042 size_t nr_opts = 0;
1043 char outfile[PATH_MAX] = { 0 };
1044 fip_toc_header_t toc_header;
1045 image_desc_t *desc;
1046 unsigned long align = 1;
1047 int fflag = 0;
1048
1049 if (argc < 2)
1050 remove_usage(EXIT_FAILURE);
1051
1052 opts = fill_common_opts(opts, &nr_opts, no_argument);
1053 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
1054 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
1055 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
1056 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
1057 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
1058
1059 while (1) {
1060 int c, opt_index = 0;
1061
1062 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
1063 if (c == -1)
1064 break;
1065
1066 switch (c) {
1067 case OPT_TOC_ENTRY: {
1068 image_desc_t *desc;
1069
1070 desc = lookup_image_desc_from_opt(opts[opt_index].name);
1071 set_image_desc_action(desc, DO_REMOVE, NULL);
1072 break;
1073 }
1074 case OPT_ALIGN:
1075 align = get_image_align(optarg);
1076 break;
1077 case 'b': {
1078 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
1079 uuid_t uuid = uuid_null;
1080 image_desc_t *desc;
1081
1082 parse_blob_opt(optarg, &uuid,
1083 filename, sizeof(filename));
1084
1085 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
1086 remove_usage(EXIT_FAILURE);
1087
1088 desc = lookup_image_desc_from_uuid(&uuid);
1089 if (desc == NULL) {
1090 uuid_to_str(name, sizeof(name), &uuid);
1091 desc = new_image_desc(&uuid, name, "blob");
1092 add_image_desc(desc);
1093 }
1094 set_image_desc_action(desc, DO_REMOVE, NULL);
1095 break;
1096 }
1097 case 'f':
1098 fflag = 1;
1099 break;
1100 case 'o':
1101 snprintf(outfile, sizeof(outfile), "%s", optarg);
1102 break;
1103 default:
1104 remove_usage(EXIT_FAILURE);
1105 }
1106 }
1107 argc -= optind;
1108 argv += optind;
1109 free(opts);
1110
1111 if (argc == 0)
1112 remove_usage(EXIT_SUCCESS);
1113
1114 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
1115 log_errx("File %s already exists, use --force to overwrite it",
1116 outfile);
1117
1118 if (outfile[0] == '\0')
1119 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
1120
1121 parse_fip(argv[0], &toc_header);
1122
1123 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
1124 if (desc->action != DO_REMOVE)
1125 continue;
1126
1127 if (desc->image != NULL) {
1128 if (verbose)
1129 log_dbgx("Removing %s",
1130 desc->cmdline_name);
1131 free(desc->image);
1132 desc->image = NULL;
1133 } else {
1134 log_warnx("%s does not exist in %s",
1135 desc->cmdline_name, argv[0]);
1136 }
1137 }
1138
1139 pack_images(outfile, toc_header.flags, align);
1140 return 0;
1141 }
1142
remove_usage(int exit_status)1143 static void remove_usage(int exit_status)
1144 {
1145 toc_entry_t *toc_entry = toc_entries;
1146
1147 printf("fiptool remove [opts] FIP_FILENAME\n");
1148 printf("\n");
1149 printf("Options:\n");
1150 printf(" --align <value>\tEach image is aligned to <value> (default: 1).\n");
1151 printf(" --blob uuid=...\tRemove an image with the given UUID.\n");
1152 printf(" --force\t\tIf the output FIP file already exists, use --force to overwrite it.\n");
1153 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
1154 printf("\n");
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);
1159 #ifdef PLAT_DEF_FIP_UUID
1160 toc_entry = plat_def_toc_entries;
1161 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1162 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
1163 toc_entry->name);
1164 #endif
1165 exit(exit_status);
1166 }
1167
version_cmd(int argc,char * argv[])1168 static int version_cmd(int argc, char *argv[])
1169 {
1170 #ifdef VERSION
1171 puts(VERSION);
1172 #else
1173 /* If built from fiptool directory, VERSION is not set. */
1174 puts("Unknown version");
1175 #endif
1176 return 0;
1177 }
1178
version_usage(int exit_status)1179 static void version_usage(int exit_status)
1180 {
1181 printf("fiptool version\n");
1182 exit(exit_status);
1183 }
1184
help_cmd(int argc,char * argv[])1185 static int help_cmd(int argc, char *argv[])
1186 {
1187 int i;
1188
1189 if (argc < 2)
1190 usage();
1191 argc--, argv++;
1192
1193 for (i = 0; i < NELEM(cmds); i++) {
1194 if (strcmp(cmds[i].name, argv[0]) == 0 &&
1195 cmds[i].usage != NULL)
1196 cmds[i].usage(EXIT_SUCCESS);
1197 }
1198 if (i == NELEM(cmds))
1199 printf("No help for subcommand '%s'\n", argv[0]);
1200 return 0;
1201 }
1202
usage(void)1203 static void usage(void)
1204 {
1205 printf("usage: fiptool [--verbose] <command> [<args>]\n");
1206 printf("Global options supported:\n");
1207 printf(" --verbose\tEnable verbose output for all commands.\n");
1208 printf("\n");
1209 printf("Commands supported:\n");
1210 printf(" info\t\tList images contained in FIP.\n");
1211 printf(" create\tCreate a new FIP with the given images.\n");
1212 printf(" update\tUpdate an existing FIP with the given images.\n");
1213 printf(" unpack\tUnpack images from FIP.\n");
1214 printf(" remove\tRemove images from FIP.\n");
1215 printf(" version\tShow fiptool version.\n");
1216 printf(" help\t\tShow help for given command.\n");
1217 exit(EXIT_SUCCESS);
1218 }
1219
main(int argc,char * argv[])1220 int main(int argc, char *argv[])
1221 {
1222 int i, ret = 0;
1223
1224 while (1) {
1225 int c, opt_index = 0;
1226 static struct option opts[] = {
1227 { "verbose", no_argument, NULL, 'v' },
1228 { NULL, no_argument, NULL, 0 }
1229 };
1230
1231 /*
1232 * Set POSIX mode so getopt stops at the first non-option
1233 * which is the subcommand.
1234 */
1235 c = getopt_long(argc, argv, "+v", opts, &opt_index);
1236 if (c == -1)
1237 break;
1238
1239 switch (c) {
1240 case 'v':
1241 verbose = 1;
1242 break;
1243 default:
1244 usage();
1245 }
1246 }
1247 argc -= optind;
1248 argv += optind;
1249 /* Reset optind for subsequent getopt processing. */
1250 optind = 0;
1251
1252 if (argc == 0)
1253 usage();
1254
1255 fill_image_descs();
1256 for (i = 0; i < NELEM(cmds); i++) {
1257 if (strcmp(cmds[i].name, argv[0]) == 0) {
1258 ret = cmds[i].handler(argc, argv);
1259 break;
1260 }
1261 }
1262 if (i == NELEM(cmds))
1263 usage();
1264 free_image_descs();
1265 return ret;
1266 }
1267