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