blob: 4d80f2f16632bb6ec067b133460d705b0387aff7 [file] [log] [blame]
dp-arm4972ec52016-05-25 16:20:20 +01001/*
Masahiro Yamadae3a2b312017-05-08 18:29:03 +09002 * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
dp-arm4972ec52016-05-25 16:20:20 +01003 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
dp-arm4972ec52016-05-25 16:20:20 +01005 */
6
7#include <sys/types.h>
8#include <sys/stat.h>
9
10#include <assert.h>
11#include <errno.h>
12#include <getopt.h>
13#include <limits.h>
14#include <stdarg.h>
15#include <stdint.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <unistd.h>
20
dp-arm12e893b2016-08-24 13:21:08 +010021#include <openssl/sha.h>
22
Masahiro Yamadae3a2b312017-05-08 18:29:03 +090023#include <firmware_image_package.h>
24
dp-arm4972ec52016-05-25 16:20:20 +010025#include "fiptool.h"
dp-arm4972ec52016-05-25 16:20:20 +010026#include "tbbr_config.h"
27
28#define OPT_TOC_ENTRY 0
29#define OPT_PLAT_TOC_FLAGS 1
Masahiro Yamada4d87eb42016-12-25 13:52:22 +090030#define OPT_ALIGN 2
dp-arm4972ec52016-05-25 16:20:20 +010031
32static int info_cmd(int argc, char *argv[]);
33static void info_usage(void);
34static int create_cmd(int argc, char *argv[]);
35static void create_usage(void);
36static int update_cmd(int argc, char *argv[]);
37static void update_usage(void);
38static int unpack_cmd(int argc, char *argv[]);
39static void unpack_usage(void);
40static int remove_cmd(int argc, char *argv[]);
41static void remove_usage(void);
42static int version_cmd(int argc, char *argv[]);
43static void version_usage(void);
44static int help_cmd(int argc, char *argv[]);
45static void usage(void);
46
47/* Available subcommands. */
48static cmd_t cmds[] = {
49 { .name = "info", .handler = info_cmd, .usage = info_usage },
50 { .name = "create", .handler = create_cmd, .usage = create_usage },
51 { .name = "update", .handler = update_cmd, .usage = update_usage },
52 { .name = "unpack", .handler = unpack_cmd, .usage = unpack_usage },
53 { .name = "remove", .handler = remove_cmd, .usage = remove_usage },
54 { .name = "version", .handler = version_cmd, .usage = version_usage },
55 { .name = "help", .handler = help_cmd, .usage = NULL },
56};
57
dp-arm90d2f0e2016-11-14 15:54:32 +000058static image_desc_t *image_desc_head;
59static size_t nr_image_descs;
dp-arm4972ec52016-05-25 16:20:20 +010060static uuid_t uuid_null = { 0 };
61static int verbose;
62
dp-armc1f8e772016-11-04 10:52:25 +000063static void vlog(int prio, const char *msg, va_list ap)
dp-arm4972ec52016-05-25 16:20:20 +010064{
65 char *prefix[] = { "DEBUG", "WARN", "ERROR" };
66
67 fprintf(stderr, "%s: ", prefix[prio]);
68 vfprintf(stderr, msg, ap);
69 fputc('\n', stderr);
70}
71
dp-armc1f8e772016-11-04 10:52:25 +000072static void log_dbgx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010073{
74 va_list ap;
75
76 va_start(ap, msg);
77 vlog(LOG_DBG, msg, ap);
78 va_end(ap);
79}
80
dp-armc1f8e772016-11-04 10:52:25 +000081static void log_warnx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010082{
83 va_list ap;
84
85 va_start(ap, msg);
86 vlog(LOG_WARN, msg, ap);
87 va_end(ap);
88}
89
dp-armc1f8e772016-11-04 10:52:25 +000090static void log_err(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010091{
92 char buf[512];
93 va_list ap;
94
95 va_start(ap, msg);
96 snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
97 vlog(LOG_ERR, buf, ap);
98 va_end(ap);
99 exit(1);
100}
101
dp-armc1f8e772016-11-04 10:52:25 +0000102static void log_errx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +0100103{
104 va_list ap;
105
106 va_start(ap, msg);
107 vlog(LOG_ERR, msg, ap);
108 va_end(ap);
109 exit(1);
110}
111
dp-armdb0f5e92016-11-04 10:56:25 +0000112static char *xstrdup(const char *s, const char *msg)
113{
114 char *d;
115
116 d = strdup(s);
117 if (d == NULL)
dp-arm5ba38542016-12-21 14:59:30 +0000118 log_errx("strdup: %s", msg);
dp-armdb0f5e92016-11-04 10:56:25 +0000119 return d;
120}
121
122static void *xmalloc(size_t size, const char *msg)
123{
124 void *d;
125
126 d = malloc(size);
127 if (d == NULL)
dp-arm5ba38542016-12-21 14:59:30 +0000128 log_errx("malloc: %s", msg);
dp-armdb0f5e92016-11-04 10:56:25 +0000129 return d;
130}
131
Masahiro Yamada03fdf692017-01-15 00:50:41 +0900132static void *xzalloc(size_t size, const char *msg)
133{
134 return memset(xmalloc(size, msg), 0, size);
135}
136
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900137static void xfwrite(void *buf, size_t size, FILE *fp, const char *filename)
138{
139 if (fwrite(buf, 1, size, fp) != size)
140 log_errx("Failed to write %s", filename);
141}
142
dp-arm90d2f0e2016-11-14 15:54:32 +0000143static image_desc_t *new_image_desc(const uuid_t *uuid,
144 const char *name, const char *cmdline_name)
dp-arm4972ec52016-05-25 16:20:20 +0100145{
dp-arm90d2f0e2016-11-14 15:54:32 +0000146 image_desc_t *desc;
147
Masahiro Yamada03fdf692017-01-15 00:50:41 +0900148 desc = xzalloc(sizeof(*desc),
dp-arm90d2f0e2016-11-14 15:54:32 +0000149 "failed to allocate memory for image descriptor");
150 memcpy(&desc->uuid, uuid, sizeof(uuid_t));
151 desc->name = xstrdup(name,
152 "failed to allocate memory for image name");
153 desc->cmdline_name = xstrdup(cmdline_name,
154 "failed to allocate memory for image command line name");
155 desc->action = DO_UNSPEC;
dp-arm90d2f0e2016-11-14 15:54:32 +0000156 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100157}
158
dp-armfb732312016-12-30 09:55:48 +0000159static void set_image_desc_action(image_desc_t *desc, int action,
160 const char *arg)
161{
162 assert(desc != NULL);
163
164 if (desc->action_arg != DO_UNSPEC)
165 free(desc->action_arg);
166 desc->action = action;
167 desc->action_arg = NULL;
168 if (arg != NULL)
169 desc->action_arg = xstrdup(arg,
170 "failed to allocate memory for argument");
171}
172
dp-arm90d2f0e2016-11-14 15:54:32 +0000173static void free_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100174{
dp-arm90d2f0e2016-11-14 15:54:32 +0000175 free(desc->name);
176 free(desc->cmdline_name);
177 free(desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000178 free(desc->image);
dp-arm90d2f0e2016-11-14 15:54:32 +0000179 free(desc);
dp-arm4972ec52016-05-25 16:20:20 +0100180}
181
dp-arm90d2f0e2016-11-14 15:54:32 +0000182static void add_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100183{
Masahiro Yamadad224b452017-01-14 23:22:02 +0900184 image_desc_t **p = &image_desc_head;
185
Masahiro Yamadad224b452017-01-14 23:22:02 +0900186 while (*p)
187 p = &(*p)->next;
188
Masahiro Yamadac2a7d9c2017-01-27 12:53:13 +0900189 assert(*p == NULL);
Masahiro Yamadad224b452017-01-14 23:22:02 +0900190 *p = desc;
dp-arm90d2f0e2016-11-14 15:54:32 +0000191 nr_image_descs++;
192}
dp-arm4972ec52016-05-25 16:20:20 +0100193
dp-arm90d2f0e2016-11-14 15:54:32 +0000194static void free_image_descs(void)
195{
196 image_desc_t *desc = image_desc_head, *tmp;
197
198 while (desc != NULL) {
199 tmp = desc->next;
200 free_image_desc(desc);
201 desc = tmp;
202 nr_image_descs--;
dp-arm4972ec52016-05-25 16:20:20 +0100203 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000204 assert(nr_image_descs == 0);
dp-arm4972ec52016-05-25 16:20:20 +0100205}
206
dp-arm90d2f0e2016-11-14 15:54:32 +0000207static void fill_image_descs(void)
208{
209 toc_entry_t *toc_entry;
210
211 for (toc_entry = toc_entries;
212 toc_entry->cmdline_name != NULL;
213 toc_entry++) {
214 image_desc_t *desc;
215
216 desc = new_image_desc(&toc_entry->uuid,
217 toc_entry->name,
218 toc_entry->cmdline_name);
219 add_image_desc(desc);
220 }
221}
222
dp-arm90d2f0e2016-11-14 15:54:32 +0000223static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid)
dp-arm4972ec52016-05-25 16:20:20 +0100224{
dp-arm90d2f0e2016-11-14 15:54:32 +0000225 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100226
dp-arm90d2f0e2016-11-14 15:54:32 +0000227 for (desc = image_desc_head; desc != NULL; desc = desc->next)
228 if (memcmp(&desc->uuid, uuid, sizeof(uuid_t)) == 0)
229 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100230 return NULL;
231}
232
dp-arm90d2f0e2016-11-14 15:54:32 +0000233static image_desc_t *lookup_image_desc_from_opt(const char *opt)
234{
235 image_desc_t *desc;
236
237 for (desc = image_desc_head; desc != NULL; desc = desc->next)
238 if (strcmp(desc->cmdline_name, opt) == 0)
239 return desc;
240 return NULL;
241}
242
dp-arm516dfcb2016-11-03 13:59:26 +0000243static void uuid_to_str(char *s, size_t len, const uuid_t *u)
244{
245 assert(len >= (_UUID_STR_LEN + 1));
246
247 snprintf(s, len, "%08X-%04X-%04X-%04X-%04X%04X%04X",
248 u->time_low,
249 u->time_mid,
250 u->time_hi_and_version,
251 ((uint16_t)u->clock_seq_hi_and_reserved << 8) | u->clock_seq_low,
252 ((uint16_t)u->node[0] << 8) | u->node[1],
253 ((uint16_t)u->node[2] << 8) | u->node[3],
254 ((uint16_t)u->node[4] << 8) | u->node[5]);
255}
256
257static void uuid_from_str(uuid_t *u, const char *s)
258{
259 int n;
260
261 if (s == NULL)
262 log_errx("UUID cannot be NULL");
263 if (strlen(s) != _UUID_STR_LEN)
264 log_errx("Invalid UUID: %s", s);
265
266 n = sscanf(s,
267 "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
268 &u->time_low, &u->time_mid, &u->time_hi_and_version,
269 &u->clock_seq_hi_and_reserved, &u->clock_seq_low, &u->node[0],
270 &u->node[1], &u->node[2], &u->node[3], &u->node[4], &u->node[5]);
271 /*
272 * Given the format specifier above, we expect 11 items to be scanned
273 * for a properly formatted UUID.
274 */
275 if (n != 11)
276 log_errx("Invalid UUID: %s", s);
277}
278
dp-armc1f8e772016-11-04 10:52:25 +0000279static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
dp-arm4972ec52016-05-25 16:20:20 +0100280{
281 struct stat st;
282 FILE *fp;
283 char *buf, *bufend;
284 fip_toc_header_t *toc_header;
285 fip_toc_entry_t *toc_entry;
dp-arm4972ec52016-05-25 16:20:20 +0100286 int terminated = 0;
287
288 fp = fopen(filename, "r");
289 if (fp == NULL)
290 log_err("fopen %s", filename);
291
292 if (fstat(fileno(fp), &st) == -1)
293 log_err("fstat %s", filename);
294
dp-armdb0f5e92016-11-04 10:56:25 +0000295 buf = xmalloc(st.st_size, "failed to load file into memory");
dp-arm4972ec52016-05-25 16:20:20 +0100296 if (fread(buf, 1, st.st_size, fp) != st.st_size)
297 log_errx("Failed to read %s", filename);
298 bufend = buf + st.st_size;
299 fclose(fp);
300
301 if (st.st_size < sizeof(fip_toc_header_t))
302 log_errx("FIP %s is truncated", filename);
303
304 toc_header = (fip_toc_header_t *)buf;
305 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
306
307 if (toc_header->name != TOC_HEADER_NAME)
308 log_errx("%s is not a FIP file", filename);
309
310 /* Return the ToC header if the caller wants it. */
311 if (toc_header_out != NULL)
312 *toc_header_out = *toc_header;
313
314 /* Walk through each ToC entry in the file. */
315 while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
dp-arm516dfcb2016-11-03 13:59:26 +0000316 image_t *image;
317 image_desc_t *desc;
318
dp-arm4972ec52016-05-25 16:20:20 +0100319 /* Found the ToC terminator, we are done. */
320 if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
321 terminated = 1;
322 break;
323 }
324
325 /*
326 * Build a new image out of the ToC entry and add it to the
327 * table of images.
328 */
Masahiro Yamadad224b452017-01-14 23:22:02 +0900329 image = xzalloc(sizeof(*image),
dp-armdb0f5e92016-11-04 10:56:25 +0000330 "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900331 image->toc_e = *toc_entry;
dp-armdb0f5e92016-11-04 10:56:25 +0000332 image->buffer = xmalloc(toc_entry->size,
333 "failed to allocate image buffer, is FIP file corrupted?");
dp-arm4972ec52016-05-25 16:20:20 +0100334 /* Overflow checks before memory copy. */
335 if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
336 log_errx("FIP %s is corrupted", filename);
337 if (toc_entry->size + toc_entry->offset_address > st.st_size)
338 log_errx("FIP %s is corrupted", filename);
339
340 memcpy(image->buffer, buf + toc_entry->offset_address,
341 toc_entry->size);
dp-arm4972ec52016-05-25 16:20:20 +0100342
dp-arm516dfcb2016-11-03 13:59:26 +0000343 /* If this is an unknown image, create a descriptor for it. */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900344 desc = lookup_image_desc_from_uuid(&toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000345 if (desc == NULL) {
346 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
347
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900348 uuid_to_str(name, sizeof(name), &toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000349 snprintf(filename, sizeof(filename), "%s%s",
350 name, ".bin");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900351 desc = new_image_desc(&toc_entry->uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000352 desc->action = DO_UNPACK;
353 desc->action_arg = xstrdup(filename,
354 "failed to allocate memory for blob filename");
355 add_image_desc(desc);
356 }
357
dp-armafa1efa2017-02-14 15:22:13 +0000358 assert(desc->image == NULL);
359 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100360
361 toc_entry++;
362 }
363
364 if (terminated == 0)
365 log_errx("FIP %s does not have a ToC terminator entry",
366 filename);
367 free(buf);
368 return 0;
369}
370
dp-armc1f8e772016-11-04 10:52:25 +0000371static image_t *read_image_from_file(const uuid_t *uuid, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100372{
373 struct stat st;
374 image_t *image;
375 FILE *fp;
376
dp-arm715ef422016-08-30 14:18:58 +0100377 assert(uuid != NULL);
378
dp-arm4972ec52016-05-25 16:20:20 +0100379 fp = fopen(filename, "r");
380 if (fp == NULL)
381 log_err("fopen %s", filename);
382
383 if (fstat(fileno(fp), &st) == -1)
384 log_errx("fstat %s", filename);
385
Masahiro Yamadad224b452017-01-14 23:22:02 +0900386 image = xzalloc(sizeof(*image), "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900387 image->toc_e.uuid = *uuid;
dp-armdb0f5e92016-11-04 10:56:25 +0000388 image->buffer = xmalloc(st.st_size, "failed to allocate image buffer");
dp-arm4972ec52016-05-25 16:20:20 +0100389 if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
390 log_errx("Failed to read %s", filename);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900391 image->toc_e.size = st.st_size;
dp-arm4972ec52016-05-25 16:20:20 +0100392
393 fclose(fp);
394 return image;
395}
396
dp-armc1f8e772016-11-04 10:52:25 +0000397static int write_image_to_file(const image_t *image, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100398{
399 FILE *fp;
400
401 fp = fopen(filename, "w");
402 if (fp == NULL)
403 log_err("fopen");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900404 xfwrite(image->buffer, image->toc_e.size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100405 fclose(fp);
406 return 0;
407}
408
dp-arm90d2f0e2016-11-14 15:54:32 +0000409static struct option *add_opt(struct option *opts, size_t *nr_opts,
410 const char *name, int has_arg, int val)
dp-arm4972ec52016-05-25 16:20:20 +0100411{
dp-arm90d2f0e2016-11-14 15:54:32 +0000412 opts = realloc(opts, (*nr_opts + 1) * sizeof(*opts));
413 if (opts == NULL)
414 log_err("realloc");
415 opts[*nr_opts].name = name;
416 opts[*nr_opts].has_arg = has_arg;
417 opts[*nr_opts].flag = NULL;
418 opts[*nr_opts].val = val;
419 ++*nr_opts;
420 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100421}
422
dp-arm90d2f0e2016-11-14 15:54:32 +0000423static struct option *fill_common_opts(struct option *opts, size_t *nr_opts,
424 int has_arg)
dp-arm4972ec52016-05-25 16:20:20 +0100425{
dp-arm90d2f0e2016-11-14 15:54:32 +0000426 image_desc_t *desc;
427
428 for (desc = image_desc_head; desc != NULL; desc = desc->next)
429 opts = add_opt(opts, nr_opts, desc->cmdline_name, has_arg,
430 OPT_TOC_ENTRY);
431 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100432}
433
dp-arm90d2f0e2016-11-14 15:54:32 +0000434static void md_print(const unsigned char *md, size_t len)
dp-arm12e893b2016-08-24 13:21:08 +0100435{
436 size_t i;
437
438 for (i = 0; i < len; i++)
439 printf("%02x", md[i]);
440}
441
dp-arm4972ec52016-05-25 16:20:20 +0100442static int info_cmd(int argc, char *argv[])
443{
dp-armafa1efa2017-02-14 15:22:13 +0000444 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100445 fip_toc_header_t toc_header;
dp-arm4972ec52016-05-25 16:20:20 +0100446
447 if (argc != 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100448 info_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100449 argc--, argv++;
450
451 parse_fip(argv[0], &toc_header);
452
453 if (verbose) {
454 log_dbgx("toc_header[name]: 0x%llX",
455 (unsigned long long)toc_header.name);
456 log_dbgx("toc_header[serial_number]: 0x%llX",
457 (unsigned long long)toc_header.serial_number);
458 log_dbgx("toc_header[flags]: 0x%llX",
459 (unsigned long long)toc_header.flags);
460 }
461
dp-armafa1efa2017-02-14 15:22:13 +0000462 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
463 image_t *image = desc->image;
dp-arm715ef422016-08-30 14:18:58 +0100464
dp-armafa1efa2017-02-14 15:22:13 +0000465 if (image == NULL)
466 continue;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900467 printf("%s: offset=0x%llX, size=0x%llX, cmdline=\"--%s\"",
468 desc->name,
469 (unsigned long long)image->toc_e.offset_address,
470 (unsigned long long)image->toc_e.size,
Masahiro Yamadaeee39312017-01-15 23:20:00 +0900471 desc->cmdline_name);
dp-arm12e893b2016-08-24 13:21:08 +0100472 if (verbose) {
473 unsigned char md[SHA256_DIGEST_LENGTH];
474
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900475 SHA256(image->buffer, image->toc_e.size, md);
dp-arm12e893b2016-08-24 13:21:08 +0100476 printf(", sha256=");
477 md_print(md, sizeof(md));
478 }
479 putchar('\n');
dp-arm4972ec52016-05-25 16:20:20 +0100480 }
481
dp-arm4972ec52016-05-25 16:20:20 +0100482 return 0;
483}
484
485static void info_usage(void)
486{
487 printf("fiptool info FIP_FILENAME\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100488 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100489}
490
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900491static int pack_images(const char *filename, uint64_t toc_flags, unsigned long align)
dp-arm4972ec52016-05-25 16:20:20 +0100492{
493 FILE *fp;
dp-armafa1efa2017-02-14 15:22:13 +0000494 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100495 fip_toc_header_t *toc_header;
496 fip_toc_entry_t *toc_entry;
497 char *buf;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900498 uint64_t entry_offset, buf_size, payload_size = 0;
dp-armafa1efa2017-02-14 15:22:13 +0000499 size_t nr_images = 0;
500
501 for (desc = image_desc_head; desc != NULL; desc = desc->next)
502 if (desc->image != NULL)
503 nr_images++;
dp-arm4972ec52016-05-25 16:20:20 +0100504
505 buf_size = sizeof(fip_toc_header_t) +
506 sizeof(fip_toc_entry_t) * (nr_images + 1);
507 buf = calloc(1, buf_size);
508 if (buf == NULL)
509 log_err("calloc");
510
511 /* Build up header and ToC entries from the image table. */
512 toc_header = (fip_toc_header_t *)buf;
513 toc_header->name = TOC_HEADER_NAME;
514 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
515 toc_header->flags = toc_flags;
516
517 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
518
519 entry_offset = buf_size;
dp-armafa1efa2017-02-14 15:22:13 +0000520 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
521 image_t *image = desc->image;
522
523 if (image == NULL)
524 continue;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900525 payload_size += image->toc_e.size;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900526 entry_offset = (entry_offset + align - 1) & ~(align - 1);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900527 image->toc_e.offset_address = entry_offset;
528 *toc_entry++ = image->toc_e;
529 entry_offset += image->toc_e.size;
dp-arm4972ec52016-05-25 16:20:20 +0100530 }
531
532 /* Append a null uuid entry to mark the end of ToC entries. */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900533 memset(toc_entry, 0, sizeof(*toc_entry));
dp-arm4972ec52016-05-25 16:20:20 +0100534 toc_entry->offset_address = entry_offset;
dp-arm4972ec52016-05-25 16:20:20 +0100535
536 /* Generate the FIP file. */
537 fp = fopen(filename, "w");
538 if (fp == NULL)
539 log_err("fopen %s", filename);
540
541 if (verbose)
542 log_dbgx("Metadata size: %zu bytes", buf_size);
543
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900544 xfwrite(buf, buf_size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100545 free(buf);
546
547 if (verbose)
548 log_dbgx("Payload size: %zu bytes", payload_size);
549
dp-armafa1efa2017-02-14 15:22:13 +0000550 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
551 image_t *image = desc->image;
552
553 if (image == NULL)
554 continue;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900555 if (fseek(fp, image->toc_e.offset_address, SEEK_SET))
556 log_errx("Failed to set file position");
557
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900558 xfwrite(image->buffer, image->toc_e.size, fp, filename);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900559 }
dp-arm4972ec52016-05-25 16:20:20 +0100560
561 fclose(fp);
562 return 0;
563}
564
565/*
566 * This function is shared between the create and update subcommands.
567 * The difference between the two subcommands is that when the FIP file
568 * is created, the parsing of an existing FIP is skipped. This results
569 * in update_fip() creating the new FIP file from scratch because the
570 * internal image table is not populated.
571 */
572static void update_fip(void)
573{
dp-arm90d2f0e2016-11-14 15:54:32 +0000574 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100575
576 /* Add or replace images in the FIP file. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000577 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-armafa1efa2017-02-14 15:22:13 +0000578 image_t *image;
dp-arm516dfcb2016-11-03 13:59:26 +0000579
dp-arm90d2f0e2016-11-14 15:54:32 +0000580 if (desc->action != DO_PACK)
dp-arm4972ec52016-05-25 16:20:20 +0100581 continue;
582
dp-armafa1efa2017-02-14 15:22:13 +0000583 image = read_image_from_file(&desc->uuid,
dp-arm90d2f0e2016-11-14 15:54:32 +0000584 desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000585 if (desc->image != NULL) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000586 if (verbose) {
dp-arm516dfcb2016-11-03 13:59:26 +0000587 log_dbgx("Replacing %s with %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000588 desc->cmdline_name,
589 desc->action_arg);
590 }
dp-armafa1efa2017-02-14 15:22:13 +0000591 free(desc->image);
592 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100593 } else {
594 if (verbose)
595 log_dbgx("Adding image %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000596 desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000597 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100598 }
dp-arm4972ec52016-05-25 16:20:20 +0100599 }
600}
601
dp-arm90d2f0e2016-11-14 15:54:32 +0000602static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
dp-arm4972ec52016-05-25 16:20:20 +0100603{
604 unsigned long long flags;
605 char *endptr;
606
607 errno = 0;
608 flags = strtoull(arg, &endptr, 16);
609 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
610 log_errx("Invalid platform ToC flags: %s", arg);
611 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
612 *toc_flags |= flags << 32;
613}
614
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900615static int is_power_of_2(unsigned long x)
616{
617 return x && !(x & (x - 1));
618}
619
620static unsigned long get_image_align(char *arg)
621{
622 char *endptr;
623 unsigned long align;
624
625 errno = 0;
Andreas Färber242a7b72017-04-21 19:39:10 +0200626 align = strtoul(arg, &endptr, 0);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900627 if (*endptr != '\0' || !is_power_of_2(align) || errno != 0)
628 log_errx("Invalid alignment: %s", arg);
629
630 return align;
631}
632
dp-arm516dfcb2016-11-03 13:59:26 +0000633static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
634{
635 char *p;
636
637 for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
638 if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
639 p += strlen("uuid=");
640 uuid_from_str(uuid, p);
641 } else if (strncmp(p, "file=", strlen("file=")) == 0) {
642 p += strlen("file=");
643 snprintf(filename, len, "%s", p);
644 }
645 }
646}
647
dp-arm4972ec52016-05-25 16:20:20 +0100648static int create_cmd(int argc, char *argv[])
649{
dp-arm90d2f0e2016-11-14 15:54:32 +0000650 struct option *opts = NULL;
651 size_t nr_opts = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100652 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900653 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100654
655 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100656 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100657
dp-arm90d2f0e2016-11-14 15:54:32 +0000658 opts = fill_common_opts(opts, &nr_opts, required_argument);
659 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100660 OPT_PLAT_TOC_FLAGS);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900661 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000662 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000663 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100664
665 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000666 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100667
dp-arm516dfcb2016-11-03 13:59:26 +0000668 c = getopt_long(argc, argv, "b:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100669 if (c == -1)
670 break;
671
672 switch (c) {
673 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000674 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100675
dp-arm90d2f0e2016-11-14 15:54:32 +0000676 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000677 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100678 break;
679 }
680 case OPT_PLAT_TOC_FLAGS:
681 parse_plat_toc_flags(optarg, &toc_flags);
682 break;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900683 case OPT_ALIGN:
684 align = get_image_align(optarg);
685 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000686 case 'b': {
687 char name[_UUID_STR_LEN + 1];
688 char filename[PATH_MAX] = { 0 };
689 uuid_t uuid = { 0 };
690 image_desc_t *desc;
691
692 parse_blob_opt(optarg, &uuid,
693 filename, sizeof(filename));
694
695 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
696 filename[0] == '\0')
697 create_usage();
698
699 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000700 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000701 uuid_to_str(name, sizeof(name), &uuid);
702 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000703 add_image_desc(desc);
704 }
dp-armfb732312016-12-30 09:55:48 +0000705 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000706 break;
707 }
dp-arm4972ec52016-05-25 16:20:20 +0100708 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100709 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100710 }
711 }
712 argc -= optind;
713 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000714 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100715
716 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100717 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100718
719 update_fip();
720
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900721 pack_images(argv[0], toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100722 return 0;
723}
724
725static void create_usage(void)
726{
727 toc_entry_t *toc_entry = toc_entries;
728
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900729 printf("fiptool create [opts] FIP_FILENAME\n");
730 printf("\n");
731 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900732 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900733 printf(" --blob uuid=...,file=...\tAdd an image with the given UUID pointed to by file.\n");
734 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 +0900735 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100736 printf("Specific images are packed with the following options:\n");
737 for (; toc_entry->cmdline_name != NULL; toc_entry++)
738 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
739 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100740 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100741}
742
743static int update_cmd(int argc, char *argv[])
744{
dp-arm90d2f0e2016-11-14 15:54:32 +0000745 struct option *opts = NULL;
746 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000747 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100748 fip_toc_header_t toc_header = { 0 };
749 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900750 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100751 int pflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100752
753 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100754 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100755
dp-arm90d2f0e2016-11-14 15:54:32 +0000756 opts = fill_common_opts(opts, &nr_opts, required_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900757 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000758 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000759 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
760 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100761 OPT_PLAT_TOC_FLAGS);
dp-arm90d2f0e2016-11-14 15:54:32 +0000762 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100763
764 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000765 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100766
dp-arm516dfcb2016-11-03 13:59:26 +0000767 c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100768 if (c == -1)
769 break;
770
771 switch (c) {
772 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000773 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100774
dp-arm90d2f0e2016-11-14 15:54:32 +0000775 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000776 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100777 break;
778 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000779 case OPT_PLAT_TOC_FLAGS:
dp-arm4972ec52016-05-25 16:20:20 +0100780 parse_plat_toc_flags(optarg, &toc_flags);
781 pflag = 1;
782 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000783 case 'b': {
784 char name[_UUID_STR_LEN + 1];
785 char filename[PATH_MAX] = { 0 };
786 uuid_t uuid = { 0 };
787 image_desc_t *desc;
788
789 parse_blob_opt(optarg, &uuid,
790 filename, sizeof(filename));
791
792 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
793 filename[0] == '\0')
794 update_usage();
795
796 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000797 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000798 uuid_to_str(name, sizeof(name), &uuid);
799 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000800 add_image_desc(desc);
801 }
dp-armfb732312016-12-30 09:55:48 +0000802 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000803 break;
804 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900805 case OPT_ALIGN:
806 align = get_image_align(optarg);
807 break;
dp-arm4972ec52016-05-25 16:20:20 +0100808 case 'o':
809 snprintf(outfile, sizeof(outfile), "%s", optarg);
810 break;
811 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100812 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100813 }
814 }
815 argc -= optind;
816 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000817 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100818
819 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100820 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100821
822 if (outfile[0] == '\0')
823 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
824
Masahiro Yamadaebb6e372016-12-25 12:41:41 +0900825 if (access(argv[0], F_OK) == 0)
dp-arm4972ec52016-05-25 16:20:20 +0100826 parse_fip(argv[0], &toc_header);
827
828 if (pflag)
829 toc_header.flags &= ~(0xffffULL << 32);
830 toc_flags = (toc_header.flags |= toc_flags);
831
832 update_fip();
833
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900834 pack_images(outfile, toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100835 return 0;
836}
837
838static void update_usage(void)
839{
840 toc_entry_t *toc_entry = toc_entries;
841
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900842 printf("fiptool update [opts] FIP_FILENAME\n");
843 printf("\n");
844 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900845 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900846 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 +0100847 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900848 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 +0900849 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100850 printf("Specific images are packed with the following options:\n");
851 for (; toc_entry->cmdline_name != NULL; toc_entry++)
852 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
853 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100854 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100855}
856
857static int unpack_cmd(int argc, char *argv[])
858{
dp-arm90d2f0e2016-11-14 15:54:32 +0000859 struct option *opts = NULL;
860 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000861 char outdir[PATH_MAX] = { 0 };
dp-arm90d2f0e2016-11-14 15:54:32 +0000862 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100863 int fflag = 0;
864 int unpack_all = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100865
866 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100867 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100868
dp-arm90d2f0e2016-11-14 15:54:32 +0000869 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000870 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000871 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
872 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
873 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100874
875 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000876 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100877
dp-arm516dfcb2016-11-03 13:59:26 +0000878 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100879 if (c == -1)
880 break;
881
882 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000883 case OPT_TOC_ENTRY: {
884 image_desc_t *desc;
885
886 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000887 set_image_desc_action(desc, DO_UNPACK, optarg);
dp-arm90d2f0e2016-11-14 15:54:32 +0000888 unpack_all = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100889 break;
dp-arm90d2f0e2016-11-14 15:54:32 +0000890 }
dp-arm516dfcb2016-11-03 13:59:26 +0000891 case 'b': {
892 char name[_UUID_STR_LEN + 1];
893 char filename[PATH_MAX] = { 0 };
894 uuid_t uuid = { 0 };
895 image_desc_t *desc;
896
897 parse_blob_opt(optarg, &uuid,
898 filename, sizeof(filename));
899
900 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
901 filename[0] == '\0')
902 unpack_usage();
903
904 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000905 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000906 uuid_to_str(name, sizeof(name), &uuid);
907 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000908 add_image_desc(desc);
909 }
dp-armfb732312016-12-30 09:55:48 +0000910 set_image_desc_action(desc, DO_UNPACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000911 unpack_all = 0;
912 break;
913 }
dp-arm4972ec52016-05-25 16:20:20 +0100914 case 'f':
915 fflag = 1;
916 break;
917 case 'o':
918 snprintf(outdir, sizeof(outdir), "%s", optarg);
919 break;
920 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100921 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100922 }
923 }
924 argc -= optind;
925 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000926 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100927
928 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100929 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100930
931 parse_fip(argv[0], NULL);
932
933 if (outdir[0] != '\0')
934 if (chdir(outdir) == -1)
935 log_err("chdir %s", outdir);
936
dp-arm4972ec52016-05-25 16:20:20 +0100937 /* Unpack all specified images. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000938 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +0000939 char file[PATH_MAX];
dp-armafa1efa2017-02-14 15:22:13 +0000940 image_t *image = desc->image;
dp-arm715ef422016-08-30 14:18:58 +0100941
dp-arm90d2f0e2016-11-14 15:54:32 +0000942 if (!unpack_all && desc->action != DO_UNPACK)
dp-arm4972ec52016-05-25 16:20:20 +0100943 continue;
944
945 /* Build filename. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000946 if (desc->action_arg == NULL)
dp-arm4972ec52016-05-25 16:20:20 +0100947 snprintf(file, sizeof(file), "%s.bin",
dp-arm90d2f0e2016-11-14 15:54:32 +0000948 desc->cmdline_name);
dp-arm4972ec52016-05-25 16:20:20 +0100949 else
950 snprintf(file, sizeof(file), "%s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000951 desc->action_arg);
dp-arm4972ec52016-05-25 16:20:20 +0100952
dp-arm715ef422016-08-30 14:18:58 +0100953 if (image == NULL) {
954 if (!unpack_all)
dp-arm516dfcb2016-11-03 13:59:26 +0000955 log_warnx("%s does not exist in %s",
dp-arm715ef422016-08-30 14:18:58 +0100956 file, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +0100957 continue;
958 }
959
960 if (access(file, F_OK) != 0 || fflag) {
961 if (verbose)
962 log_dbgx("Unpacking %s", file);
dp-arm715ef422016-08-30 14:18:58 +0100963 write_image_to_file(image, file);
dp-arm4972ec52016-05-25 16:20:20 +0100964 } else {
965 log_warnx("File %s already exists, use --force to overwrite it",
966 file);
967 }
dp-arm4972ec52016-05-25 16:20:20 +0100968 }
969
dp-arm4972ec52016-05-25 16:20:20 +0100970 return 0;
971}
972
973static void unpack_usage(void)
974{
975 toc_entry_t *toc_entry = toc_entries;
976
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900977 printf("fiptool unpack [opts] FIP_FILENAME\n");
978 printf("\n");
979 printf("Options:\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900980 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID to file.\n");
981 printf(" --force\t\t\tIf the output file already exists, use --force to overwrite it.\n");
dp-arm516dfcb2016-11-03 13:59:26 +0000982 printf(" --out path\t\t\tSet the output directory path.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +0900983 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100984 printf("Specific images are unpacked with the following options:\n");
985 for (; toc_entry->cmdline_name != NULL; toc_entry++)
986 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
987 toc_entry->name);
Masahiro Yamada1b900152017-02-02 16:34:14 +0900988 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100989 printf("If no options are provided, all images will be unpacked.\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100990 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100991}
992
993static int remove_cmd(int argc, char *argv[])
994{
dp-arm90d2f0e2016-11-14 15:54:32 +0000995 struct option *opts = NULL;
996 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000997 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100998 fip_toc_header_t toc_header;
dp-arm90d2f0e2016-11-14 15:54:32 +0000999 image_desc_t *desc;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001000 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +01001001 int fflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001002
1003 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +01001004 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001005
dp-arm90d2f0e2016-11-14 15:54:32 +00001006 opts = fill_common_opts(opts, &nr_opts, no_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001007 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +00001008 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +00001009 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
1010 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
1011 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +01001012
1013 while (1) {
dp-armfe92b892016-11-07 11:13:54 +00001014 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001015
dp-arm516dfcb2016-11-03 13:59:26 +00001016 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +01001017 if (c == -1)
1018 break;
1019
1020 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001021 case OPT_TOC_ENTRY: {
1022 image_desc_t *desc;
1023
1024 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +00001025 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm4972ec52016-05-25 16:20:20 +01001026 break;
dp-arm90d2f0e2016-11-14 15:54:32 +00001027 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001028 case OPT_ALIGN:
1029 align = get_image_align(optarg);
1030 break;
dp-arm516dfcb2016-11-03 13:59:26 +00001031 case 'b': {
1032 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
1033 uuid_t uuid = { 0 };
1034 image_desc_t *desc;
1035
1036 parse_blob_opt(optarg, &uuid,
1037 filename, sizeof(filename));
1038
1039 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
1040 remove_usage();
1041
1042 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +00001043 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +00001044 uuid_to_str(name, sizeof(name), &uuid);
1045 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +00001046 add_image_desc(desc);
1047 }
dp-armfb732312016-12-30 09:55:48 +00001048 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm516dfcb2016-11-03 13:59:26 +00001049 break;
1050 }
dp-arm4972ec52016-05-25 16:20:20 +01001051 case 'f':
1052 fflag = 1;
1053 break;
1054 case 'o':
1055 snprintf(outfile, sizeof(outfile), "%s", optarg);
1056 break;
1057 default:
dp-arm29f1b5c2016-09-15 09:58:50 +01001058 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001059 }
1060 }
1061 argc -= optind;
1062 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001063 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001064
1065 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001066 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001067
1068 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
1069 log_errx("File %s already exists, use --force to overwrite it",
1070 outfile);
1071
1072 if (outfile[0] == '\0')
1073 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
1074
1075 parse_fip(argv[0], &toc_header);
1076
dp-arm90d2f0e2016-11-14 15:54:32 +00001077 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001078 if (desc->action != DO_REMOVE)
dp-arm4972ec52016-05-25 16:20:20 +01001079 continue;
dp-arm516dfcb2016-11-03 13:59:26 +00001080
dp-armafa1efa2017-02-14 15:22:13 +00001081 if (desc->image != NULL) {
dp-arm4972ec52016-05-25 16:20:20 +01001082 if (verbose)
dp-arm516dfcb2016-11-03 13:59:26 +00001083 log_dbgx("Removing %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001084 desc->cmdline_name);
dp-armafa1efa2017-02-14 15:22:13 +00001085 free(desc->image);
1086 desc->image = NULL;
dp-arm4972ec52016-05-25 16:20:20 +01001087 } else {
dp-arm516dfcb2016-11-03 13:59:26 +00001088 log_warnx("%s does not exist in %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001089 desc->cmdline_name, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001090 }
1091 }
1092
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001093 pack_images(outfile, toc_header.flags, align);
dp-arm4972ec52016-05-25 16:20:20 +01001094 return 0;
1095}
1096
1097static void remove_usage(void)
1098{
1099 toc_entry_t *toc_entry = toc_entries;
1100
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001101 printf("fiptool remove [opts] FIP_FILENAME\n");
1102 printf("\n");
1103 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001104 printf(" --align <value>\tEach image is aligned to <value> (default: 1).\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001105 printf(" --blob uuid=...\tRemove an image with the given UUID.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +09001106 printf(" --force\t\tIf the output FIP file already exists, use --force to overwrite it.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001107 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001108 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001109 printf("Specific images are removed with the following options:\n");
1110 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1111 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
1112 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +01001113 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001114}
1115
1116static int version_cmd(int argc, char *argv[])
1117{
1118#ifdef VERSION
1119 puts(VERSION);
1120#else
1121 /* If built from fiptool directory, VERSION is not set. */
1122 puts("Unknown version");
1123#endif
1124 return 0;
1125}
1126
1127static void version_usage(void)
1128{
1129 printf("fiptool version\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001130 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001131}
1132
1133static int help_cmd(int argc, char *argv[])
1134{
1135 int i;
1136
1137 if (argc < 2)
1138 usage();
1139 argc--, argv++;
1140
1141 for (i = 0; i < NELEM(cmds); i++) {
1142 if (strcmp(cmds[i].name, argv[0]) == 0 &&
dp-arm29f1b5c2016-09-15 09:58:50 +01001143 cmds[i].usage != NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001144 cmds[i].usage();
dp-arm4972ec52016-05-25 16:20:20 +01001145 }
1146 if (i == NELEM(cmds))
1147 printf("No help for subcommand '%s'\n", argv[0]);
1148 return 0;
1149}
1150
1151static void usage(void)
1152{
Masahiro Yamada252c3362017-01-13 02:13:06 +09001153 printf("usage: fiptool [--verbose] <command> [<args>]\n");
dp-arm4972ec52016-05-25 16:20:20 +01001154 printf("Global options supported:\n");
1155 printf(" --verbose\tEnable verbose output for all commands.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001156 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001157 printf("Commands supported:\n");
1158 printf(" info\t\tList images contained in FIP.\n");
1159 printf(" create\tCreate a new FIP with the given images.\n");
1160 printf(" update\tUpdate an existing FIP with the given images.\n");
1161 printf(" unpack\tUnpack images from FIP.\n");
1162 printf(" remove\tRemove images from FIP.\n");
1163 printf(" version\tShow fiptool version.\n");
1164 printf(" help\t\tShow help for given command.\n");
1165 exit(1);
1166}
1167
1168int main(int argc, char *argv[])
1169{
1170 int i, ret = 0;
1171
dp-arm5cd10ae2016-11-07 10:45:59 +00001172 while (1) {
1173 int c, opt_index = 0;
1174 static struct option opts[] = {
1175 { "verbose", no_argument, NULL, 'v' },
1176 { NULL, no_argument, NULL, 0 }
1177 };
1178
1179 /*
1180 * Set POSIX mode so getopt stops at the first non-option
1181 * which is the subcommand.
1182 */
1183 c = getopt_long(argc, argv, "+v", opts, &opt_index);
1184 if (c == -1)
1185 break;
dp-arm4972ec52016-05-25 16:20:20 +01001186
dp-arm5cd10ae2016-11-07 10:45:59 +00001187 switch (c) {
1188 case 'v':
1189 verbose = 1;
1190 break;
1191 default:
Masahiro Yamada48a24972016-10-26 13:24:26 +09001192 usage();
dp-arm5cd10ae2016-11-07 10:45:59 +00001193 }
dp-arm4972ec52016-05-25 16:20:20 +01001194 }
dp-arm5cd10ae2016-11-07 10:45:59 +00001195 argc -= optind;
1196 argv += optind;
1197 /* Reset optind for subsequent getopt processing. */
1198 optind = 0;
1199
1200 if (argc == 0)
1201 usage();
dp-arm4972ec52016-05-25 16:20:20 +01001202
dp-arm90d2f0e2016-11-14 15:54:32 +00001203 fill_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001204 for (i = 0; i < NELEM(cmds); i++) {
1205 if (strcmp(cmds[i].name, argv[0]) == 0) {
1206 ret = cmds[i].handler(argc, argv);
1207 break;
1208 }
1209 }
1210 if (i == NELEM(cmds))
1211 usage();
dp-arm90d2f0e2016-11-14 15:54:32 +00001212 free_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001213 return ret;
1214}