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