blob: 1e4451f8c0b7e364a47e74a119b9e477ad979d85 [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");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900825 printf(" --blob uuid=...,file=...\tAdd an image with the given UUID pointed to by file.\n");
826 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field occupying bits 32-47 in 64-bit ToC header.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +0900827 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100828 printf("Specific images are packed with the following options:\n");
829 for (; toc_entry->cmdline_name != NULL; toc_entry++)
830 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
831 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100832 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100833}
834
835static int update_cmd(int argc, char *argv[])
836{
dp-arm90d2f0e2016-11-14 15:54:32 +0000837 struct option *opts = NULL;
838 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000839 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100840 fip_toc_header_t toc_header = { 0 };
841 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900842 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100843 int pflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100844
845 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100846 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100847
dp-arm90d2f0e2016-11-14 15:54:32 +0000848 opts = fill_common_opts(opts, &nr_opts, required_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900849 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000850 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000851 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
852 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100853 OPT_PLAT_TOC_FLAGS);
dp-arm90d2f0e2016-11-14 15:54:32 +0000854 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100855
856 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000857 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100858
dp-arm516dfcb2016-11-03 13:59:26 +0000859 c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100860 if (c == -1)
861 break;
862
863 switch (c) {
864 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000865 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100866
dp-arm90d2f0e2016-11-14 15:54:32 +0000867 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000868 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100869 break;
870 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000871 case OPT_PLAT_TOC_FLAGS:
dp-arm4972ec52016-05-25 16:20:20 +0100872 parse_plat_toc_flags(optarg, &toc_flags);
873 pflag = 1;
874 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000875 case 'b': {
876 char name[_UUID_STR_LEN + 1];
877 char filename[PATH_MAX] = { 0 };
878 uuid_t uuid = { 0 };
879 image_desc_t *desc;
880
881 parse_blob_opt(optarg, &uuid,
882 filename, sizeof(filename));
883
884 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
885 filename[0] == '\0')
886 update_usage();
887
888 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000889 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000890 uuid_to_str(name, sizeof(name), &uuid);
891 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000892 add_image_desc(desc);
893 }
dp-armfb732312016-12-30 09:55:48 +0000894 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000895 break;
896 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900897 case OPT_ALIGN:
898 align = get_image_align(optarg);
899 break;
dp-arm4972ec52016-05-25 16:20:20 +0100900 case 'o':
901 snprintf(outfile, sizeof(outfile), "%s", optarg);
902 break;
903 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100904 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100905 }
906 }
907 argc -= optind;
908 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000909 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100910
911 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100912 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100913
914 if (outfile[0] == '\0')
915 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
916
Masahiro Yamadaebb6e372016-12-25 12:41:41 +0900917 if (access(argv[0], F_OK) == 0)
dp-arm4972ec52016-05-25 16:20:20 +0100918 parse_fip(argv[0], &toc_header);
919
920 if (pflag)
921 toc_header.flags &= ~(0xffffULL << 32);
922 toc_flags = (toc_header.flags |= toc_flags);
923
924 update_fip();
925
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900926 pack_images(outfile, toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100927 free_images();
928 return 0;
929}
930
931static void update_usage(void)
932{
933 toc_entry_t *toc_entry = toc_entries;
934
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900935 printf("fiptool update [opts] FIP_FILENAME\n");
936 printf("\n");
937 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900938 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900939 printf(" --blob uuid=...,file=...\tAdd or update an image with the given UUID pointed to by file.\n");
dp-arm4972ec52016-05-25 16:20:20 +0100940 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900941 printf(" --plat-toc-flags <value>\t16-bit platform specific flag field occupying bits 32-47 in 64-bit ToC header.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +0900942 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100943 printf("Specific images are packed with the following options:\n");
944 for (; toc_entry->cmdline_name != NULL; toc_entry++)
945 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
946 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100947 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100948}
949
950static int unpack_cmd(int argc, char *argv[])
951{
dp-arm90d2f0e2016-11-14 15:54:32 +0000952 struct option *opts = NULL;
953 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000954 char outdir[PATH_MAX] = { 0 };
dp-arm90d2f0e2016-11-14 15:54:32 +0000955 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100956 int fflag = 0;
957 int unpack_all = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100958
959 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100960 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100961
dp-arm90d2f0e2016-11-14 15:54:32 +0000962 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000963 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000964 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
965 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
966 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100967
968 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000969 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100970
dp-arm516dfcb2016-11-03 13:59:26 +0000971 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100972 if (c == -1)
973 break;
974
975 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000976 case OPT_TOC_ENTRY: {
977 image_desc_t *desc;
978
979 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000980 set_image_desc_action(desc, DO_UNPACK, optarg);
dp-arm90d2f0e2016-11-14 15:54:32 +0000981 unpack_all = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100982 break;
dp-arm90d2f0e2016-11-14 15:54:32 +0000983 }
dp-arm516dfcb2016-11-03 13:59:26 +0000984 case 'b': {
985 char name[_UUID_STR_LEN + 1];
986 char filename[PATH_MAX] = { 0 };
987 uuid_t uuid = { 0 };
988 image_desc_t *desc;
989
990 parse_blob_opt(optarg, &uuid,
991 filename, sizeof(filename));
992
993 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
994 filename[0] == '\0')
995 unpack_usage();
996
997 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000998 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000999 uuid_to_str(name, sizeof(name), &uuid);
1000 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +00001001 add_image_desc(desc);
1002 }
dp-armfb732312016-12-30 09:55:48 +00001003 set_image_desc_action(desc, DO_UNPACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +00001004 unpack_all = 0;
1005 break;
1006 }
dp-arm4972ec52016-05-25 16:20:20 +01001007 case 'f':
1008 fflag = 1;
1009 break;
1010 case 'o':
1011 snprintf(outdir, sizeof(outdir), "%s", optarg);
1012 break;
1013 default:
dp-arm29f1b5c2016-09-15 09:58:50 +01001014 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001015 }
1016 }
1017 argc -= optind;
1018 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001019 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001020
1021 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001022 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001023
1024 parse_fip(argv[0], NULL);
1025
1026 if (outdir[0] != '\0')
1027 if (chdir(outdir) == -1)
1028 log_err("chdir %s", outdir);
1029
dp-arm4972ec52016-05-25 16:20:20 +01001030 /* Unpack all specified images. */
dp-arm90d2f0e2016-11-14 15:54:32 +00001031 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +00001032 char file[PATH_MAX];
dp-arm715ef422016-08-30 14:18:58 +01001033 image_t *image;
1034
dp-arm90d2f0e2016-11-14 15:54:32 +00001035 if (!unpack_all && desc->action != DO_UNPACK)
dp-arm4972ec52016-05-25 16:20:20 +01001036 continue;
1037
1038 /* Build filename. */
dp-arm90d2f0e2016-11-14 15:54:32 +00001039 if (desc->action_arg == NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001040 snprintf(file, sizeof(file), "%s.bin",
dp-arm90d2f0e2016-11-14 15:54:32 +00001041 desc->cmdline_name);
dp-arm4972ec52016-05-25 16:20:20 +01001042 else
1043 snprintf(file, sizeof(file), "%s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001044 desc->action_arg);
dp-arm4972ec52016-05-25 16:20:20 +01001045
dp-arm90d2f0e2016-11-14 15:54:32 +00001046 image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +01001047 if (image == NULL) {
1048 if (!unpack_all)
dp-arm516dfcb2016-11-03 13:59:26 +00001049 log_warnx("%s does not exist in %s",
dp-arm715ef422016-08-30 14:18:58 +01001050 file, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001051 continue;
1052 }
1053
1054 if (access(file, F_OK) != 0 || fflag) {
1055 if (verbose)
1056 log_dbgx("Unpacking %s", file);
dp-arm715ef422016-08-30 14:18:58 +01001057 write_image_to_file(image, file);
dp-arm4972ec52016-05-25 16:20:20 +01001058 } else {
1059 log_warnx("File %s already exists, use --force to overwrite it",
1060 file);
1061 }
dp-arm4972ec52016-05-25 16:20:20 +01001062 }
1063
1064 free_images();
1065 return 0;
1066}
1067
1068static void unpack_usage(void)
1069{
1070 toc_entry_t *toc_entry = toc_entries;
1071
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001072 printf("fiptool unpack [opts] FIP_FILENAME\n");
1073 printf("\n");
1074 printf("Options:\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +09001075 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID to file.\n");
1076 printf(" --force\t\t\tIf the output file already exists, use --force to overwrite it.\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001077 printf(" --out path\t\t\tSet the output directory path.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001078 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001079 printf("Specific images are unpacked with the following options:\n");
1080 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1081 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
1082 toc_entry->name);
Masahiro Yamada1b900152017-02-02 16:34:14 +09001083 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001084 printf("If no options are provided, all images will be unpacked.\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001085 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001086}
1087
1088static int remove_cmd(int argc, char *argv[])
1089{
dp-arm90d2f0e2016-11-14 15:54:32 +00001090 struct option *opts = NULL;
1091 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +00001092 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +01001093 fip_toc_header_t toc_header;
dp-arm90d2f0e2016-11-14 15:54:32 +00001094 image_desc_t *desc;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001095 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +01001096 int fflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001097
1098 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +01001099 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001100
dp-arm90d2f0e2016-11-14 15:54:32 +00001101 opts = fill_common_opts(opts, &nr_opts, no_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001102 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +00001103 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +00001104 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
1105 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
1106 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +01001107
1108 while (1) {
dp-armfe92b892016-11-07 11:13:54 +00001109 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001110
dp-arm516dfcb2016-11-03 13:59:26 +00001111 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +01001112 if (c == -1)
1113 break;
1114
1115 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001116 case OPT_TOC_ENTRY: {
1117 image_desc_t *desc;
1118
1119 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +00001120 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm4972ec52016-05-25 16:20:20 +01001121 break;
dp-arm90d2f0e2016-11-14 15:54:32 +00001122 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001123 case OPT_ALIGN:
1124 align = get_image_align(optarg);
1125 break;
dp-arm516dfcb2016-11-03 13:59:26 +00001126 case 'b': {
1127 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
1128 uuid_t uuid = { 0 };
1129 image_desc_t *desc;
1130
1131 parse_blob_opt(optarg, &uuid,
1132 filename, sizeof(filename));
1133
1134 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
1135 remove_usage();
1136
1137 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +00001138 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +00001139 uuid_to_str(name, sizeof(name), &uuid);
1140 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +00001141 add_image_desc(desc);
1142 }
dp-armfb732312016-12-30 09:55:48 +00001143 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm516dfcb2016-11-03 13:59:26 +00001144 break;
1145 }
dp-arm4972ec52016-05-25 16:20:20 +01001146 case 'f':
1147 fflag = 1;
1148 break;
1149 case 'o':
1150 snprintf(outfile, sizeof(outfile), "%s", optarg);
1151 break;
1152 default:
dp-arm29f1b5c2016-09-15 09:58:50 +01001153 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001154 }
1155 }
1156 argc -= optind;
1157 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001158 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001159
1160 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001161 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001162
1163 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
1164 log_errx("File %s already exists, use --force to overwrite it",
1165 outfile);
1166
1167 if (outfile[0] == '\0')
1168 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
1169
1170 parse_fip(argv[0], &toc_header);
1171
dp-arm90d2f0e2016-11-14 15:54:32 +00001172 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm715ef422016-08-30 14:18:58 +01001173 image_t *image;
1174
dp-arm90d2f0e2016-11-14 15:54:32 +00001175 if (desc->action != DO_REMOVE)
dp-arm4972ec52016-05-25 16:20:20 +01001176 continue;
dp-arm516dfcb2016-11-03 13:59:26 +00001177
dp-arm90d2f0e2016-11-14 15:54:32 +00001178 image = lookup_image_from_uuid(&desc->uuid);
dp-arm715ef422016-08-30 14:18:58 +01001179 if (image != NULL) {
dp-arm4972ec52016-05-25 16:20:20 +01001180 if (verbose)
dp-arm516dfcb2016-11-03 13:59:26 +00001181 log_dbgx("Removing %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001182 desc->cmdline_name);
dp-arm715ef422016-08-30 14:18:58 +01001183 remove_image(image);
dp-arm4972ec52016-05-25 16:20:20 +01001184 } else {
dp-arm516dfcb2016-11-03 13:59:26 +00001185 log_warnx("%s does not exist in %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001186 desc->cmdline_name, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001187 }
1188 }
1189
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001190 pack_images(outfile, toc_header.flags, align);
dp-arm4972ec52016-05-25 16:20:20 +01001191 free_images();
1192 return 0;
1193}
1194
1195static void remove_usage(void)
1196{
1197 toc_entry_t *toc_entry = toc_entries;
1198
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001199 printf("fiptool remove [opts] FIP_FILENAME\n");
1200 printf("\n");
1201 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001202 printf(" --align <value>\tEach image is aligned to <value> (default: 1).\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001203 printf(" --blob uuid=...\tRemove an image with the given UUID.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +09001204 printf(" --force\t\tIf the output FIP file already exists, use --force to overwrite it.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001205 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001206 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001207 printf("Specific images are removed with the following options:\n");
1208 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1209 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
1210 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +01001211 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001212}
1213
1214static int version_cmd(int argc, char *argv[])
1215{
1216#ifdef VERSION
1217 puts(VERSION);
1218#else
1219 /* If built from fiptool directory, VERSION is not set. */
1220 puts("Unknown version");
1221#endif
1222 return 0;
1223}
1224
1225static void version_usage(void)
1226{
1227 printf("fiptool version\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001228 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001229}
1230
1231static int help_cmd(int argc, char *argv[])
1232{
1233 int i;
1234
1235 if (argc < 2)
1236 usage();
1237 argc--, argv++;
1238
1239 for (i = 0; i < NELEM(cmds); i++) {
1240 if (strcmp(cmds[i].name, argv[0]) == 0 &&
dp-arm29f1b5c2016-09-15 09:58:50 +01001241 cmds[i].usage != NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001242 cmds[i].usage();
dp-arm4972ec52016-05-25 16:20:20 +01001243 }
1244 if (i == NELEM(cmds))
1245 printf("No help for subcommand '%s'\n", argv[0]);
1246 return 0;
1247}
1248
1249static void usage(void)
1250{
Masahiro Yamada252c3362017-01-13 02:13:06 +09001251 printf("usage: fiptool [--verbose] <command> [<args>]\n");
dp-arm4972ec52016-05-25 16:20:20 +01001252 printf("Global options supported:\n");
1253 printf(" --verbose\tEnable verbose output for all commands.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001254 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001255 printf("Commands supported:\n");
1256 printf(" info\t\tList images contained in FIP.\n");
1257 printf(" create\tCreate a new FIP with the given images.\n");
1258 printf(" update\tUpdate an existing FIP with the given images.\n");
1259 printf(" unpack\tUnpack images from FIP.\n");
1260 printf(" remove\tRemove images from FIP.\n");
1261 printf(" version\tShow fiptool version.\n");
1262 printf(" help\t\tShow help for given command.\n");
1263 exit(1);
1264}
1265
1266int main(int argc, char *argv[])
1267{
1268 int i, ret = 0;
1269
dp-arm5cd10ae2016-11-07 10:45:59 +00001270 while (1) {
1271 int c, opt_index = 0;
1272 static struct option opts[] = {
1273 { "verbose", no_argument, NULL, 'v' },
1274 { NULL, no_argument, NULL, 0 }
1275 };
1276
1277 /*
1278 * Set POSIX mode so getopt stops at the first non-option
1279 * which is the subcommand.
1280 */
1281 c = getopt_long(argc, argv, "+v", opts, &opt_index);
1282 if (c == -1)
1283 break;
dp-arm4972ec52016-05-25 16:20:20 +01001284
dp-arm5cd10ae2016-11-07 10:45:59 +00001285 switch (c) {
1286 case 'v':
1287 verbose = 1;
1288 break;
1289 default:
Masahiro Yamada48a24972016-10-26 13:24:26 +09001290 usage();
dp-arm5cd10ae2016-11-07 10:45:59 +00001291 }
dp-arm4972ec52016-05-25 16:20:20 +01001292 }
dp-arm5cd10ae2016-11-07 10:45:59 +00001293 argc -= optind;
1294 argv += optind;
1295 /* Reset optind for subsequent getopt processing. */
1296 optind = 0;
1297
1298 if (argc == 0)
1299 usage();
dp-arm4972ec52016-05-25 16:20:20 +01001300
dp-arm90d2f0e2016-11-14 15:54:32 +00001301 fill_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001302 for (i = 0; i < NELEM(cmds); i++) {
1303 if (strcmp(cmds[i].name, argv[0]) == 0) {
1304 ret = cmds[i].handler(argc, argv);
1305 break;
1306 }
1307 }
1308 if (i == NELEM(cmds))
1309 usage();
dp-arm90d2f0e2016-11-14 15:54:32 +00001310 free_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001311 return ret;
1312}