blob: 8cf7ecec5dfceb1e639d465a5f8ed80d2655691e [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
Masahiro Yamada4d87eb42016-12-25 13:52:22 +090053#define OPT_ALIGN 2
dp-arm4972ec52016-05-25 16:20:20 +010054
dp-arm90d2f0e2016-11-14 15:54:32 +000055static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid);
56static image_t *lookup_image_from_uuid(const uuid_t *uuid);
dp-arm4972ec52016-05-25 16:20:20 +010057static int info_cmd(int argc, char *argv[]);
58static void info_usage(void);
59static int create_cmd(int argc, char *argv[]);
60static void create_usage(void);
61static int update_cmd(int argc, char *argv[]);
62static void update_usage(void);
63static int unpack_cmd(int argc, char *argv[]);
64static void unpack_usage(void);
65static int remove_cmd(int argc, char *argv[]);
66static void remove_usage(void);
67static int version_cmd(int argc, char *argv[]);
68static void version_usage(void);
69static int help_cmd(int argc, char *argv[]);
70static void usage(void);
71
72/* Available subcommands. */
73static cmd_t cmds[] = {
74 { .name = "info", .handler = info_cmd, .usage = info_usage },
75 { .name = "create", .handler = create_cmd, .usage = create_usage },
76 { .name = "update", .handler = update_cmd, .usage = update_usage },
77 { .name = "unpack", .handler = unpack_cmd, .usage = unpack_usage },
78 { .name = "remove", .handler = remove_cmd, .usage = remove_usage },
79 { .name = "version", .handler = version_cmd, .usage = version_usage },
80 { .name = "help", .handler = help_cmd, .usage = NULL },
81};
82
dp-arm90d2f0e2016-11-14 15:54:32 +000083static image_desc_t *image_desc_head;
84static size_t nr_image_descs;
85static image_t *image_head;
dp-arm4972ec52016-05-25 16:20:20 +010086static size_t nr_images;
87static uuid_t uuid_null = { 0 };
88static int verbose;
89
dp-armc1f8e772016-11-04 10:52:25 +000090static void vlog(int prio, const char *msg, va_list ap)
dp-arm4972ec52016-05-25 16:20:20 +010091{
92 char *prefix[] = { "DEBUG", "WARN", "ERROR" };
93
94 fprintf(stderr, "%s: ", prefix[prio]);
95 vfprintf(stderr, msg, ap);
96 fputc('\n', stderr);
97}
98
dp-armc1f8e772016-11-04 10:52:25 +000099static void log_dbgx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +0100100{
101 va_list ap;
102
103 va_start(ap, msg);
104 vlog(LOG_DBG, msg, ap);
105 va_end(ap);
106}
107
dp-armc1f8e772016-11-04 10:52:25 +0000108static void log_warnx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +0100109{
110 va_list ap;
111
112 va_start(ap, msg);
113 vlog(LOG_WARN, msg, ap);
114 va_end(ap);
115}
116
dp-armc1f8e772016-11-04 10:52:25 +0000117static void log_err(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +0100118{
119 char buf[512];
120 va_list ap;
121
122 va_start(ap, msg);
123 snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
124 vlog(LOG_ERR, buf, ap);
125 va_end(ap);
126 exit(1);
127}
128
dp-armc1f8e772016-11-04 10:52:25 +0000129static void log_errx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +0100130{
131 va_list ap;
132
133 va_start(ap, msg);
134 vlog(LOG_ERR, msg, ap);
135 va_end(ap);
136 exit(1);
137}
138
dp-armdb0f5e92016-11-04 10:56:25 +0000139static char *xstrdup(const char *s, const char *msg)
140{
141 char *d;
142
143 d = strdup(s);
144 if (d == NULL)
dp-arm5ba38542016-12-21 14:59:30 +0000145 log_errx("strdup: %s", msg);
dp-armdb0f5e92016-11-04 10:56:25 +0000146 return d;
147}
148
149static void *xmalloc(size_t size, const char *msg)
150{
151 void *d;
152
153 d = malloc(size);
154 if (d == NULL)
dp-arm5ba38542016-12-21 14:59:30 +0000155 log_errx("malloc: %s", msg);
dp-armdb0f5e92016-11-04 10:56:25 +0000156 return d;
157}
158
Masahiro Yamada03fdf692017-01-15 00:50:41 +0900159static void *xzalloc(size_t size, const char *msg)
160{
161 return memset(xmalloc(size, msg), 0, size);
162}
163
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900164static void xfwrite(void *buf, size_t size, FILE *fp, const char *filename)
165{
166 if (fwrite(buf, 1, size, fp) != size)
167 log_errx("Failed to write %s", filename);
168}
169
dp-arm90d2f0e2016-11-14 15:54:32 +0000170static image_desc_t *new_image_desc(const uuid_t *uuid,
171 const char *name, const char *cmdline_name)
dp-arm4972ec52016-05-25 16:20:20 +0100172{
dp-arm90d2f0e2016-11-14 15:54:32 +0000173 image_desc_t *desc;
174
Masahiro Yamada03fdf692017-01-15 00:50:41 +0900175 desc = xzalloc(sizeof(*desc),
dp-arm90d2f0e2016-11-14 15:54:32 +0000176 "failed to allocate memory for image descriptor");
177 memcpy(&desc->uuid, uuid, sizeof(uuid_t));
178 desc->name = xstrdup(name,
179 "failed to allocate memory for image name");
180 desc->cmdline_name = xstrdup(cmdline_name,
181 "failed to allocate memory for image command line name");
182 desc->action = DO_UNSPEC;
dp-arm90d2f0e2016-11-14 15:54:32 +0000183 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100184}
185
dp-armfb732312016-12-30 09:55:48 +0000186static void set_image_desc_action(image_desc_t *desc, int action,
187 const char *arg)
188{
189 assert(desc != NULL);
190
191 if (desc->action_arg != DO_UNSPEC)
192 free(desc->action_arg);
193 desc->action = action;
194 desc->action_arg = NULL;
195 if (arg != NULL)
196 desc->action_arg = xstrdup(arg,
197 "failed to allocate memory for argument");
198}
199
dp-arm90d2f0e2016-11-14 15:54:32 +0000200static void free_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100201{
dp-arm90d2f0e2016-11-14 15:54:32 +0000202 free(desc->name);
203 free(desc->cmdline_name);
204 free(desc->action_arg);
205 free(desc);
dp-arm4972ec52016-05-25 16:20:20 +0100206}
207
dp-arm90d2f0e2016-11-14 15:54:32 +0000208static void add_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100209{
Masahiro Yamadad224b452017-01-14 23:22:02 +0900210 image_desc_t **p = &image_desc_head;
211
Masahiro Yamadad224b452017-01-14 23:22:02 +0900212 while (*p)
213 p = &(*p)->next;
214
Masahiro Yamadac2a7d9c2017-01-27 12:53:13 +0900215 assert(*p == NULL);
Masahiro Yamadad224b452017-01-14 23:22:02 +0900216 *p = desc;
dp-arm90d2f0e2016-11-14 15:54:32 +0000217 nr_image_descs++;
218}
dp-arm4972ec52016-05-25 16:20:20 +0100219
dp-arm90d2f0e2016-11-14 15:54:32 +0000220static void free_image_descs(void)
221{
222 image_desc_t *desc = image_desc_head, *tmp;
223
224 while (desc != NULL) {
225 tmp = desc->next;
226 free_image_desc(desc);
227 desc = tmp;
228 nr_image_descs--;
dp-arm4972ec52016-05-25 16:20:20 +0100229 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000230 assert(nr_image_descs == 0);
dp-arm4972ec52016-05-25 16:20:20 +0100231}
232
dp-arm90d2f0e2016-11-14 15:54:32 +0000233static void fill_image_descs(void)
234{
235 toc_entry_t *toc_entry;
236
237 for (toc_entry = toc_entries;
238 toc_entry->cmdline_name != NULL;
239 toc_entry++) {
240 image_desc_t *desc;
241
242 desc = new_image_desc(&toc_entry->uuid,
243 toc_entry->name,
244 toc_entry->cmdline_name);
245 add_image_desc(desc);
246 }
247}
248
249static void add_image(image_t *image)
250{
Masahiro Yamadad224b452017-01-14 23:22:02 +0900251 image_t **p = &image_head;
252
Masahiro Yamadad224b452017-01-14 23:22:02 +0900253 while (*p)
254 p = &(*p)->next;
255
Masahiro Yamadac2a7d9c2017-01-27 12:53:13 +0900256 assert(*p == NULL);
Masahiro Yamadad224b452017-01-14 23:22:02 +0900257 *p = image;
258
dp-arm90d2f0e2016-11-14 15:54:32 +0000259 nr_images++;
260}
261
Masahiro Yamadad65e7fd2017-01-27 11:57:54 +0900262static void replace_image(image_t *image)
263{
264 image_t **p = &image_head;
265
266 while (*p) {
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900267 if (!memcmp(&(*p)->toc_e.uuid, &image->toc_e.uuid,
268 sizeof(image->toc_e.uuid)))
Masahiro Yamadad65e7fd2017-01-27 11:57:54 +0900269 break;
270 p = &(*p)->next;
271 }
272
273 assert(*p != NULL);
274
275 image->next = (*p)->next;
276 *p = image;
277}
278
dp-arm90d2f0e2016-11-14 15:54:32 +0000279static void free_image(image_t *image)
280{
281 free(image->buffer);
282 free(image);
283}
284
dp-arm4972ec52016-05-25 16:20:20 +0100285static void remove_image(image_t *image)
286{
Masahiro Yamadac0d2d982017-01-27 13:31:40 +0900287 image_t *tmp, **p = &image_head;
dp-arm4972ec52016-05-25 16:20:20 +0100288
Masahiro Yamadac0d2d982017-01-27 13:31:40 +0900289 while (*p) {
290 if (*p == image)
291 break;
292 p = &(*p)->next;
dp-arm4972ec52016-05-25 16:20:20 +0100293 }
Masahiro Yamadac0d2d982017-01-27 13:31:40 +0900294
295 assert(*p != NULL);
296
297 tmp = *p;
298 *p = tmp->next;
299 free_image(tmp);
300
dp-arm4972ec52016-05-25 16:20:20 +0100301 nr_images--;
302}
303
304static void free_images(void)
305{
dp-arm90d2f0e2016-11-14 15:54:32 +0000306 image_t *image = image_head, *tmp;
dp-arm4972ec52016-05-25 16:20:20 +0100307
dp-arm90d2f0e2016-11-14 15:54:32 +0000308 while (image != NULL) {
309 tmp = image->next;
310 free_image(image);
311 image = tmp;
312 nr_images--;
dp-arm4972ec52016-05-25 16:20:20 +0100313 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000314 assert(nr_images == 0);
dp-arm4972ec52016-05-25 16:20:20 +0100315}
316
dp-arm90d2f0e2016-11-14 15:54:32 +0000317static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid)
dp-arm4972ec52016-05-25 16:20:20 +0100318{
dp-arm90d2f0e2016-11-14 15:54:32 +0000319 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100320
dp-arm90d2f0e2016-11-14 15:54:32 +0000321 for (desc = image_desc_head; desc != NULL; desc = desc->next)
322 if (memcmp(&desc->uuid, uuid, sizeof(uuid_t)) == 0)
323 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100324 return NULL;
325}
326
dp-arm90d2f0e2016-11-14 15:54:32 +0000327static image_desc_t *lookup_image_desc_from_opt(const char *opt)
328{
329 image_desc_t *desc;
330
331 for (desc = image_desc_head; desc != NULL; desc = desc->next)
332 if (strcmp(desc->cmdline_name, opt) == 0)
333 return desc;
334 return NULL;
335}
336
337static image_t *lookup_image_from_uuid(const uuid_t *uuid)
dp-arm715ef422016-08-30 14:18:58 +0100338{
339 image_t *image;
dp-arm715ef422016-08-30 14:18:58 +0100340
dp-arm90d2f0e2016-11-14 15:54:32 +0000341 for (image = image_head; image != NULL; image = image->next)
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900342 if (!memcmp(&image->toc_e.uuid, uuid, sizeof(*uuid)))
dp-arm715ef422016-08-30 14:18:58 +0100343 return image;
dp-arm715ef422016-08-30 14:18:58 +0100344 return NULL;
345}
346
dp-arm516dfcb2016-11-03 13:59:26 +0000347static void uuid_to_str(char *s, size_t len, const uuid_t *u)
348{
349 assert(len >= (_UUID_STR_LEN + 1));
350
351 snprintf(s, len, "%08X-%04X-%04X-%04X-%04X%04X%04X",
352 u->time_low,
353 u->time_mid,
354 u->time_hi_and_version,
355 ((uint16_t)u->clock_seq_hi_and_reserved << 8) | u->clock_seq_low,
356 ((uint16_t)u->node[0] << 8) | u->node[1],
357 ((uint16_t)u->node[2] << 8) | u->node[3],
358 ((uint16_t)u->node[4] << 8) | u->node[5]);
359}
360
361static void uuid_from_str(uuid_t *u, const char *s)
362{
363 int n;
364
365 if (s == NULL)
366 log_errx("UUID cannot be NULL");
367 if (strlen(s) != _UUID_STR_LEN)
368 log_errx("Invalid UUID: %s", s);
369
370 n = sscanf(s,
371 "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
372 &u->time_low, &u->time_mid, &u->time_hi_and_version,
373 &u->clock_seq_hi_and_reserved, &u->clock_seq_low, &u->node[0],
374 &u->node[1], &u->node[2], &u->node[3], &u->node[4], &u->node[5]);
375 /*
376 * Given the format specifier above, we expect 11 items to be scanned
377 * for a properly formatted UUID.
378 */
379 if (n != 11)
380 log_errx("Invalid UUID: %s", s);
381}
382
dp-armc1f8e772016-11-04 10:52:25 +0000383static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
dp-arm4972ec52016-05-25 16:20:20 +0100384{
385 struct stat st;
386 FILE *fp;
387 char *buf, *bufend;
388 fip_toc_header_t *toc_header;
389 fip_toc_entry_t *toc_entry;
dp-arm4972ec52016-05-25 16:20:20 +0100390 int terminated = 0;
391
392 fp = fopen(filename, "r");
393 if (fp == NULL)
394 log_err("fopen %s", filename);
395
396 if (fstat(fileno(fp), &st) == -1)
397 log_err("fstat %s", filename);
398
dp-armdb0f5e92016-11-04 10:56:25 +0000399 buf = xmalloc(st.st_size, "failed to load file into memory");
dp-arm4972ec52016-05-25 16:20:20 +0100400 if (fread(buf, 1, st.st_size, fp) != st.st_size)
401 log_errx("Failed to read %s", filename);
402 bufend = buf + st.st_size;
403 fclose(fp);
404
405 if (st.st_size < sizeof(fip_toc_header_t))
406 log_errx("FIP %s is truncated", filename);
407
408 toc_header = (fip_toc_header_t *)buf;
409 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
410
411 if (toc_header->name != TOC_HEADER_NAME)
412 log_errx("%s is not a FIP file", filename);
413
414 /* Return the ToC header if the caller wants it. */
415 if (toc_header_out != NULL)
416 *toc_header_out = *toc_header;
417
418 /* Walk through each ToC entry in the file. */
419 while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
dp-arm516dfcb2016-11-03 13:59:26 +0000420 image_t *image;
421 image_desc_t *desc;
422
dp-arm4972ec52016-05-25 16:20:20 +0100423 /* Found the ToC terminator, we are done. */
424 if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
425 terminated = 1;
426 break;
427 }
428
429 /*
430 * Build a new image out of the ToC entry and add it to the
431 * table of images.
432 */
Masahiro Yamadad224b452017-01-14 23:22:02 +0900433 image = xzalloc(sizeof(*image),
dp-armdb0f5e92016-11-04 10:56:25 +0000434 "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900435 image->toc_e = *toc_entry;
dp-armdb0f5e92016-11-04 10:56:25 +0000436 image->buffer = xmalloc(toc_entry->size,
437 "failed to allocate image buffer, is FIP file corrupted?");
dp-arm4972ec52016-05-25 16:20:20 +0100438 /* Overflow checks before memory copy. */
439 if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
440 log_errx("FIP %s is corrupted", filename);
441 if (toc_entry->size + toc_entry->offset_address > st.st_size)
442 log_errx("FIP %s is corrupted", filename);
443
444 memcpy(image->buffer, buf + toc_entry->offset_address,
445 toc_entry->size);
dp-arm4972ec52016-05-25 16:20:20 +0100446
dp-arm516dfcb2016-11-03 13:59:26 +0000447 /* If this is an unknown image, create a descriptor for it. */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900448 desc = lookup_image_desc_from_uuid(&toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000449 if (desc == NULL) {
450 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
451
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900452 uuid_to_str(name, sizeof(name), &toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000453 snprintf(filename, sizeof(filename), "%s%s",
454 name, ".bin");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900455 desc = new_image_desc(&toc_entry->uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000456 desc->action = DO_UNPACK;
457 desc->action_arg = xstrdup(filename,
458 "failed to allocate memory for blob filename");
459 add_image_desc(desc);
460 }
461
dp-arm4972ec52016-05-25 16:20:20 +0100462 add_image(image);
463
464 toc_entry++;
465 }
466
467 if (terminated == 0)
468 log_errx("FIP %s does not have a ToC terminator entry",
469 filename);
470 free(buf);
471 return 0;
472}
473
dp-armc1f8e772016-11-04 10:52:25 +0000474static image_t *read_image_from_file(const uuid_t *uuid, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100475{
476 struct stat st;
477 image_t *image;
478 FILE *fp;
479
dp-arm715ef422016-08-30 14:18:58 +0100480 assert(uuid != NULL);
481
dp-arm4972ec52016-05-25 16:20:20 +0100482 fp = fopen(filename, "r");
483 if (fp == NULL)
484 log_err("fopen %s", filename);
485
486 if (fstat(fileno(fp), &st) == -1)
487 log_errx("fstat %s", filename);
488
Masahiro Yamadad224b452017-01-14 23:22:02 +0900489 image = xzalloc(sizeof(*image), "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900490 image->toc_e.uuid = *uuid;
dp-armdb0f5e92016-11-04 10:56:25 +0000491 image->buffer = xmalloc(st.st_size, "failed to allocate image buffer");
dp-arm4972ec52016-05-25 16:20:20 +0100492 if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
493 log_errx("Failed to read %s", filename);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900494 image->toc_e.size = st.st_size;
dp-arm4972ec52016-05-25 16:20:20 +0100495
496 fclose(fp);
497 return image;
498}
499
dp-armc1f8e772016-11-04 10:52:25 +0000500static int write_image_to_file(const image_t *image, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100501{
502 FILE *fp;
503
504 fp = fopen(filename, "w");
505 if (fp == NULL)
506 log_err("fopen");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900507 xfwrite(image->buffer, image->toc_e.size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100508 fclose(fp);
509 return 0;
510}
511
dp-arm90d2f0e2016-11-14 15:54:32 +0000512static struct option *add_opt(struct option *opts, size_t *nr_opts,
513 const char *name, int has_arg, int val)
dp-arm4972ec52016-05-25 16:20:20 +0100514{
dp-arm90d2f0e2016-11-14 15:54:32 +0000515 opts = realloc(opts, (*nr_opts + 1) * sizeof(*opts));
516 if (opts == NULL)
517 log_err("realloc");
518 opts[*nr_opts].name = name;
519 opts[*nr_opts].has_arg = has_arg;
520 opts[*nr_opts].flag = NULL;
521 opts[*nr_opts].val = val;
522 ++*nr_opts;
523 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100524}
525
dp-arm90d2f0e2016-11-14 15:54:32 +0000526static struct option *fill_common_opts(struct option *opts, size_t *nr_opts,
527 int has_arg)
dp-arm4972ec52016-05-25 16:20:20 +0100528{
dp-arm90d2f0e2016-11-14 15:54:32 +0000529 image_desc_t *desc;
530
531 for (desc = image_desc_head; desc != NULL; desc = desc->next)
532 opts = add_opt(opts, nr_opts, desc->cmdline_name, has_arg,
533 OPT_TOC_ENTRY);
534 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100535}
536
dp-arm90d2f0e2016-11-14 15:54:32 +0000537static void md_print(const unsigned char *md, size_t len)
dp-arm12e893b2016-08-24 13:21:08 +0100538{
539 size_t i;
540
541 for (i = 0; i < len; i++)
542 printf("%02x", md[i]);
543}
544
dp-arm4972ec52016-05-25 16:20:20 +0100545static int info_cmd(int argc, char *argv[])
546{
547 image_t *image;
dp-arm4972ec52016-05-25 16:20:20 +0100548 fip_toc_header_t toc_header;
dp-arm4972ec52016-05-25 16:20:20 +0100549
550 if (argc != 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100551 info_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100552 argc--, argv++;
553
554 parse_fip(argv[0], &toc_header);
555
556 if (verbose) {
557 log_dbgx("toc_header[name]: 0x%llX",
558 (unsigned long long)toc_header.name);
559 log_dbgx("toc_header[serial_number]: 0x%llX",
560 (unsigned long long)toc_header.serial_number);
561 log_dbgx("toc_header[flags]: 0x%llX",
562 (unsigned long long)toc_header.flags);
563 }
564
dp-arm90d2f0e2016-11-14 15:54:32 +0000565 for (image = image_head; image != NULL; image = image->next) {
566 image_desc_t *desc;
dp-arm715ef422016-08-30 14:18:58 +0100567
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900568 desc = lookup_image_desc_from_uuid(&image->toc_e.uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000569 assert(desc != NULL);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900570 printf("%s: offset=0x%llX, size=0x%llX, cmdline=\"--%s\"",
571 desc->name,
572 (unsigned long long)image->toc_e.offset_address,
573 (unsigned long long)image->toc_e.size,
Masahiro Yamadaeee39312017-01-15 23:20:00 +0900574 desc->cmdline_name);
dp-arm12e893b2016-08-24 13:21:08 +0100575 if (verbose) {
576 unsigned char md[SHA256_DIGEST_LENGTH];
577
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900578 SHA256(image->buffer, image->toc_e.size, md);
dp-arm12e893b2016-08-24 13:21:08 +0100579 printf(", sha256=");
580 md_print(md, sizeof(md));
581 }
582 putchar('\n');
dp-arm4972ec52016-05-25 16:20:20 +0100583 }
584
585 free_images();
586 return 0;
587}
588
589static void info_usage(void)
590{
591 printf("fiptool info FIP_FILENAME\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100592 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100593}
594
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900595static int pack_images(const char *filename, uint64_t toc_flags, unsigned long align)
dp-arm4972ec52016-05-25 16:20:20 +0100596{
597 FILE *fp;
598 image_t *image;
599 fip_toc_header_t *toc_header;
600 fip_toc_entry_t *toc_entry;
601 char *buf;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900602 uint64_t entry_offset, buf_size, payload_size = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100603
604 buf_size = sizeof(fip_toc_header_t) +
605 sizeof(fip_toc_entry_t) * (nr_images + 1);
606 buf = calloc(1, buf_size);
607 if (buf == NULL)
608 log_err("calloc");
609
610 /* Build up header and ToC entries from the image table. */
611 toc_header = (fip_toc_header_t *)buf;
612 toc_header->name = TOC_HEADER_NAME;
613 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
614 toc_header->flags = toc_flags;
615
616 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
617
618 entry_offset = buf_size;
dp-arm90d2f0e2016-11-14 15:54:32 +0000619 for (image = image_head; image != NULL; image = image->next) {
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900620 payload_size += image->toc_e.size;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900621 entry_offset = (entry_offset + align - 1) & ~(align - 1);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900622 image->toc_e.offset_address = entry_offset;
623 *toc_entry++ = image->toc_e;
624 entry_offset += image->toc_e.size;
dp-arm4972ec52016-05-25 16:20:20 +0100625 }
626
627 /* Append a null uuid entry to mark the end of ToC entries. */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900628 memset(toc_entry, 0, sizeof(*toc_entry));
dp-arm4972ec52016-05-25 16:20:20 +0100629 toc_entry->offset_address = entry_offset;
dp-arm4972ec52016-05-25 16:20:20 +0100630
631 /* Generate the FIP file. */
632 fp = fopen(filename, "w");
633 if (fp == NULL)
634 log_err("fopen %s", filename);
635
636 if (verbose)
637 log_dbgx("Metadata size: %zu bytes", buf_size);
638
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900639 xfwrite(buf, buf_size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100640 free(buf);
641
642 if (verbose)
643 log_dbgx("Payload size: %zu bytes", payload_size);
644
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900645 for (image = image_head; image != NULL; image = image->next) {
646 if (fseek(fp, image->toc_e.offset_address, SEEK_SET))
647 log_errx("Failed to set file position");
648
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900649 xfwrite(image->buffer, image->toc_e.size, fp, filename);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900650 }
dp-arm4972ec52016-05-25 16:20:20 +0100651
652 fclose(fp);
653 return 0;
654}
655
656/*
657 * This function is shared between the create and update subcommands.
658 * The difference between the two subcommands is that when the FIP file
659 * is created, the parsing of an existing FIP is skipped. This results
660 * in update_fip() creating the new FIP file from scratch because the
661 * internal image table is not populated.
662 */
663static void update_fip(void)
664{
dp-arm90d2f0e2016-11-14 15:54:32 +0000665 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100666
667 /* Add or replace images in the FIP file. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000668 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +0000669 image_t *new_image, *old_image;
670
dp-arm90d2f0e2016-11-14 15:54:32 +0000671 if (desc->action != DO_PACK)
dp-arm4972ec52016-05-25 16:20:20 +0100672 continue;
673
dp-arm90d2f0e2016-11-14 15:54:32 +0000674 new_image = read_image_from_file(&desc->uuid,
675 desc->action_arg);
676 old_image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +0100677 if (old_image != NULL) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000678 if (verbose) {
dp-arm516dfcb2016-11-03 13:59:26 +0000679 log_dbgx("Replacing %s with %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000680 desc->cmdline_name,
681 desc->action_arg);
682 }
Masahiro Yamadad65e7fd2017-01-27 11:57:54 +0900683 replace_image(new_image);
dp-arm4972ec52016-05-25 16:20:20 +0100684 } else {
685 if (verbose)
686 log_dbgx("Adding image %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000687 desc->action_arg);
dp-arm715ef422016-08-30 14:18:58 +0100688 add_image(new_image);
dp-arm4972ec52016-05-25 16:20:20 +0100689 }
dp-arm4972ec52016-05-25 16:20:20 +0100690 }
691}
692
dp-arm90d2f0e2016-11-14 15:54:32 +0000693static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
dp-arm4972ec52016-05-25 16:20:20 +0100694{
695 unsigned long long flags;
696 char *endptr;
697
698 errno = 0;
699 flags = strtoull(arg, &endptr, 16);
700 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
701 log_errx("Invalid platform ToC flags: %s", arg);
702 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
703 *toc_flags |= flags << 32;
704}
705
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900706static int is_power_of_2(unsigned long x)
707{
708 return x && !(x & (x - 1));
709}
710
711static unsigned long get_image_align(char *arg)
712{
713 char *endptr;
714 unsigned long align;
715
716 errno = 0;
717 align = strtoul(arg, &endptr, 10);
718 if (*endptr != '\0' || !is_power_of_2(align) || errno != 0)
719 log_errx("Invalid alignment: %s", arg);
720
721 return align;
722}
723
dp-arm516dfcb2016-11-03 13:59:26 +0000724static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
725{
726 char *p;
727
728 for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
729 if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
730 p += strlen("uuid=");
731 uuid_from_str(uuid, p);
732 } else if (strncmp(p, "file=", strlen("file=")) == 0) {
733 p += strlen("file=");
734 snprintf(filename, len, "%s", p);
735 }
736 }
737}
738
dp-arm4972ec52016-05-25 16:20:20 +0100739static int create_cmd(int argc, char *argv[])
740{
dp-arm90d2f0e2016-11-14 15:54:32 +0000741 struct option *opts = NULL;
742 size_t nr_opts = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100743 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900744 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100745
746 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100747 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100748
dp-arm90d2f0e2016-11-14 15:54:32 +0000749 opts = fill_common_opts(opts, &nr_opts, required_argument);
750 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100751 OPT_PLAT_TOC_FLAGS);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900752 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000753 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000754 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100755
756 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000757 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100758
dp-arm516dfcb2016-11-03 13:59:26 +0000759 c = getopt_long(argc, argv, "b:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100760 if (c == -1)
761 break;
762
763 switch (c) {
764 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000765 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100766
dp-arm90d2f0e2016-11-14 15:54:32 +0000767 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000768 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100769 break;
770 }
771 case OPT_PLAT_TOC_FLAGS:
772 parse_plat_toc_flags(optarg, &toc_flags);
773 break;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900774 case OPT_ALIGN:
775 align = get_image_align(optarg);
776 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000777 case 'b': {
778 char name[_UUID_STR_LEN + 1];
779 char filename[PATH_MAX] = { 0 };
780 uuid_t uuid = { 0 };
781 image_desc_t *desc;
782
783 parse_blob_opt(optarg, &uuid,
784 filename, sizeof(filename));
785
786 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
787 filename[0] == '\0')
788 create_usage();
789
790 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000791 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000792 uuid_to_str(name, sizeof(name), &uuid);
793 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000794 add_image_desc(desc);
795 }
dp-armfb732312016-12-30 09:55:48 +0000796 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000797 break;
798 }
dp-arm4972ec52016-05-25 16:20:20 +0100799 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100800 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100801 }
802 }
803 argc -= optind;
804 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000805 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100806
807 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100808 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100809
810 update_fip();
811
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900812 pack_images(argv[0], toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100813 free_images();
814 return 0;
815}
816
817static void create_usage(void)
818{
819 toc_entry_t *toc_entry = toc_entries;
820
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900821 printf("fiptool create [opts] FIP_FILENAME\n");
822 printf("\n");
823 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900824 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
dp-arm516dfcb2016-11-03 13:59:26 +0000825 printf(" --blob uuid=...,file=...\tAdd an image with the given UUID "
826 "pointed to by file.\n");
dp-arm4972ec52016-05-25 16:20:20 +0100827 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field "
828 "occupying bits 32-47 in 64-bit ToC header.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +0900829 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100830 printf("Specific images are packed with the following options:\n");
831 for (; toc_entry->cmdline_name != NULL; toc_entry++)
832 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
833 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100834 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100835}
836
837static int update_cmd(int argc, char *argv[])
838{
dp-arm90d2f0e2016-11-14 15:54:32 +0000839 struct option *opts = NULL;
840 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000841 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100842 fip_toc_header_t toc_header = { 0 };
843 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900844 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100845 int pflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100846
847 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100848 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100849
dp-arm90d2f0e2016-11-14 15:54:32 +0000850 opts = fill_common_opts(opts, &nr_opts, required_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900851 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000852 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000853 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
854 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100855 OPT_PLAT_TOC_FLAGS);
dp-arm90d2f0e2016-11-14 15:54:32 +0000856 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100857
858 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000859 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100860
dp-arm516dfcb2016-11-03 13:59:26 +0000861 c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100862 if (c == -1)
863 break;
864
865 switch (c) {
866 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000867 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100868
dp-arm90d2f0e2016-11-14 15:54:32 +0000869 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000870 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100871 break;
872 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000873 case OPT_PLAT_TOC_FLAGS:
dp-arm4972ec52016-05-25 16:20:20 +0100874 parse_plat_toc_flags(optarg, &toc_flags);
875 pflag = 1;
876 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000877 case 'b': {
878 char name[_UUID_STR_LEN + 1];
879 char filename[PATH_MAX] = { 0 };
880 uuid_t uuid = { 0 };
881 image_desc_t *desc;
882
883 parse_blob_opt(optarg, &uuid,
884 filename, sizeof(filename));
885
886 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
887 filename[0] == '\0')
888 update_usage();
889
890 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000891 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000892 uuid_to_str(name, sizeof(name), &uuid);
893 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000894 add_image_desc(desc);
895 }
dp-armfb732312016-12-30 09:55:48 +0000896 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000897 break;
898 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900899 case OPT_ALIGN:
900 align = get_image_align(optarg);
901 break;
dp-arm4972ec52016-05-25 16:20:20 +0100902 case 'o':
903 snprintf(outfile, sizeof(outfile), "%s", optarg);
904 break;
905 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100906 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100907 }
908 }
909 argc -= optind;
910 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000911 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100912
913 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100914 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100915
916 if (outfile[0] == '\0')
917 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
918
Masahiro Yamadaebb6e372016-12-25 12:41:41 +0900919 if (access(argv[0], F_OK) == 0)
dp-arm4972ec52016-05-25 16:20:20 +0100920 parse_fip(argv[0], &toc_header);
921
922 if (pflag)
923 toc_header.flags &= ~(0xffffULL << 32);
924 toc_flags = (toc_header.flags |= toc_flags);
925
926 update_fip();
927
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900928 pack_images(outfile, toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100929 free_images();
930 return 0;
931}
932
933static void update_usage(void)
934{
935 toc_entry_t *toc_entry = toc_entries;
936
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900937 printf("fiptool update [opts] FIP_FILENAME\n");
938 printf("\n");
939 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900940 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
dp-arm516dfcb2016-11-03 13:59:26 +0000941 printf(" --blob uuid=...,file=...\tAdd or update an image "
942 "with the given UUID pointed to by file.\n");
dp-arm4972ec52016-05-25 16:20:20 +0100943 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
944 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field "
945 "occupying bits 32-47 in 64-bit ToC header.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +0900946 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100947 printf("Specific images are packed with the following options:\n");
948 for (; toc_entry->cmdline_name != NULL; toc_entry++)
949 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
950 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100951 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100952}
953
954static int unpack_cmd(int argc, char *argv[])
955{
dp-arm90d2f0e2016-11-14 15:54:32 +0000956 struct option *opts = NULL;
957 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000958 char outdir[PATH_MAX] = { 0 };
dp-arm90d2f0e2016-11-14 15:54:32 +0000959 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100960 int fflag = 0;
961 int unpack_all = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100962
963 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100964 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100965
dp-arm90d2f0e2016-11-14 15:54:32 +0000966 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000967 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000968 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
969 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
970 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100971
972 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000973 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100974
dp-arm516dfcb2016-11-03 13:59:26 +0000975 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100976 if (c == -1)
977 break;
978
979 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000980 case OPT_TOC_ENTRY: {
981 image_desc_t *desc;
982
983 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000984 set_image_desc_action(desc, DO_UNPACK, optarg);
dp-arm90d2f0e2016-11-14 15:54:32 +0000985 unpack_all = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100986 break;
dp-arm90d2f0e2016-11-14 15:54:32 +0000987 }
dp-arm516dfcb2016-11-03 13:59:26 +0000988 case 'b': {
989 char name[_UUID_STR_LEN + 1];
990 char filename[PATH_MAX] = { 0 };
991 uuid_t uuid = { 0 };
992 image_desc_t *desc;
993
994 parse_blob_opt(optarg, &uuid,
995 filename, sizeof(filename));
996
997 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
998 filename[0] == '\0')
999 unpack_usage();
1000
1001 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +00001002 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +00001003 uuid_to_str(name, sizeof(name), &uuid);
1004 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +00001005 add_image_desc(desc);
1006 }
dp-armfb732312016-12-30 09:55:48 +00001007 set_image_desc_action(desc, DO_UNPACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +00001008 unpack_all = 0;
1009 break;
1010 }
dp-arm4972ec52016-05-25 16:20:20 +01001011 case 'f':
1012 fflag = 1;
1013 break;
1014 case 'o':
1015 snprintf(outdir, sizeof(outdir), "%s", optarg);
1016 break;
1017 default:
dp-arm29f1b5c2016-09-15 09:58:50 +01001018 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001019 }
1020 }
1021 argc -= optind;
1022 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001023 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001024
1025 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001026 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001027
1028 parse_fip(argv[0], NULL);
1029
1030 if (outdir[0] != '\0')
1031 if (chdir(outdir) == -1)
1032 log_err("chdir %s", outdir);
1033
dp-arm4972ec52016-05-25 16:20:20 +01001034 /* Unpack all specified images. */
dp-arm90d2f0e2016-11-14 15:54:32 +00001035 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +00001036 char file[PATH_MAX];
dp-arm715ef422016-08-30 14:18:58 +01001037 image_t *image;
1038
dp-arm90d2f0e2016-11-14 15:54:32 +00001039 if (!unpack_all && desc->action != DO_UNPACK)
dp-arm4972ec52016-05-25 16:20:20 +01001040 continue;
1041
1042 /* Build filename. */
dp-arm90d2f0e2016-11-14 15:54:32 +00001043 if (desc->action_arg == NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001044 snprintf(file, sizeof(file), "%s.bin",
dp-arm90d2f0e2016-11-14 15:54:32 +00001045 desc->cmdline_name);
dp-arm4972ec52016-05-25 16:20:20 +01001046 else
1047 snprintf(file, sizeof(file), "%s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001048 desc->action_arg);
dp-arm4972ec52016-05-25 16:20:20 +01001049
dp-arm90d2f0e2016-11-14 15:54:32 +00001050 image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +01001051 if (image == NULL) {
1052 if (!unpack_all)
dp-arm516dfcb2016-11-03 13:59:26 +00001053 log_warnx("%s does not exist in %s",
dp-arm715ef422016-08-30 14:18:58 +01001054 file, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001055 continue;
1056 }
1057
1058 if (access(file, F_OK) != 0 || fflag) {
1059 if (verbose)
1060 log_dbgx("Unpacking %s", file);
dp-arm715ef422016-08-30 14:18:58 +01001061 write_image_to_file(image, file);
dp-arm4972ec52016-05-25 16:20:20 +01001062 } else {
1063 log_warnx("File %s already exists, use --force to overwrite it",
1064 file);
1065 }
dp-arm4972ec52016-05-25 16:20:20 +01001066 }
1067
1068 free_images();
1069 return 0;
1070}
1071
1072static void unpack_usage(void)
1073{
1074 toc_entry_t *toc_entry = toc_entries;
1075
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001076 printf("fiptool unpack [opts] FIP_FILENAME\n");
1077 printf("\n");
1078 printf("Options:\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001079 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID "
1080 "to file.\n");
1081 printf(" --force\t\t\tIf the output file already exists, use --force to "
dp-arm4972ec52016-05-25 16:20:20 +01001082 "overwrite it.\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001083 printf(" --out path\t\t\tSet the output directory path.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001084 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001085 printf("Specific images are unpacked with the following options:\n");
1086 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1087 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
1088 toc_entry->name);
Masahiro Yamada1b900152017-02-02 16:34:14 +09001089 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001090 printf("If no options are provided, all images will be unpacked.\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001091 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001092}
1093
1094static int remove_cmd(int argc, char *argv[])
1095{
dp-arm90d2f0e2016-11-14 15:54:32 +00001096 struct option *opts = NULL;
1097 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +00001098 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +01001099 fip_toc_header_t toc_header;
dp-arm90d2f0e2016-11-14 15:54:32 +00001100 image_desc_t *desc;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001101 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +01001102 int fflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001103
1104 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +01001105 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001106
dp-arm90d2f0e2016-11-14 15:54:32 +00001107 opts = fill_common_opts(opts, &nr_opts, no_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001108 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +00001109 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +00001110 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
1111 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
1112 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +01001113
1114 while (1) {
dp-armfe92b892016-11-07 11:13:54 +00001115 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001116
dp-arm516dfcb2016-11-03 13:59:26 +00001117 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +01001118 if (c == -1)
1119 break;
1120
1121 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001122 case OPT_TOC_ENTRY: {
1123 image_desc_t *desc;
1124
1125 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +00001126 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm4972ec52016-05-25 16:20:20 +01001127 break;
dp-arm90d2f0e2016-11-14 15:54:32 +00001128 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001129 case OPT_ALIGN:
1130 align = get_image_align(optarg);
1131 break;
dp-arm516dfcb2016-11-03 13:59:26 +00001132 case 'b': {
1133 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
1134 uuid_t uuid = { 0 };
1135 image_desc_t *desc;
1136
1137 parse_blob_opt(optarg, &uuid,
1138 filename, sizeof(filename));
1139
1140 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
1141 remove_usage();
1142
1143 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +00001144 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +00001145 uuid_to_str(name, sizeof(name), &uuid);
1146 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +00001147 add_image_desc(desc);
1148 }
dp-armfb732312016-12-30 09:55:48 +00001149 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm516dfcb2016-11-03 13:59:26 +00001150 break;
1151 }
dp-arm4972ec52016-05-25 16:20:20 +01001152 case 'f':
1153 fflag = 1;
1154 break;
1155 case 'o':
1156 snprintf(outfile, sizeof(outfile), "%s", optarg);
1157 break;
1158 default:
dp-arm29f1b5c2016-09-15 09:58:50 +01001159 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001160 }
1161 }
1162 argc -= optind;
1163 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001164 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001165
1166 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001167 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001168
1169 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
1170 log_errx("File %s already exists, use --force to overwrite it",
1171 outfile);
1172
1173 if (outfile[0] == '\0')
1174 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
1175
1176 parse_fip(argv[0], &toc_header);
1177
dp-arm90d2f0e2016-11-14 15:54:32 +00001178 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm715ef422016-08-30 14:18:58 +01001179 image_t *image;
1180
dp-arm90d2f0e2016-11-14 15:54:32 +00001181 if (desc->action != DO_REMOVE)
dp-arm4972ec52016-05-25 16:20:20 +01001182 continue;
dp-arm516dfcb2016-11-03 13:59:26 +00001183
dp-arm90d2f0e2016-11-14 15:54:32 +00001184 image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +01001185 if (image != NULL) {
dp-arm4972ec52016-05-25 16:20:20 +01001186 if (verbose)
dp-arm516dfcb2016-11-03 13:59:26 +00001187 log_dbgx("Removing %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001188 desc->cmdline_name);
dp-arm715ef422016-08-30 14:18:58 +01001189 remove_image(image);
dp-arm4972ec52016-05-25 16:20:20 +01001190 } else {
dp-arm516dfcb2016-11-03 13:59:26 +00001191 log_warnx("%s does not exist in %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001192 desc->cmdline_name, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001193 }
1194 }
1195
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001196 pack_images(outfile, toc_header.flags, align);
dp-arm4972ec52016-05-25 16:20:20 +01001197 free_images();
1198 return 0;
1199}
1200
1201static void remove_usage(void)
1202{
1203 toc_entry_t *toc_entry = toc_entries;
1204
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001205 printf("fiptool remove [opts] FIP_FILENAME\n");
1206 printf("\n");
1207 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001208 printf(" --align <value>\tEach image is aligned to <value> (default: 1).\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001209 printf(" --blob uuid=...\tRemove an image with the given UUID.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001210 printf(" --force\t\tIf the output FIP file already exists, use --force to "
1211 "overwrite it.\n");
1212 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001213 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001214 printf("Specific images are removed with the following options:\n");
1215 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1216 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
1217 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +01001218 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001219}
1220
1221static int version_cmd(int argc, char *argv[])
1222{
1223#ifdef VERSION
1224 puts(VERSION);
1225#else
1226 /* If built from fiptool directory, VERSION is not set. */
1227 puts("Unknown version");
1228#endif
1229 return 0;
1230}
1231
1232static void version_usage(void)
1233{
1234 printf("fiptool version\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001235 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001236}
1237
1238static int help_cmd(int argc, char *argv[])
1239{
1240 int i;
1241
1242 if (argc < 2)
1243 usage();
1244 argc--, argv++;
1245
1246 for (i = 0; i < NELEM(cmds); i++) {
1247 if (strcmp(cmds[i].name, argv[0]) == 0 &&
dp-arm29f1b5c2016-09-15 09:58:50 +01001248 cmds[i].usage != NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001249 cmds[i].usage();
dp-arm4972ec52016-05-25 16:20:20 +01001250 }
1251 if (i == NELEM(cmds))
1252 printf("No help for subcommand '%s'\n", argv[0]);
1253 return 0;
1254}
1255
1256static void usage(void)
1257{
Masahiro Yamada252c3362017-01-13 02:13:06 +09001258 printf("usage: fiptool [--verbose] <command> [<args>]\n");
dp-arm4972ec52016-05-25 16:20:20 +01001259 printf("Global options supported:\n");
1260 printf(" --verbose\tEnable verbose output for all commands.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001261 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001262 printf("Commands supported:\n");
1263 printf(" info\t\tList images contained in FIP.\n");
1264 printf(" create\tCreate a new FIP with the given images.\n");
1265 printf(" update\tUpdate an existing FIP with the given images.\n");
1266 printf(" unpack\tUnpack images from FIP.\n");
1267 printf(" remove\tRemove images from FIP.\n");
1268 printf(" version\tShow fiptool version.\n");
1269 printf(" help\t\tShow help for given command.\n");
1270 exit(1);
1271}
1272
1273int main(int argc, char *argv[])
1274{
1275 int i, ret = 0;
1276
dp-arm5cd10ae2016-11-07 10:45:59 +00001277 while (1) {
1278 int c, opt_index = 0;
1279 static struct option opts[] = {
1280 { "verbose", no_argument, NULL, 'v' },
1281 { NULL, no_argument, NULL, 0 }
1282 };
1283
1284 /*
1285 * Set POSIX mode so getopt stops at the first non-option
1286 * which is the subcommand.
1287 */
1288 c = getopt_long(argc, argv, "+v", opts, &opt_index);
1289 if (c == -1)
1290 break;
dp-arm4972ec52016-05-25 16:20:20 +01001291
dp-arm5cd10ae2016-11-07 10:45:59 +00001292 switch (c) {
1293 case 'v':
1294 verbose = 1;
1295 break;
1296 default:
Masahiro Yamada48a24972016-10-26 13:24:26 +09001297 usage();
dp-arm5cd10ae2016-11-07 10:45:59 +00001298 }
dp-arm4972ec52016-05-25 16:20:20 +01001299 }
dp-arm5cd10ae2016-11-07 10:45:59 +00001300 argc -= optind;
1301 argv += optind;
1302 /* Reset optind for subsequent getopt processing. */
1303 optind = 0;
1304
1305 if (argc == 0)
1306 usage();
dp-arm4972ec52016-05-25 16:20:20 +01001307
dp-arm90d2f0e2016-11-14 15:54:32 +00001308 fill_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001309 for (i = 0; i < NELEM(cmds); i++) {
1310 if (strcmp(cmds[i].name, argv[0]) == 0) {
1311 ret = cmds[i].handler(argc, argv);
1312 break;
1313 }
1314 }
1315 if (i == NELEM(cmds))
1316 usage();
dp-arm90d2f0e2016-11-14 15:54:32 +00001317 free_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001318 return ret;
1319}