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