blob: 7145815b5a3ec0b2defbd55632ab2c11eef3265a [file] [log] [blame]
dp-arm4972ec52016-05-25 16:20:20 +01001/*
2 * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * Neither the name of ARM nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without specific
16 * prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <sys/types.h>
32#include <sys/stat.h>
33
34#include <assert.h>
35#include <errno.h>
36#include <getopt.h>
37#include <limits.h>
38#include <stdarg.h>
39#include <stdint.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44
dp-arm12e893b2016-08-24 13:21:08 +010045#include <openssl/sha.h>
46
dp-arm4972ec52016-05-25 16:20:20 +010047#include "fiptool.h"
48#include "firmware_image_package.h"
49#include "tbbr_config.h"
50
51#define OPT_TOC_ENTRY 0
52#define OPT_PLAT_TOC_FLAGS 1
53
54static int info_cmd(int argc, char *argv[]);
55static void info_usage(void);
56static int create_cmd(int argc, char *argv[]);
57static void create_usage(void);
58static int update_cmd(int argc, char *argv[]);
59static void update_usage(void);
60static int unpack_cmd(int argc, char *argv[]);
61static void unpack_usage(void);
62static int remove_cmd(int argc, char *argv[]);
63static void remove_usage(void);
64static int version_cmd(int argc, char *argv[]);
65static void version_usage(void);
66static int help_cmd(int argc, char *argv[]);
67static void usage(void);
68
69/* Available subcommands. */
70static cmd_t cmds[] = {
71 { .name = "info", .handler = info_cmd, .usage = info_usage },
72 { .name = "create", .handler = create_cmd, .usage = create_usage },
73 { .name = "update", .handler = update_cmd, .usage = update_usage },
74 { .name = "unpack", .handler = unpack_cmd, .usage = unpack_usage },
75 { .name = "remove", .handler = remove_cmd, .usage = remove_usage },
76 { .name = "version", .handler = version_cmd, .usage = version_usage },
77 { .name = "help", .handler = help_cmd, .usage = NULL },
78};
79
80static image_t *images[MAX_IMAGES];
81static size_t nr_images;
82static uuid_t uuid_null = { 0 };
83static int verbose;
84
dp-armc1f8e772016-11-04 10:52:25 +000085static void vlog(int prio, const char *msg, va_list ap)
dp-arm4972ec52016-05-25 16:20:20 +010086{
87 char *prefix[] = { "DEBUG", "WARN", "ERROR" };
88
89 fprintf(stderr, "%s: ", prefix[prio]);
90 vfprintf(stderr, msg, ap);
91 fputc('\n', stderr);
92}
93
dp-armc1f8e772016-11-04 10:52:25 +000094static void log_dbgx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010095{
96 va_list ap;
97
98 va_start(ap, msg);
99 vlog(LOG_DBG, msg, ap);
100 va_end(ap);
101}
102
dp-armc1f8e772016-11-04 10:52:25 +0000103static void log_warnx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +0100104{
105 va_list ap;
106
107 va_start(ap, msg);
108 vlog(LOG_WARN, msg, ap);
109 va_end(ap);
110}
111
dp-armc1f8e772016-11-04 10:52:25 +0000112static void log_err(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +0100113{
114 char buf[512];
115 va_list ap;
116
117 va_start(ap, msg);
118 snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
119 vlog(LOG_ERR, buf, ap);
120 va_end(ap);
121 exit(1);
122}
123
dp-armc1f8e772016-11-04 10:52:25 +0000124static void log_errx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +0100125{
126 va_list ap;
127
128 va_start(ap, msg);
129 vlog(LOG_ERR, msg, ap);
130 va_end(ap);
131 exit(1);
132}
133
dp-armdb0f5e92016-11-04 10:56:25 +0000134static char *xstrdup(const char *s, const char *msg)
135{
136 char *d;
137
138 d = strdup(s);
139 if (d == NULL)
140 log_errx("strdup: ", msg);
141 return d;
142}
143
144static void *xmalloc(size_t size, const char *msg)
145{
146 void *d;
147
148 d = malloc(size);
149 if (d == NULL)
150 log_errx("malloc: ", msg);
151 return d;
152}
153
dp-arm4972ec52016-05-25 16:20:20 +0100154static void add_image(image_t *image)
155{
156 if (nr_images + 1 > MAX_IMAGES)
157 log_errx("Too many images");
158 images[nr_images++] = image;
159}
160
161static void free_image(image_t *image)
162{
163 free(image->buffer);
164 free(image);
165}
166
167static void replace_image(image_t *image_dst, image_t *image_src)
168{
169 int i;
170
171 for (i = 0; i < nr_images; i++) {
172 if (images[i] == image_dst) {
173 free_image(images[i]);
174 images[i] = image_src;
175 break;
176 }
177 }
178 assert(i != nr_images);
179}
180
181static void remove_image(image_t *image)
182{
183 int i;
184
185 for (i = 0; i < nr_images; i++) {
186 if (images[i] == image) {
187 free_image(images[i]);
188 images[i] = NULL;
189 break;
190 }
191 }
192 assert(i != nr_images);
193
194 /* Compact array. */
195 memmove(&images[i], &images[i + 1],
196 (nr_images - i - 1) * sizeof(*images));
197 nr_images--;
198}
199
200static void free_images(void)
201{
202 int i;
203
204 for (i = 0; i < nr_images; i++) {
205 free_image(images[i]);
206 images[i] = NULL;
207 }
208}
209
dp-arm715ef422016-08-30 14:18:58 +0100210static toc_entry_t *lookup_entry_from_uuid(uuid_t *uuid)
dp-arm4972ec52016-05-25 16:20:20 +0100211{
212 toc_entry_t *toc_entry = toc_entries;
213
214 for (; toc_entry->cmdline_name != NULL; toc_entry++)
215 if (memcmp(&toc_entry->uuid, uuid, sizeof(uuid_t)) == 0)
216 return toc_entry;
217 return NULL;
218}
219
dp-arm715ef422016-08-30 14:18:58 +0100220static image_t *lookup_image_from_uuid(uuid_t *uuid)
221{
222 image_t *image;
223 int i;
224
225 for (i = 0; i < nr_images; i++) {
226 image = images[i];
227 if (memcmp(&image->uuid, uuid, sizeof(uuid_t)) == 0)
228 return image;
229 }
230 return NULL;
231}
232
dp-armc1f8e772016-11-04 10:52:25 +0000233static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
dp-arm4972ec52016-05-25 16:20:20 +0100234{
235 struct stat st;
236 FILE *fp;
237 char *buf, *bufend;
238 fip_toc_header_t *toc_header;
239 fip_toc_entry_t *toc_entry;
240 image_t *image;
241 int terminated = 0;
242
243 fp = fopen(filename, "r");
244 if (fp == NULL)
245 log_err("fopen %s", filename);
246
247 if (fstat(fileno(fp), &st) == -1)
248 log_err("fstat %s", filename);
249
dp-armdb0f5e92016-11-04 10:56:25 +0000250 buf = xmalloc(st.st_size, "failed to load file into memory");
dp-arm4972ec52016-05-25 16:20:20 +0100251 if (fread(buf, 1, st.st_size, fp) != st.st_size)
252 log_errx("Failed to read %s", filename);
253 bufend = buf + st.st_size;
254 fclose(fp);
255
256 if (st.st_size < sizeof(fip_toc_header_t))
257 log_errx("FIP %s is truncated", filename);
258
259 toc_header = (fip_toc_header_t *)buf;
260 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
261
262 if (toc_header->name != TOC_HEADER_NAME)
263 log_errx("%s is not a FIP file", filename);
264
265 /* Return the ToC header if the caller wants it. */
266 if (toc_header_out != NULL)
267 *toc_header_out = *toc_header;
268
269 /* Walk through each ToC entry in the file. */
270 while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
271 /* Found the ToC terminator, we are done. */
272 if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
273 terminated = 1;
274 break;
275 }
276
277 /*
278 * Build a new image out of the ToC entry and add it to the
279 * table of images.
280 */
dp-armdb0f5e92016-11-04 10:56:25 +0000281 image = xmalloc(sizeof(*image),
282 "failed to allocate memory for image");
dp-arm4972ec52016-05-25 16:20:20 +0100283 memcpy(&image->uuid, &toc_entry->uuid, sizeof(uuid_t));
dp-armdb0f5e92016-11-04 10:56:25 +0000284 image->buffer = xmalloc(toc_entry->size,
285 "failed to allocate image buffer, is FIP file corrupted?");
dp-arm4972ec52016-05-25 16:20:20 +0100286 /* Overflow checks before memory copy. */
287 if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
288 log_errx("FIP %s is corrupted", filename);
289 if (toc_entry->size + toc_entry->offset_address > st.st_size)
290 log_errx("FIP %s is corrupted", filename);
291
292 memcpy(image->buffer, buf + toc_entry->offset_address,
293 toc_entry->size);
294 image->size = toc_entry->size;
295
dp-arm4972ec52016-05-25 16:20:20 +0100296 add_image(image);
297
298 toc_entry++;
299 }
300
301 if (terminated == 0)
302 log_errx("FIP %s does not have a ToC terminator entry",
303 filename);
304 free(buf);
305 return 0;
306}
307
dp-armc1f8e772016-11-04 10:52:25 +0000308static image_t *read_image_from_file(const uuid_t *uuid, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100309{
310 struct stat st;
311 image_t *image;
312 FILE *fp;
313
dp-arm715ef422016-08-30 14:18:58 +0100314 assert(uuid != NULL);
315
dp-arm4972ec52016-05-25 16:20:20 +0100316 fp = fopen(filename, "r");
317 if (fp == NULL)
318 log_err("fopen %s", filename);
319
320 if (fstat(fileno(fp), &st) == -1)
321 log_errx("fstat %s", filename);
322
dp-armdb0f5e92016-11-04 10:56:25 +0000323 image = xmalloc(sizeof(*image), "failed to allocate memory for image");
dp-arm715ef422016-08-30 14:18:58 +0100324 memcpy(&image->uuid, uuid, sizeof(uuid_t));
dp-armdb0f5e92016-11-04 10:56:25 +0000325 image->buffer = xmalloc(st.st_size, "failed to allocate image buffer");
dp-arm4972ec52016-05-25 16:20:20 +0100326 if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
327 log_errx("Failed to read %s", filename);
328 image->size = st.st_size;
dp-arm4972ec52016-05-25 16:20:20 +0100329
330 fclose(fp);
331 return image;
332}
333
dp-armc1f8e772016-11-04 10:52:25 +0000334static int write_image_to_file(const image_t *image, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100335{
336 FILE *fp;
337
338 fp = fopen(filename, "w");
339 if (fp == NULL)
340 log_err("fopen");
341 if (fwrite(image->buffer, 1, image->size, fp) != image->size)
342 log_errx("Failed to write %s", filename);
343 fclose(fp);
344 return 0;
345}
346
347static int fill_common_opts(struct option *opts, int has_arg)
348{
349 int i;
350
351 for (i = 0; toc_entries[i].cmdline_name != NULL; i++) {
352 opts[i].name = toc_entries[i].cmdline_name;
353 opts[i].has_arg = has_arg;
354 opts[i].flag = NULL;
355 opts[i].val = 0;
356 }
357 return i;
358}
359
360static void add_opt(struct option *opts, int idx, char *name,
361 int has_arg, int val)
362{
363 opts[idx].name = name;
364 opts[idx].has_arg = has_arg;
365 opts[idx].flag = NULL;
366 opts[idx].val = val;
367}
368
dp-arm12e893b2016-08-24 13:21:08 +0100369static void md_print(unsigned char *md, size_t len)
370{
371 size_t i;
372
373 for (i = 0; i < len; i++)
374 printf("%02x", md[i]);
375}
376
dp-arm4972ec52016-05-25 16:20:20 +0100377static int info_cmd(int argc, char *argv[])
378{
379 image_t *image;
380 uint64_t image_offset;
381 uint64_t image_size = 0;
382 fip_toc_header_t toc_header;
383 int i;
384
385 if (argc != 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100386 info_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100387 argc--, argv++;
388
389 parse_fip(argv[0], &toc_header);
390
391 if (verbose) {
392 log_dbgx("toc_header[name]: 0x%llX",
393 (unsigned long long)toc_header.name);
394 log_dbgx("toc_header[serial_number]: 0x%llX",
395 (unsigned long long)toc_header.serial_number);
396 log_dbgx("toc_header[flags]: 0x%llX",
397 (unsigned long long)toc_header.flags);
398 }
399
400 image_offset = sizeof(fip_toc_header_t) +
401 (sizeof(fip_toc_entry_t) * (nr_images + 1));
402
403 for (i = 0; i < nr_images; i++) {
dp-arm715ef422016-08-30 14:18:58 +0100404 toc_entry_t *toc_entry;
405
dp-arm4972ec52016-05-25 16:20:20 +0100406 image = images[i];
dp-arm715ef422016-08-30 14:18:58 +0100407 toc_entry = lookup_entry_from_uuid(&image->uuid);
408 if (toc_entry != NULL)
409 printf("%s: ", toc_entry->name);
dp-arm4972ec52016-05-25 16:20:20 +0100410 else
411 printf("Unknown entry: ");
412 image_size = image->size;
413 printf("offset=0x%llX, size=0x%llX",
414 (unsigned long long)image_offset,
415 (unsigned long long)image_size);
dp-arm715ef422016-08-30 14:18:58 +0100416 if (toc_entry != NULL)
dp-arm12e893b2016-08-24 13:21:08 +0100417 printf(", cmdline=\"--%s\"",
dp-arm715ef422016-08-30 14:18:58 +0100418 toc_entry->cmdline_name);
dp-arm12e893b2016-08-24 13:21:08 +0100419 if (verbose) {
420 unsigned char md[SHA256_DIGEST_LENGTH];
421
422 SHA256(image->buffer, image_size, md);
423 printf(", sha256=");
424 md_print(md, sizeof(md));
425 }
426 putchar('\n');
dp-arm4972ec52016-05-25 16:20:20 +0100427 image_offset += image_size;
428 }
429
430 free_images();
431 return 0;
432}
433
434static void info_usage(void)
435{
436 printf("fiptool info FIP_FILENAME\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100437 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100438}
439
440static int pack_images(char *filename, uint64_t toc_flags)
441{
442 FILE *fp;
443 image_t *image;
444 fip_toc_header_t *toc_header;
445 fip_toc_entry_t *toc_entry;
446 char *buf;
447 uint64_t entry_offset, buf_size, payload_size;
448 int i;
449
450 /* Calculate total payload size and allocate scratch buffer. */
451 payload_size = 0;
452 for (i = 0; i < nr_images; i++)
453 payload_size += images[i]->size;
454
455 buf_size = sizeof(fip_toc_header_t) +
456 sizeof(fip_toc_entry_t) * (nr_images + 1);
457 buf = calloc(1, buf_size);
458 if (buf == NULL)
459 log_err("calloc");
460
461 /* Build up header and ToC entries from the image table. */
462 toc_header = (fip_toc_header_t *)buf;
463 toc_header->name = TOC_HEADER_NAME;
464 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
465 toc_header->flags = toc_flags;
466
467 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
468
469 entry_offset = buf_size;
470 for (i = 0; i < nr_images; i++) {
471 image = images[i];
472 memcpy(&toc_entry->uuid, &image->uuid, sizeof(uuid_t));
473 toc_entry->offset_address = entry_offset;
474 toc_entry->size = image->size;
475 toc_entry->flags = 0;
476 entry_offset += toc_entry->size;
477 toc_entry++;
478 }
479
480 /* Append a null uuid entry to mark the end of ToC entries. */
481 memcpy(&toc_entry->uuid, &uuid_null, sizeof(uuid_t));
482 toc_entry->offset_address = entry_offset;
483 toc_entry->size = 0;
484 toc_entry->flags = 0;
485
486 /* Generate the FIP file. */
487 fp = fopen(filename, "w");
488 if (fp == NULL)
489 log_err("fopen %s", filename);
490
491 if (verbose)
492 log_dbgx("Metadata size: %zu bytes", buf_size);
493
494 if (fwrite(buf, 1, buf_size, fp) != buf_size)
495 log_errx("Failed to write image to %s", filename);
496 free(buf);
497
498 if (verbose)
499 log_dbgx("Payload size: %zu bytes", payload_size);
500
501 for (i = 0; i < nr_images; i++) {
502 image = images[i];
503 if (fwrite(image->buffer, 1, image->size, fp) != image->size)
504 log_errx("Failed to write image to %s", filename);
505 }
506
507 fclose(fp);
508 return 0;
509}
510
511/*
512 * This function is shared between the create and update subcommands.
513 * The difference between the two subcommands is that when the FIP file
514 * is created, the parsing of an existing FIP is skipped. This results
515 * in update_fip() creating the new FIP file from scratch because the
516 * internal image table is not populated.
517 */
518static void update_fip(void)
519{
520 toc_entry_t *toc_entry;
dp-arm715ef422016-08-30 14:18:58 +0100521 image_t *new_image, *old_image;
dp-arm4972ec52016-05-25 16:20:20 +0100522
523 /* Add or replace images in the FIP file. */
524 for (toc_entry = toc_entries;
525 toc_entry->cmdline_name != NULL;
526 toc_entry++) {
527 if (toc_entry->action != DO_PACK)
528 continue;
529
dp-arm715ef422016-08-30 14:18:58 +0100530 new_image = read_image_from_file(&toc_entry->uuid,
531 toc_entry->action_arg);
532 old_image = lookup_image_from_uuid(&toc_entry->uuid);
533 if (old_image != NULL) {
dp-arm4972ec52016-05-25 16:20:20 +0100534 if (verbose)
535 log_dbgx("Replacing image %s.bin with %s",
536 toc_entry->cmdline_name,
537 toc_entry->action_arg);
dp-arm715ef422016-08-30 14:18:58 +0100538 replace_image(old_image, new_image);
dp-arm4972ec52016-05-25 16:20:20 +0100539 } else {
540 if (verbose)
541 log_dbgx("Adding image %s",
542 toc_entry->action_arg);
dp-arm715ef422016-08-30 14:18:58 +0100543 add_image(new_image);
dp-arm4972ec52016-05-25 16:20:20 +0100544 }
dp-arm4972ec52016-05-25 16:20:20 +0100545
546 free(toc_entry->action_arg);
547 toc_entry->action_arg = NULL;
548 }
549}
550
551static void parse_plat_toc_flags(char *arg, unsigned long long *toc_flags)
552{
553 unsigned long long flags;
554 char *endptr;
555
556 errno = 0;
557 flags = strtoull(arg, &endptr, 16);
558 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
559 log_errx("Invalid platform ToC flags: %s", arg);
560 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
561 *toc_flags |= flags << 32;
562}
563
564static int create_cmd(int argc, char *argv[])
565{
566 struct option opts[toc_entries_len + 1];
567 unsigned long long toc_flags = 0;
568 int i;
569
570 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100571 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100572
573 i = fill_common_opts(opts, required_argument);
574 add_opt(opts, i, "plat-toc-flags", required_argument,
575 OPT_PLAT_TOC_FLAGS);
576 add_opt(opts, ++i, NULL, 0, 0);
577
578 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000579 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100580
dp-armc1f8e772016-11-04 10:52:25 +0000581 c = getopt_long(argc, argv, "", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100582 if (c == -1)
583 break;
584
585 switch (c) {
586 case OPT_TOC_ENTRY: {
587 toc_entry_t *toc_entry;
588
589 toc_entry = &toc_entries[opt_index];
590 toc_entry->action = DO_PACK;
dp-armdb0f5e92016-11-04 10:56:25 +0000591 toc_entry->action_arg = xstrdup(optarg,
592 "failed to allocate memory for argument");
dp-arm4972ec52016-05-25 16:20:20 +0100593 break;
594 }
595 case OPT_PLAT_TOC_FLAGS:
596 parse_plat_toc_flags(optarg, &toc_flags);
597 break;
598 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100599 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100600 }
601 }
602 argc -= optind;
603 argv += optind;
604
605 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100606 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100607
608 update_fip();
609
610 pack_images(argv[0], toc_flags);
611 free_images();
612 return 0;
613}
614
615static void create_usage(void)
616{
617 toc_entry_t *toc_entry = toc_entries;
618
dp-arm1b434b02016-08-23 14:31:41 +0100619 printf("fiptool create [--plat-toc-flags <value>] [opts] FIP_FILENAME\n");
dp-arm4972ec52016-05-25 16:20:20 +0100620 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field "
621 "occupying bits 32-47 in 64-bit ToC header.\n");
622 fputc('\n', stderr);
623 printf("Specific images are packed with the following options:\n");
624 for (; toc_entry->cmdline_name != NULL; toc_entry++)
625 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
626 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100627 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100628}
629
630static int update_cmd(int argc, char *argv[])
631{
632 struct option opts[toc_entries_len + 2];
633 char outfile[FILENAME_MAX] = { 0 };
634 fip_toc_header_t toc_header = { 0 };
635 unsigned long long toc_flags = 0;
636 int pflag = 0;
637 int i;
638
639 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100640 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100641
642 i = fill_common_opts(opts, required_argument);
643 add_opt(opts, i, "out", required_argument, 'o');
644 add_opt(opts, ++i, "plat-toc-flags", required_argument,
645 OPT_PLAT_TOC_FLAGS);
646 add_opt(opts, ++i, NULL, 0, 0);
647
648 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000649 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100650
651 c = getopt_long(argc, argv, "o:", opts, &opt_index);
652 if (c == -1)
653 break;
654
655 switch (c) {
656 case OPT_TOC_ENTRY: {
657 toc_entry_t *toc_entry;
658
659 toc_entry = &toc_entries[opt_index];
660 toc_entry->action = DO_PACK;
dp-armdb0f5e92016-11-04 10:56:25 +0000661 toc_entry->action_arg = xstrdup(optarg,
662 "failed to allocate memory for argument");
dp-arm4972ec52016-05-25 16:20:20 +0100663 break;
664 }
665 case OPT_PLAT_TOC_FLAGS: {
666 parse_plat_toc_flags(optarg, &toc_flags);
667 pflag = 1;
668 break;
669 }
670 case 'o':
671 snprintf(outfile, sizeof(outfile), "%s", optarg);
672 break;
673 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100674 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100675 }
676 }
677 argc -= optind;
678 argv += optind;
679
680 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100681 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100682
683 if (outfile[0] == '\0')
684 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
685
686 if (access(outfile, F_OK) == 0)
687 parse_fip(argv[0], &toc_header);
688
689 if (pflag)
690 toc_header.flags &= ~(0xffffULL << 32);
691 toc_flags = (toc_header.flags |= toc_flags);
692
693 update_fip();
694
695 pack_images(outfile, toc_flags);
696 free_images();
697 return 0;
698}
699
700static void update_usage(void)
701{
702 toc_entry_t *toc_entry = toc_entries;
703
dp-arm1b434b02016-08-23 14:31:41 +0100704 printf("fiptool update [--out FIP_FILENAME] "
dp-arm4972ec52016-05-25 16:20:20 +0100705 "[--plat-toc-flags <value>] [opts] FIP_FILENAME\n");
706 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
707 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field "
708 "occupying bits 32-47 in 64-bit ToC header.\n");
709 fputc('\n', stderr);
710 printf("Specific images are packed with the following options:\n");
711 for (; toc_entry->cmdline_name != NULL; toc_entry++)
712 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
713 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100714 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100715}
716
717static int unpack_cmd(int argc, char *argv[])
718{
719 struct option opts[toc_entries_len + 3];
720 char file[FILENAME_MAX], outdir[PATH_MAX] = { 0 };
721 toc_entry_t *toc_entry;
722 int fflag = 0;
723 int unpack_all = 1;
724 int i;
725
726 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100727 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100728
729 i = fill_common_opts(opts, required_argument);
730 add_opt(opts, i, "force", no_argument, 'f');
731 add_opt(opts, ++i, "out", required_argument, 'o');
732 add_opt(opts, ++i, NULL, 0, 0);
733
734 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000735 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100736
737 c = getopt_long(argc, argv, "fo:", opts, &opt_index);
738 if (c == -1)
739 break;
740
741 switch (c) {
742 case OPT_TOC_ENTRY:
743 unpack_all = 0;
744 toc_entry = &toc_entries[opt_index];
745 toc_entry->action = DO_UNPACK;
dp-armdb0f5e92016-11-04 10:56:25 +0000746 toc_entry->action_arg = xstrdup(optarg,
747 "failed to allocate memory for argument");
dp-arm4972ec52016-05-25 16:20:20 +0100748 break;
749 case 'f':
750 fflag = 1;
751 break;
752 case 'o':
753 snprintf(outdir, sizeof(outdir), "%s", optarg);
754 break;
755 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100756 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100757 }
758 }
759 argc -= optind;
760 argv += optind;
761
762 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100763 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100764
765 parse_fip(argv[0], NULL);
766
767 if (outdir[0] != '\0')
768 if (chdir(outdir) == -1)
769 log_err("chdir %s", outdir);
770
dp-arm4972ec52016-05-25 16:20:20 +0100771 /* Unpack all specified images. */
772 for (toc_entry = toc_entries;
773 toc_entry->cmdline_name != NULL;
774 toc_entry++) {
dp-arm715ef422016-08-30 14:18:58 +0100775 image_t *image;
776
777 if (!unpack_all && toc_entry->action != DO_UNPACK)
dp-arm4972ec52016-05-25 16:20:20 +0100778 continue;
779
780 /* Build filename. */
781 if (toc_entry->action_arg == NULL)
782 snprintf(file, sizeof(file), "%s.bin",
783 toc_entry->cmdline_name);
784 else
785 snprintf(file, sizeof(file), "%s",
786 toc_entry->action_arg);
787
dp-arm715ef422016-08-30 14:18:58 +0100788 image = lookup_image_from_uuid(&toc_entry->uuid);
789 if (image == NULL) {
790 if (!unpack_all)
791 log_warnx("Requested image %s is not in %s",
792 file, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +0100793 free(toc_entry->action_arg);
794 toc_entry->action_arg = NULL;
795 continue;
796 }
797
798 if (access(file, F_OK) != 0 || fflag) {
799 if (verbose)
800 log_dbgx("Unpacking %s", file);
dp-arm715ef422016-08-30 14:18:58 +0100801 write_image_to_file(image, file);
dp-arm4972ec52016-05-25 16:20:20 +0100802 } else {
803 log_warnx("File %s already exists, use --force to overwrite it",
804 file);
805 }
806
807 free(toc_entry->action_arg);
808 toc_entry->action_arg = NULL;
809 }
810
811 free_images();
812 return 0;
813}
814
815static void unpack_usage(void)
816{
817 toc_entry_t *toc_entry = toc_entries;
818
819 printf("fiptool unpack [--force] [--out <path>] [opts] FIP_FILENAME\n");
820 printf(" --force\tIf the output file already exists, use --force to "
821 "overwrite it.\n");
822 printf(" --out path\tSet the output directory path.\n");
823 fputc('\n', stderr);
824 printf("Specific images are unpacked with the following options:\n");
825 for (; toc_entry->cmdline_name != NULL; toc_entry++)
826 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
827 toc_entry->name);
828 fputc('\n', stderr);
829 printf("If no options are provided, all images will be unpacked.\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100830 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100831}
832
833static int remove_cmd(int argc, char *argv[])
834{
835 struct option opts[toc_entries_len + 2];
836 char outfile[FILENAME_MAX] = { 0 };
837 fip_toc_header_t toc_header;
838 toc_entry_t *toc_entry;
839 int fflag = 0;
840 int i;
841
842 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100843 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100844
845 i = fill_common_opts(opts, no_argument);
846 add_opt(opts, i, "force", no_argument, 'f');
847 add_opt(opts, ++i, "out", required_argument, 'o');
848 add_opt(opts, ++i, NULL, 0, 0);
849
850 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000851 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100852
853 c = getopt_long(argc, argv, "fo:", opts, &opt_index);
854 if (c == -1)
855 break;
856
857 switch (c) {
858 case OPT_TOC_ENTRY:
859 toc_entry = &toc_entries[opt_index];
860 toc_entry->action = DO_REMOVE;
861 break;
862 case 'f':
863 fflag = 1;
864 break;
865 case 'o':
866 snprintf(outfile, sizeof(outfile), "%s", optarg);
867 break;
868 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100869 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100870 }
871 }
872 argc -= optind;
873 argv += optind;
874
875 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100876 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100877
878 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
879 log_errx("File %s already exists, use --force to overwrite it",
880 outfile);
881
882 if (outfile[0] == '\0')
883 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
884
885 parse_fip(argv[0], &toc_header);
886
887 for (toc_entry = toc_entries;
888 toc_entry->cmdline_name != NULL;
889 toc_entry++) {
dp-arm715ef422016-08-30 14:18:58 +0100890 image_t *image;
891
dp-arm4972ec52016-05-25 16:20:20 +0100892 if (toc_entry->action != DO_REMOVE)
893 continue;
dp-arm715ef422016-08-30 14:18:58 +0100894 image = lookup_image_from_uuid(&toc_entry->uuid);
895 if (image != NULL) {
dp-arm4972ec52016-05-25 16:20:20 +0100896 if (verbose)
897 log_dbgx("Removing %s.bin",
898 toc_entry->cmdline_name);
dp-arm715ef422016-08-30 14:18:58 +0100899 remove_image(image);
dp-arm4972ec52016-05-25 16:20:20 +0100900 } else {
901 log_warnx("Requested image %s.bin is not in %s",
902 toc_entry->cmdline_name, argv[0]);
903 }
904 }
905
906 pack_images(outfile, toc_header.flags);
907 free_images();
908 return 0;
909}
910
911static void remove_usage(void)
912{
913 toc_entry_t *toc_entry = toc_entries;
914
915 printf("fiptool remove [--force] [--out FIP_FILENAME] [opts] FIP_FILENAME\n");
916 printf(" --force\t\tIf the output FIP file already exists, use --force to "
917 "overwrite it.\n");
918 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
919 fputc('\n', stderr);
920 printf("Specific images are removed with the following options:\n");
921 for (; toc_entry->cmdline_name != NULL; toc_entry++)
922 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
923 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100924 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100925}
926
927static int version_cmd(int argc, char *argv[])
928{
929#ifdef VERSION
930 puts(VERSION);
931#else
932 /* If built from fiptool directory, VERSION is not set. */
933 puts("Unknown version");
934#endif
935 return 0;
936}
937
938static void version_usage(void)
939{
940 printf("fiptool version\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100941 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100942}
943
944static int help_cmd(int argc, char *argv[])
945{
946 int i;
947
948 if (argc < 2)
949 usage();
950 argc--, argv++;
951
952 for (i = 0; i < NELEM(cmds); i++) {
953 if (strcmp(cmds[i].name, argv[0]) == 0 &&
dp-arm29f1b5c2016-09-15 09:58:50 +0100954 cmds[i].usage != NULL)
dp-arm4972ec52016-05-25 16:20:20 +0100955 cmds[i].usage();
dp-arm4972ec52016-05-25 16:20:20 +0100956 }
957 if (i == NELEM(cmds))
958 printf("No help for subcommand '%s'\n", argv[0]);
959 return 0;
960}
961
962static void usage(void)
963{
964 printf("usage: [--verbose] fiptool <command> [<args>]\n");
965 printf("Global options supported:\n");
966 printf(" --verbose\tEnable verbose output for all commands.\n");
967 fputc('\n', stderr);
968 printf("Commands supported:\n");
969 printf(" info\t\tList images contained in FIP.\n");
970 printf(" create\tCreate a new FIP with the given images.\n");
971 printf(" update\tUpdate an existing FIP with the given images.\n");
972 printf(" unpack\tUnpack images from FIP.\n");
973 printf(" remove\tRemove images from FIP.\n");
974 printf(" version\tShow fiptool version.\n");
975 printf(" help\t\tShow help for given command.\n");
976 exit(1);
977}
978
979int main(int argc, char *argv[])
980{
981 int i, ret = 0;
982
dp-arm5cd10ae2016-11-07 10:45:59 +0000983 while (1) {
984 int c, opt_index = 0;
985 static struct option opts[] = {
986 { "verbose", no_argument, NULL, 'v' },
987 { NULL, no_argument, NULL, 0 }
988 };
989
990 /*
991 * Set POSIX mode so getopt stops at the first non-option
992 * which is the subcommand.
993 */
994 c = getopt_long(argc, argv, "+v", opts, &opt_index);
995 if (c == -1)
996 break;
dp-arm4972ec52016-05-25 16:20:20 +0100997
dp-arm5cd10ae2016-11-07 10:45:59 +0000998 switch (c) {
999 case 'v':
1000 verbose = 1;
1001 break;
1002 default:
Masahiro Yamada48a24972016-10-26 13:24:26 +09001003 usage();
dp-arm5cd10ae2016-11-07 10:45:59 +00001004 }
dp-arm4972ec52016-05-25 16:20:20 +01001005 }
dp-arm5cd10ae2016-11-07 10:45:59 +00001006 argc -= optind;
1007 argv += optind;
1008 /* Reset optind for subsequent getopt processing. */
1009 optind = 0;
1010
1011 if (argc == 0)
1012 usage();
dp-arm4972ec52016-05-25 16:20:20 +01001013
1014 for (i = 0; i < NELEM(cmds); i++) {
1015 if (strcmp(cmds[i].name, argv[0]) == 0) {
1016 ret = cmds[i].handler(argc, argv);
1017 break;
1018 }
1019 }
1020 if (i == NELEM(cmds))
1021 usage();
1022 return ret;
1023}