blob: e70ff364b738a593e954b68abf2802e849b248b8 [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>
dp-arm4972ec52016-05-25 16:20:20 +010012#include <limits.h>
13#include <stdarg.h>
14#include <stdint.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
Masahiro Yamadae3a2b312017-05-08 18:29:03 +090018
dp-arm4972ec52016-05-25 16:20:20 +010019#include "fiptool.h"
dp-arm4972ec52016-05-25 16:20:20 +010020#include "tbbr_config.h"
21
22#define OPT_TOC_ENTRY 0
23#define OPT_PLAT_TOC_FLAGS 1
Masahiro Yamada4d87eb42016-12-25 13:52:22 +090024#define OPT_ALIGN 2
dp-arm4972ec52016-05-25 16:20:20 +010025
26static int info_cmd(int argc, char *argv[]);
27static void info_usage(void);
28static int create_cmd(int argc, char *argv[]);
29static void create_usage(void);
30static int update_cmd(int argc, char *argv[]);
31static void update_usage(void);
32static int unpack_cmd(int argc, char *argv[]);
33static void unpack_usage(void);
34static int remove_cmd(int argc, char *argv[]);
35static void remove_usage(void);
36static int version_cmd(int argc, char *argv[]);
37static void version_usage(void);
38static int help_cmd(int argc, char *argv[]);
39static void usage(void);
40
41/* Available subcommands. */
42static cmd_t cmds[] = {
43 { .name = "info", .handler = info_cmd, .usage = info_usage },
44 { .name = "create", .handler = create_cmd, .usage = create_usage },
45 { .name = "update", .handler = update_cmd, .usage = update_usage },
46 { .name = "unpack", .handler = unpack_cmd, .usage = unpack_usage },
47 { .name = "remove", .handler = remove_cmd, .usage = remove_usage },
48 { .name = "version", .handler = version_cmd, .usage = version_usage },
49 { .name = "help", .handler = help_cmd, .usage = NULL },
50};
51
dp-arm90d2f0e2016-11-14 15:54:32 +000052static image_desc_t *image_desc_head;
53static size_t nr_image_descs;
dp-arm4972ec52016-05-25 16:20:20 +010054static uuid_t uuid_null = { 0 };
55static int verbose;
56
dp-armc1f8e772016-11-04 10:52:25 +000057static void vlog(int prio, const char *msg, va_list ap)
dp-arm4972ec52016-05-25 16:20:20 +010058{
59 char *prefix[] = { "DEBUG", "WARN", "ERROR" };
60
61 fprintf(stderr, "%s: ", prefix[prio]);
62 vfprintf(stderr, msg, ap);
63 fputc('\n', stderr);
64}
65
dp-armc1f8e772016-11-04 10:52:25 +000066static void log_dbgx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010067{
68 va_list ap;
69
70 va_start(ap, msg);
71 vlog(LOG_DBG, msg, ap);
72 va_end(ap);
73}
74
dp-armc1f8e772016-11-04 10:52:25 +000075static void log_warnx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010076{
77 va_list ap;
78
79 va_start(ap, msg);
80 vlog(LOG_WARN, msg, ap);
81 va_end(ap);
82}
83
dp-armc1f8e772016-11-04 10:52:25 +000084static void log_err(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010085{
86 char buf[512];
87 va_list ap;
88
89 va_start(ap, msg);
90 snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
91 vlog(LOG_ERR, buf, ap);
92 va_end(ap);
93 exit(1);
94}
95
dp-armc1f8e772016-11-04 10:52:25 +000096static void log_errx(const char *msg, ...)
dp-arm4972ec52016-05-25 16:20:20 +010097{
98 va_list ap;
99
100 va_start(ap, msg);
101 vlog(LOG_ERR, msg, ap);
102 va_end(ap);
103 exit(1);
104}
105
dp-armdb0f5e92016-11-04 10:56:25 +0000106static char *xstrdup(const char *s, const char *msg)
107{
108 char *d;
109
110 d = strdup(s);
111 if (d == NULL)
dp-arm5ba38542016-12-21 14:59:30 +0000112 log_errx("strdup: %s", msg);
dp-armdb0f5e92016-11-04 10:56:25 +0000113 return d;
114}
115
116static void *xmalloc(size_t size, const char *msg)
117{
118 void *d;
119
120 d = malloc(size);
121 if (d == NULL)
dp-arm5ba38542016-12-21 14:59:30 +0000122 log_errx("malloc: %s", msg);
dp-armdb0f5e92016-11-04 10:56:25 +0000123 return d;
124}
125
Masahiro Yamada03fdf692017-01-15 00:50:41 +0900126static void *xzalloc(size_t size, const char *msg)
127{
128 return memset(xmalloc(size, msg), 0, size);
129}
130
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900131static void xfwrite(void *buf, size_t size, FILE *fp, const char *filename)
132{
133 if (fwrite(buf, 1, size, fp) != size)
134 log_errx("Failed to write %s", filename);
135}
136
dp-arm90d2f0e2016-11-14 15:54:32 +0000137static image_desc_t *new_image_desc(const uuid_t *uuid,
138 const char *name, const char *cmdline_name)
dp-arm4972ec52016-05-25 16:20:20 +0100139{
dp-arm90d2f0e2016-11-14 15:54:32 +0000140 image_desc_t *desc;
141
Masahiro Yamada03fdf692017-01-15 00:50:41 +0900142 desc = xzalloc(sizeof(*desc),
dp-arm90d2f0e2016-11-14 15:54:32 +0000143 "failed to allocate memory for image descriptor");
144 memcpy(&desc->uuid, uuid, sizeof(uuid_t));
145 desc->name = xstrdup(name,
146 "failed to allocate memory for image name");
147 desc->cmdline_name = xstrdup(cmdline_name,
148 "failed to allocate memory for image command line name");
149 desc->action = DO_UNSPEC;
dp-arm90d2f0e2016-11-14 15:54:32 +0000150 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100151}
152
dp-armfb732312016-12-30 09:55:48 +0000153static void set_image_desc_action(image_desc_t *desc, int action,
154 const char *arg)
155{
156 assert(desc != NULL);
157
Evan Lloyd04dc3442017-05-25 19:06:47 +0100158 if (desc->action_arg != (char *)DO_UNSPEC)
dp-armfb732312016-12-30 09:55:48 +0000159 free(desc->action_arg);
160 desc->action = action;
161 desc->action_arg = NULL;
162 if (arg != NULL)
163 desc->action_arg = xstrdup(arg,
164 "failed to allocate memory for argument");
165}
166
dp-arm90d2f0e2016-11-14 15:54:32 +0000167static void free_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100168{
dp-arm90d2f0e2016-11-14 15:54:32 +0000169 free(desc->name);
170 free(desc->cmdline_name);
171 free(desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000172 free(desc->image);
dp-arm90d2f0e2016-11-14 15:54:32 +0000173 free(desc);
dp-arm4972ec52016-05-25 16:20:20 +0100174}
175
dp-arm90d2f0e2016-11-14 15:54:32 +0000176static void add_image_desc(image_desc_t *desc)
dp-arm4972ec52016-05-25 16:20:20 +0100177{
Masahiro Yamadad224b452017-01-14 23:22:02 +0900178 image_desc_t **p = &image_desc_head;
179
Masahiro Yamadad224b452017-01-14 23:22:02 +0900180 while (*p)
181 p = &(*p)->next;
182
Masahiro Yamadac2a7d9c2017-01-27 12:53:13 +0900183 assert(*p == NULL);
Masahiro Yamadad224b452017-01-14 23:22:02 +0900184 *p = desc;
dp-arm90d2f0e2016-11-14 15:54:32 +0000185 nr_image_descs++;
186}
dp-arm4972ec52016-05-25 16:20:20 +0100187
dp-arm90d2f0e2016-11-14 15:54:32 +0000188static void free_image_descs(void)
189{
190 image_desc_t *desc = image_desc_head, *tmp;
191
192 while (desc != NULL) {
193 tmp = desc->next;
194 free_image_desc(desc);
195 desc = tmp;
196 nr_image_descs--;
dp-arm4972ec52016-05-25 16:20:20 +0100197 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000198 assert(nr_image_descs == 0);
dp-arm4972ec52016-05-25 16:20:20 +0100199}
200
dp-arm90d2f0e2016-11-14 15:54:32 +0000201static void fill_image_descs(void)
202{
203 toc_entry_t *toc_entry;
204
205 for (toc_entry = toc_entries;
206 toc_entry->cmdline_name != NULL;
207 toc_entry++) {
208 image_desc_t *desc;
209
210 desc = new_image_desc(&toc_entry->uuid,
211 toc_entry->name,
212 toc_entry->cmdline_name);
213 add_image_desc(desc);
214 }
215}
216
dp-arm90d2f0e2016-11-14 15:54:32 +0000217static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid)
dp-arm4972ec52016-05-25 16:20:20 +0100218{
dp-arm90d2f0e2016-11-14 15:54:32 +0000219 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100220
dp-arm90d2f0e2016-11-14 15:54:32 +0000221 for (desc = image_desc_head; desc != NULL; desc = desc->next)
222 if (memcmp(&desc->uuid, uuid, sizeof(uuid_t)) == 0)
223 return desc;
dp-arm4972ec52016-05-25 16:20:20 +0100224 return NULL;
225}
226
dp-arm90d2f0e2016-11-14 15:54:32 +0000227static image_desc_t *lookup_image_desc_from_opt(const char *opt)
228{
229 image_desc_t *desc;
230
231 for (desc = image_desc_head; desc != NULL; desc = desc->next)
232 if (strcmp(desc->cmdline_name, opt) == 0)
233 return desc;
234 return NULL;
235}
236
dp-arm516dfcb2016-11-03 13:59:26 +0000237static void uuid_to_str(char *s, size_t len, const uuid_t *u)
238{
239 assert(len >= (_UUID_STR_LEN + 1));
240
241 snprintf(s, len, "%08X-%04X-%04X-%04X-%04X%04X%04X",
242 u->time_low,
243 u->time_mid,
244 u->time_hi_and_version,
245 ((uint16_t)u->clock_seq_hi_and_reserved << 8) | u->clock_seq_low,
246 ((uint16_t)u->node[0] << 8) | u->node[1],
247 ((uint16_t)u->node[2] << 8) | u->node[3],
248 ((uint16_t)u->node[4] << 8) | u->node[5]);
249}
250
251static void uuid_from_str(uuid_t *u, const char *s)
252{
253 int n;
254
255 if (s == NULL)
256 log_errx("UUID cannot be NULL");
257 if (strlen(s) != _UUID_STR_LEN)
258 log_errx("Invalid UUID: %s", s);
259
260 n = sscanf(s,
261 "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
262 &u->time_low, &u->time_mid, &u->time_hi_and_version,
263 &u->clock_seq_hi_and_reserved, &u->clock_seq_low, &u->node[0],
264 &u->node[1], &u->node[2], &u->node[3], &u->node[4], &u->node[5]);
265 /*
266 * Given the format specifier above, we expect 11 items to be scanned
267 * for a properly formatted UUID.
268 */
269 if (n != 11)
270 log_errx("Invalid UUID: %s", s);
271}
272
dp-armc1f8e772016-11-04 10:52:25 +0000273static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
dp-arm4972ec52016-05-25 16:20:20 +0100274{
Evan Lloyd04dc3442017-05-25 19:06:47 +0100275 struct BLD_PLAT_STAT st;
dp-arm4972ec52016-05-25 16:20:20 +0100276 FILE *fp;
277 char *buf, *bufend;
278 fip_toc_header_t *toc_header;
279 fip_toc_entry_t *toc_entry;
dp-arm4972ec52016-05-25 16:20:20 +0100280 int terminated = 0;
281
Evan Lloydb1939392017-01-13 14:13:09 +0000282 fp = fopen(filename, "rb");
dp-arm4972ec52016-05-25 16:20:20 +0100283 if (fp == NULL)
284 log_err("fopen %s", filename);
285
286 if (fstat(fileno(fp), &st) == -1)
287 log_err("fstat %s", filename);
288
dp-armdb0f5e92016-11-04 10:56:25 +0000289 buf = xmalloc(st.st_size, "failed to load file into memory");
dp-arm4972ec52016-05-25 16:20:20 +0100290 if (fread(buf, 1, st.st_size, fp) != st.st_size)
291 log_errx("Failed to read %s", filename);
292 bufend = buf + st.st_size;
293 fclose(fp);
294
295 if (st.st_size < sizeof(fip_toc_header_t))
296 log_errx("FIP %s is truncated", filename);
297
298 toc_header = (fip_toc_header_t *)buf;
299 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
300
301 if (toc_header->name != TOC_HEADER_NAME)
302 log_errx("%s is not a FIP file", filename);
303
304 /* Return the ToC header if the caller wants it. */
305 if (toc_header_out != NULL)
306 *toc_header_out = *toc_header;
307
308 /* Walk through each ToC entry in the file. */
309 while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
dp-arm516dfcb2016-11-03 13:59:26 +0000310 image_t *image;
311 image_desc_t *desc;
312
dp-arm4972ec52016-05-25 16:20:20 +0100313 /* Found the ToC terminator, we are done. */
314 if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
315 terminated = 1;
316 break;
317 }
318
319 /*
320 * Build a new image out of the ToC entry and add it to the
321 * table of images.
322 */
Masahiro Yamadad224b452017-01-14 23:22:02 +0900323 image = xzalloc(sizeof(*image),
dp-armdb0f5e92016-11-04 10:56:25 +0000324 "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900325 image->toc_e = *toc_entry;
dp-armdb0f5e92016-11-04 10:56:25 +0000326 image->buffer = xmalloc(toc_entry->size,
327 "failed to allocate image buffer, is FIP file corrupted?");
dp-arm4972ec52016-05-25 16:20:20 +0100328 /* Overflow checks before memory copy. */
329 if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
330 log_errx("FIP %s is corrupted", filename);
331 if (toc_entry->size + toc_entry->offset_address > st.st_size)
332 log_errx("FIP %s is corrupted", filename);
333
334 memcpy(image->buffer, buf + toc_entry->offset_address,
335 toc_entry->size);
dp-arm4972ec52016-05-25 16:20:20 +0100336
dp-arm516dfcb2016-11-03 13:59:26 +0000337 /* If this is an unknown image, create a descriptor for it. */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900338 desc = lookup_image_desc_from_uuid(&toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000339 if (desc == NULL) {
340 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
341
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900342 uuid_to_str(name, sizeof(name), &toc_entry->uuid);
dp-arm516dfcb2016-11-03 13:59:26 +0000343 snprintf(filename, sizeof(filename), "%s%s",
344 name, ".bin");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900345 desc = new_image_desc(&toc_entry->uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000346 desc->action = DO_UNPACK;
347 desc->action_arg = xstrdup(filename,
348 "failed to allocate memory for blob filename");
349 add_image_desc(desc);
350 }
351
dp-armafa1efa2017-02-14 15:22:13 +0000352 assert(desc->image == NULL);
353 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100354
355 toc_entry++;
356 }
357
358 if (terminated == 0)
359 log_errx("FIP %s does not have a ToC terminator entry",
360 filename);
361 free(buf);
362 return 0;
363}
364
dp-armc1f8e772016-11-04 10:52:25 +0000365static image_t *read_image_from_file(const uuid_t *uuid, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100366{
Evan Lloyd04dc3442017-05-25 19:06:47 +0100367 struct BLD_PLAT_STAT st;
dp-arm4972ec52016-05-25 16:20:20 +0100368 image_t *image;
369 FILE *fp;
370
dp-arm715ef422016-08-30 14:18:58 +0100371 assert(uuid != NULL);
Evan Lloyd04dc3442017-05-25 19:06:47 +0100372 assert(filename != NULL);
dp-arm715ef422016-08-30 14:18:58 +0100373
Evan Lloydb1939392017-01-13 14:13:09 +0000374 fp = fopen(filename, "rb");
dp-arm4972ec52016-05-25 16:20:20 +0100375 if (fp == NULL)
376 log_err("fopen %s", filename);
377
378 if (fstat(fileno(fp), &st) == -1)
379 log_errx("fstat %s", filename);
380
Masahiro Yamadad224b452017-01-14 23:22:02 +0900381 image = xzalloc(sizeof(*image), "failed to allocate memory for image");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900382 image->toc_e.uuid = *uuid;
dp-armdb0f5e92016-11-04 10:56:25 +0000383 image->buffer = xmalloc(st.st_size, "failed to allocate image buffer");
dp-arm4972ec52016-05-25 16:20:20 +0100384 if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
385 log_errx("Failed to read %s", filename);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900386 image->toc_e.size = st.st_size;
dp-arm4972ec52016-05-25 16:20:20 +0100387
388 fclose(fp);
389 return image;
390}
391
dp-armc1f8e772016-11-04 10:52:25 +0000392static int write_image_to_file(const image_t *image, const char *filename)
dp-arm4972ec52016-05-25 16:20:20 +0100393{
394 FILE *fp;
395
Evan Lloydb1939392017-01-13 14:13:09 +0000396 fp = fopen(filename, "wb");
dp-arm4972ec52016-05-25 16:20:20 +0100397 if (fp == NULL)
398 log_err("fopen");
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900399 xfwrite(image->buffer, image->toc_e.size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100400 fclose(fp);
401 return 0;
402}
403
dp-arm90d2f0e2016-11-14 15:54:32 +0000404static struct option *add_opt(struct option *opts, size_t *nr_opts,
405 const char *name, int has_arg, int val)
dp-arm4972ec52016-05-25 16:20:20 +0100406{
dp-arm90d2f0e2016-11-14 15:54:32 +0000407 opts = realloc(opts, (*nr_opts + 1) * sizeof(*opts));
408 if (opts == NULL)
409 log_err("realloc");
410 opts[*nr_opts].name = name;
411 opts[*nr_opts].has_arg = has_arg;
412 opts[*nr_opts].flag = NULL;
413 opts[*nr_opts].val = val;
414 ++*nr_opts;
415 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100416}
417
dp-arm90d2f0e2016-11-14 15:54:32 +0000418static struct option *fill_common_opts(struct option *opts, size_t *nr_opts,
419 int has_arg)
dp-arm4972ec52016-05-25 16:20:20 +0100420{
dp-arm90d2f0e2016-11-14 15:54:32 +0000421 image_desc_t *desc;
422
423 for (desc = image_desc_head; desc != NULL; desc = desc->next)
424 opts = add_opt(opts, nr_opts, desc->cmdline_name, has_arg,
425 OPT_TOC_ENTRY);
426 return opts;
dp-arm4972ec52016-05-25 16:20:20 +0100427}
428
dp-arm90d2f0e2016-11-14 15:54:32 +0000429static void md_print(const unsigned char *md, size_t len)
dp-arm12e893b2016-08-24 13:21:08 +0100430{
431 size_t i;
432
433 for (i = 0; i < len; i++)
434 printf("%02x", md[i]);
435}
436
dp-arm4972ec52016-05-25 16:20:20 +0100437static int info_cmd(int argc, char *argv[])
438{
dp-armafa1efa2017-02-14 15:22:13 +0000439 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100440 fip_toc_header_t toc_header;
dp-arm4972ec52016-05-25 16:20:20 +0100441
442 if (argc != 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100443 info_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100444 argc--, argv++;
445
446 parse_fip(argv[0], &toc_header);
447
448 if (verbose) {
449 log_dbgx("toc_header[name]: 0x%llX",
450 (unsigned long long)toc_header.name);
451 log_dbgx("toc_header[serial_number]: 0x%llX",
452 (unsigned long long)toc_header.serial_number);
453 log_dbgx("toc_header[flags]: 0x%llX",
454 (unsigned long long)toc_header.flags);
455 }
456
dp-armafa1efa2017-02-14 15:22:13 +0000457 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
458 image_t *image = desc->image;
dp-arm715ef422016-08-30 14:18:58 +0100459
dp-armafa1efa2017-02-14 15:22:13 +0000460 if (image == NULL)
461 continue;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900462 printf("%s: offset=0x%llX, size=0x%llX, cmdline=\"--%s\"",
463 desc->name,
464 (unsigned long long)image->toc_e.offset_address,
465 (unsigned long long)image->toc_e.size,
Masahiro Yamadaeee39312017-01-15 23:20:00 +0900466 desc->cmdline_name);
Evan Lloyd04dc3442017-05-25 19:06:47 +0100467#ifndef _MSC_VER /* We don't have SHA256 for Visual Studio. */
dp-arm12e893b2016-08-24 13:21:08 +0100468 if (verbose) {
469 unsigned char md[SHA256_DIGEST_LENGTH];
470
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900471 SHA256(image->buffer, image->toc_e.size, md);
dp-arm12e893b2016-08-24 13:21:08 +0100472 printf(", sha256=");
473 md_print(md, sizeof(md));
474 }
Evan Lloyd04dc3442017-05-25 19:06:47 +0100475#endif
dp-arm12e893b2016-08-24 13:21:08 +0100476 putchar('\n');
dp-arm4972ec52016-05-25 16:20:20 +0100477 }
478
dp-arm4972ec52016-05-25 16:20:20 +0100479 return 0;
480}
481
482static void info_usage(void)
483{
484 printf("fiptool info FIP_FILENAME\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100485 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100486}
487
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900488static int pack_images(const char *filename, uint64_t toc_flags, unsigned long align)
dp-arm4972ec52016-05-25 16:20:20 +0100489{
490 FILE *fp;
dp-armafa1efa2017-02-14 15:22:13 +0000491 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100492 fip_toc_header_t *toc_header;
493 fip_toc_entry_t *toc_entry;
494 char *buf;
Roberto Vargas81935762017-12-19 11:56:57 +0000495 uint64_t entry_offset, buf_size, payload_size = 0, pad_size;
dp-armafa1efa2017-02-14 15:22:13 +0000496 size_t nr_images = 0;
497
498 for (desc = image_desc_head; desc != NULL; desc = desc->next)
499 if (desc->image != NULL)
500 nr_images++;
dp-arm4972ec52016-05-25 16:20:20 +0100501
502 buf_size = sizeof(fip_toc_header_t) +
503 sizeof(fip_toc_entry_t) * (nr_images + 1);
504 buf = calloc(1, buf_size);
505 if (buf == NULL)
506 log_err("calloc");
507
508 /* Build up header and ToC entries from the image table. */
509 toc_header = (fip_toc_header_t *)buf;
510 toc_header->name = TOC_HEADER_NAME;
511 toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
512 toc_header->flags = toc_flags;
513
514 toc_entry = (fip_toc_entry_t *)(toc_header + 1);
515
516 entry_offset = buf_size;
dp-armafa1efa2017-02-14 15:22:13 +0000517 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
518 image_t *image = desc->image;
519
520 if (image == NULL)
521 continue;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900522 payload_size += image->toc_e.size;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900523 entry_offset = (entry_offset + align - 1) & ~(align - 1);
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900524 image->toc_e.offset_address = entry_offset;
525 *toc_entry++ = image->toc_e;
526 entry_offset += image->toc_e.size;
dp-arm4972ec52016-05-25 16:20:20 +0100527 }
528
Roberto Vargas81935762017-12-19 11:56:57 +0000529 /*
530 * Append a null uuid entry to mark the end of ToC entries.
531 * NOTE the offset address for the last toc_entry must match the fip
532 * size.
533 */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900534 memset(toc_entry, 0, sizeof(*toc_entry));
Roberto Vargas81935762017-12-19 11:56:57 +0000535 toc_entry->offset_address = (entry_offset + align - 1) & ~(align - 1);
dp-arm4972ec52016-05-25 16:20:20 +0100536
537 /* Generate the FIP file. */
Evan Lloydb1939392017-01-13 14:13:09 +0000538 fp = fopen(filename, "wb");
dp-arm4972ec52016-05-25 16:20:20 +0100539 if (fp == NULL)
540 log_err("fopen %s", filename);
541
542 if (verbose)
543 log_dbgx("Metadata size: %zu bytes", buf_size);
544
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900545 xfwrite(buf, buf_size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100546
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
Roberto Vargas81935762017-12-19 11:56:57 +0000561 if (fseek(fp, entry_offset, SEEK_SET))
562 log_errx("Failed to set file position");
563
564 pad_size = toc_entry->offset_address - entry_offset;
565 while (pad_size--)
566 fputc(0x0, fp);
567
Andreas Färber860b5dc2018-01-27 16:46:59 +0100568 free(buf);
dp-arm4972ec52016-05-25 16:20:20 +0100569 fclose(fp);
570 return 0;
571}
572
573/*
574 * This function is shared between the create and update subcommands.
575 * The difference between the two subcommands is that when the FIP file
576 * is created, the parsing of an existing FIP is skipped. This results
577 * in update_fip() creating the new FIP file from scratch because the
578 * internal image table is not populated.
579 */
580static void update_fip(void)
581{
dp-arm90d2f0e2016-11-14 15:54:32 +0000582 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100583
584 /* Add or replace images in the FIP file. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000585 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-armafa1efa2017-02-14 15:22:13 +0000586 image_t *image;
dp-arm516dfcb2016-11-03 13:59:26 +0000587
dp-arm90d2f0e2016-11-14 15:54:32 +0000588 if (desc->action != DO_PACK)
dp-arm4972ec52016-05-25 16:20:20 +0100589 continue;
590
dp-armafa1efa2017-02-14 15:22:13 +0000591 image = read_image_from_file(&desc->uuid,
dp-arm90d2f0e2016-11-14 15:54:32 +0000592 desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000593 if (desc->image != NULL) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000594 if (verbose) {
dp-arm516dfcb2016-11-03 13:59:26 +0000595 log_dbgx("Replacing %s with %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000596 desc->cmdline_name,
597 desc->action_arg);
598 }
dp-armafa1efa2017-02-14 15:22:13 +0000599 free(desc->image);
600 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100601 } else {
602 if (verbose)
603 log_dbgx("Adding image %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000604 desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000605 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100606 }
dp-arm4972ec52016-05-25 16:20:20 +0100607 }
608}
609
dp-arm90d2f0e2016-11-14 15:54:32 +0000610static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
dp-arm4972ec52016-05-25 16:20:20 +0100611{
612 unsigned long long flags;
613 char *endptr;
614
615 errno = 0;
616 flags = strtoull(arg, &endptr, 16);
617 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
618 log_errx("Invalid platform ToC flags: %s", arg);
619 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
620 *toc_flags |= flags << 32;
621}
622
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900623static int is_power_of_2(unsigned long x)
624{
625 return x && !(x & (x - 1));
626}
627
628static unsigned long get_image_align(char *arg)
629{
630 char *endptr;
631 unsigned long align;
632
633 errno = 0;
Andreas Färber242a7b72017-04-21 19:39:10 +0200634 align = strtoul(arg, &endptr, 0);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900635 if (*endptr != '\0' || !is_power_of_2(align) || errno != 0)
636 log_errx("Invalid alignment: %s", arg);
637
638 return align;
639}
640
dp-arm516dfcb2016-11-03 13:59:26 +0000641static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
642{
643 char *p;
644
645 for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
646 if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
647 p += strlen("uuid=");
648 uuid_from_str(uuid, p);
649 } else if (strncmp(p, "file=", strlen("file=")) == 0) {
650 p += strlen("file=");
651 snprintf(filename, len, "%s", p);
652 }
653 }
654}
655
dp-arm4972ec52016-05-25 16:20:20 +0100656static int create_cmd(int argc, char *argv[])
657{
dp-arm90d2f0e2016-11-14 15:54:32 +0000658 struct option *opts = NULL;
659 size_t nr_opts = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100660 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900661 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100662
663 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100664 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100665
dp-arm90d2f0e2016-11-14 15:54:32 +0000666 opts = fill_common_opts(opts, &nr_opts, required_argument);
667 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100668 OPT_PLAT_TOC_FLAGS);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900669 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000670 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000671 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100672
673 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000674 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100675
dp-arm516dfcb2016-11-03 13:59:26 +0000676 c = getopt_long(argc, argv, "b:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100677 if (c == -1)
678 break;
679
680 switch (c) {
681 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000682 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100683
dp-arm90d2f0e2016-11-14 15:54:32 +0000684 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000685 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100686 break;
687 }
688 case OPT_PLAT_TOC_FLAGS:
689 parse_plat_toc_flags(optarg, &toc_flags);
690 break;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900691 case OPT_ALIGN:
692 align = get_image_align(optarg);
693 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000694 case 'b': {
695 char name[_UUID_STR_LEN + 1];
696 char filename[PATH_MAX] = { 0 };
697 uuid_t uuid = { 0 };
698 image_desc_t *desc;
699
700 parse_blob_opt(optarg, &uuid,
701 filename, sizeof(filename));
702
703 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
704 filename[0] == '\0')
705 create_usage();
706
707 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000708 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000709 uuid_to_str(name, sizeof(name), &uuid);
710 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000711 add_image_desc(desc);
712 }
dp-armfb732312016-12-30 09:55:48 +0000713 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000714 break;
715 }
dp-arm4972ec52016-05-25 16:20:20 +0100716 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100717 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100718 }
719 }
720 argc -= optind;
721 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000722 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100723
724 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100725 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100726
727 update_fip();
728
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900729 pack_images(argv[0], toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100730 return 0;
731}
732
733static void create_usage(void)
734{
735 toc_entry_t *toc_entry = toc_entries;
736
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900737 printf("fiptool create [opts] FIP_FILENAME\n");
738 printf("\n");
739 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900740 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900741 printf(" --blob uuid=...,file=...\tAdd an image with the given UUID pointed to by file.\n");
742 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 +0900743 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100744 printf("Specific images are packed with the following options:\n");
745 for (; toc_entry->cmdline_name != NULL; toc_entry++)
746 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
747 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100748 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100749}
750
751static int update_cmd(int argc, char *argv[])
752{
dp-arm90d2f0e2016-11-14 15:54:32 +0000753 struct option *opts = NULL;
754 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000755 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100756 fip_toc_header_t toc_header = { 0 };
757 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900758 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100759 int pflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100760
761 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100762 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100763
dp-arm90d2f0e2016-11-14 15:54:32 +0000764 opts = fill_common_opts(opts, &nr_opts, required_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900765 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000766 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000767 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
768 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100769 OPT_PLAT_TOC_FLAGS);
dp-arm90d2f0e2016-11-14 15:54:32 +0000770 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100771
772 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000773 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100774
dp-arm516dfcb2016-11-03 13:59:26 +0000775 c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100776 if (c == -1)
777 break;
778
779 switch (c) {
780 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000781 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100782
dp-arm90d2f0e2016-11-14 15:54:32 +0000783 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000784 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100785 break;
786 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000787 case OPT_PLAT_TOC_FLAGS:
dp-arm4972ec52016-05-25 16:20:20 +0100788 parse_plat_toc_flags(optarg, &toc_flags);
789 pflag = 1;
790 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000791 case 'b': {
792 char name[_UUID_STR_LEN + 1];
793 char filename[PATH_MAX] = { 0 };
794 uuid_t uuid = { 0 };
795 image_desc_t *desc;
796
797 parse_blob_opt(optarg, &uuid,
798 filename, sizeof(filename));
799
800 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
801 filename[0] == '\0')
802 update_usage();
803
804 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000805 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000806 uuid_to_str(name, sizeof(name), &uuid);
807 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000808 add_image_desc(desc);
809 }
dp-armfb732312016-12-30 09:55:48 +0000810 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000811 break;
812 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900813 case OPT_ALIGN:
814 align = get_image_align(optarg);
815 break;
dp-arm4972ec52016-05-25 16:20:20 +0100816 case 'o':
817 snprintf(outfile, sizeof(outfile), "%s", optarg);
818 break;
819 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100820 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100821 }
822 }
823 argc -= optind;
824 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000825 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100826
827 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100828 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100829
830 if (outfile[0] == '\0')
831 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
832
Masahiro Yamadaebb6e372016-12-25 12:41:41 +0900833 if (access(argv[0], F_OK) == 0)
dp-arm4972ec52016-05-25 16:20:20 +0100834 parse_fip(argv[0], &toc_header);
835
836 if (pflag)
837 toc_header.flags &= ~(0xffffULL << 32);
838 toc_flags = (toc_header.flags |= toc_flags);
839
840 update_fip();
841
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900842 pack_images(outfile, toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100843 return 0;
844}
845
846static void update_usage(void)
847{
848 toc_entry_t *toc_entry = toc_entries;
849
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900850 printf("fiptool update [opts] FIP_FILENAME\n");
851 printf("\n");
852 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900853 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900854 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 +0100855 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900856 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 +0900857 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100858 printf("Specific images are packed with the following options:\n");
859 for (; toc_entry->cmdline_name != NULL; toc_entry++)
860 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
861 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100862 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100863}
864
865static int unpack_cmd(int argc, char *argv[])
866{
dp-arm90d2f0e2016-11-14 15:54:32 +0000867 struct option *opts = NULL;
868 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000869 char outdir[PATH_MAX] = { 0 };
dp-arm90d2f0e2016-11-14 15:54:32 +0000870 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100871 int fflag = 0;
872 int unpack_all = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100873
874 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100875 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100876
dp-arm90d2f0e2016-11-14 15:54:32 +0000877 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000878 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000879 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
880 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
881 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100882
883 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000884 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100885
dp-arm516dfcb2016-11-03 13:59:26 +0000886 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100887 if (c == -1)
888 break;
889
890 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000891 case OPT_TOC_ENTRY: {
892 image_desc_t *desc;
893
894 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000895 set_image_desc_action(desc, DO_UNPACK, optarg);
dp-arm90d2f0e2016-11-14 15:54:32 +0000896 unpack_all = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100897 break;
dp-arm90d2f0e2016-11-14 15:54:32 +0000898 }
dp-arm516dfcb2016-11-03 13:59:26 +0000899 case 'b': {
900 char name[_UUID_STR_LEN + 1];
901 char filename[PATH_MAX] = { 0 };
902 uuid_t uuid = { 0 };
903 image_desc_t *desc;
904
905 parse_blob_opt(optarg, &uuid,
906 filename, sizeof(filename));
907
908 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
909 filename[0] == '\0')
910 unpack_usage();
911
912 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000913 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000914 uuid_to_str(name, sizeof(name), &uuid);
915 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000916 add_image_desc(desc);
917 }
dp-armfb732312016-12-30 09:55:48 +0000918 set_image_desc_action(desc, DO_UNPACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000919 unpack_all = 0;
920 break;
921 }
dp-arm4972ec52016-05-25 16:20:20 +0100922 case 'f':
923 fflag = 1;
924 break;
925 case 'o':
926 snprintf(outdir, sizeof(outdir), "%s", optarg);
927 break;
928 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100929 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100930 }
931 }
932 argc -= optind;
933 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000934 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100935
936 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100937 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100938
939 parse_fip(argv[0], NULL);
940
941 if (outdir[0] != '\0')
942 if (chdir(outdir) == -1)
943 log_err("chdir %s", outdir);
944
dp-arm4972ec52016-05-25 16:20:20 +0100945 /* Unpack all specified images. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000946 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +0000947 char file[PATH_MAX];
dp-armafa1efa2017-02-14 15:22:13 +0000948 image_t *image = desc->image;
dp-arm715ef422016-08-30 14:18:58 +0100949
dp-arm90d2f0e2016-11-14 15:54:32 +0000950 if (!unpack_all && desc->action != DO_UNPACK)
dp-arm4972ec52016-05-25 16:20:20 +0100951 continue;
952
953 /* Build filename. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000954 if (desc->action_arg == NULL)
dp-arm4972ec52016-05-25 16:20:20 +0100955 snprintf(file, sizeof(file), "%s.bin",
dp-arm90d2f0e2016-11-14 15:54:32 +0000956 desc->cmdline_name);
dp-arm4972ec52016-05-25 16:20:20 +0100957 else
958 snprintf(file, sizeof(file), "%s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000959 desc->action_arg);
dp-arm4972ec52016-05-25 16:20:20 +0100960
dp-arm715ef422016-08-30 14:18:58 +0100961 if (image == NULL) {
962 if (!unpack_all)
dp-arm516dfcb2016-11-03 13:59:26 +0000963 log_warnx("%s does not exist in %s",
dp-arm715ef422016-08-30 14:18:58 +0100964 file, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +0100965 continue;
966 }
967
968 if (access(file, F_OK) != 0 || fflag) {
969 if (verbose)
970 log_dbgx("Unpacking %s", file);
dp-arm715ef422016-08-30 14:18:58 +0100971 write_image_to_file(image, file);
dp-arm4972ec52016-05-25 16:20:20 +0100972 } else {
973 log_warnx("File %s already exists, use --force to overwrite it",
974 file);
975 }
dp-arm4972ec52016-05-25 16:20:20 +0100976 }
977
dp-arm4972ec52016-05-25 16:20:20 +0100978 return 0;
979}
980
981static void unpack_usage(void)
982{
983 toc_entry_t *toc_entry = toc_entries;
984
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900985 printf("fiptool unpack [opts] FIP_FILENAME\n");
986 printf("\n");
987 printf("Options:\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900988 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID to file.\n");
989 printf(" --force\t\t\tIf the output file already exists, use --force to overwrite it.\n");
dp-arm516dfcb2016-11-03 13:59:26 +0000990 printf(" --out path\t\t\tSet the output directory path.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +0900991 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100992 printf("Specific images are unpacked with the following options:\n");
993 for (; toc_entry->cmdline_name != NULL; toc_entry++)
994 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
995 toc_entry->name);
Masahiro Yamada1b900152017-02-02 16:34:14 +0900996 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100997 printf("If no options are provided, all images will be unpacked.\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100998 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100999}
1000
1001static int remove_cmd(int argc, char *argv[])
1002{
dp-arm90d2f0e2016-11-14 15:54:32 +00001003 struct option *opts = NULL;
1004 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +00001005 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +01001006 fip_toc_header_t toc_header;
dp-arm90d2f0e2016-11-14 15:54:32 +00001007 image_desc_t *desc;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001008 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +01001009 int fflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001010
1011 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +01001012 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001013
dp-arm90d2f0e2016-11-14 15:54:32 +00001014 opts = fill_common_opts(opts, &nr_opts, no_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001015 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +00001016 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +00001017 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
1018 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
1019 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +01001020
1021 while (1) {
dp-armfe92b892016-11-07 11:13:54 +00001022 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001023
dp-arm516dfcb2016-11-03 13:59:26 +00001024 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +01001025 if (c == -1)
1026 break;
1027
1028 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001029 case OPT_TOC_ENTRY: {
1030 image_desc_t *desc;
1031
1032 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +00001033 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm4972ec52016-05-25 16:20:20 +01001034 break;
dp-arm90d2f0e2016-11-14 15:54:32 +00001035 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001036 case OPT_ALIGN:
1037 align = get_image_align(optarg);
1038 break;
dp-arm516dfcb2016-11-03 13:59:26 +00001039 case 'b': {
1040 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
1041 uuid_t uuid = { 0 };
1042 image_desc_t *desc;
1043
1044 parse_blob_opt(optarg, &uuid,
1045 filename, sizeof(filename));
1046
1047 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
1048 remove_usage();
1049
1050 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +00001051 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +00001052 uuid_to_str(name, sizeof(name), &uuid);
1053 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +00001054 add_image_desc(desc);
1055 }
dp-armfb732312016-12-30 09:55:48 +00001056 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm516dfcb2016-11-03 13:59:26 +00001057 break;
1058 }
dp-arm4972ec52016-05-25 16:20:20 +01001059 case 'f':
1060 fflag = 1;
1061 break;
1062 case 'o':
1063 snprintf(outfile, sizeof(outfile), "%s", optarg);
1064 break;
1065 default:
dp-arm29f1b5c2016-09-15 09:58:50 +01001066 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001067 }
1068 }
1069 argc -= optind;
1070 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001071 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001072
1073 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001074 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001075
1076 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
1077 log_errx("File %s already exists, use --force to overwrite it",
1078 outfile);
1079
1080 if (outfile[0] == '\0')
1081 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
1082
1083 parse_fip(argv[0], &toc_header);
1084
dp-arm90d2f0e2016-11-14 15:54:32 +00001085 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001086 if (desc->action != DO_REMOVE)
dp-arm4972ec52016-05-25 16:20:20 +01001087 continue;
dp-arm516dfcb2016-11-03 13:59:26 +00001088
dp-armafa1efa2017-02-14 15:22:13 +00001089 if (desc->image != NULL) {
dp-arm4972ec52016-05-25 16:20:20 +01001090 if (verbose)
dp-arm516dfcb2016-11-03 13:59:26 +00001091 log_dbgx("Removing %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001092 desc->cmdline_name);
dp-armafa1efa2017-02-14 15:22:13 +00001093 free(desc->image);
1094 desc->image = NULL;
dp-arm4972ec52016-05-25 16:20:20 +01001095 } else {
dp-arm516dfcb2016-11-03 13:59:26 +00001096 log_warnx("%s does not exist in %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001097 desc->cmdline_name, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001098 }
1099 }
1100
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001101 pack_images(outfile, toc_header.flags, align);
dp-arm4972ec52016-05-25 16:20:20 +01001102 return 0;
1103}
1104
1105static void remove_usage(void)
1106{
1107 toc_entry_t *toc_entry = toc_entries;
1108
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001109 printf("fiptool remove [opts] FIP_FILENAME\n");
1110 printf("\n");
1111 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001112 printf(" --align <value>\tEach image is aligned to <value> (default: 1).\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001113 printf(" --blob uuid=...\tRemove an image with the given UUID.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +09001114 printf(" --force\t\tIf the output FIP file already exists, use --force to overwrite it.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001115 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001116 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001117 printf("Specific images are removed with the following options:\n");
1118 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1119 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
1120 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +01001121 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001122}
1123
1124static int version_cmd(int argc, char *argv[])
1125{
1126#ifdef VERSION
1127 puts(VERSION);
1128#else
1129 /* If built from fiptool directory, VERSION is not set. */
1130 puts("Unknown version");
1131#endif
1132 return 0;
1133}
1134
1135static void version_usage(void)
1136{
1137 printf("fiptool version\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001138 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001139}
1140
1141static int help_cmd(int argc, char *argv[])
1142{
1143 int i;
1144
1145 if (argc < 2)
1146 usage();
1147 argc--, argv++;
1148
1149 for (i = 0; i < NELEM(cmds); i++) {
1150 if (strcmp(cmds[i].name, argv[0]) == 0 &&
dp-arm29f1b5c2016-09-15 09:58:50 +01001151 cmds[i].usage != NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001152 cmds[i].usage();
dp-arm4972ec52016-05-25 16:20:20 +01001153 }
1154 if (i == NELEM(cmds))
1155 printf("No help for subcommand '%s'\n", argv[0]);
1156 return 0;
1157}
1158
1159static void usage(void)
1160{
Masahiro Yamada252c3362017-01-13 02:13:06 +09001161 printf("usage: fiptool [--verbose] <command> [<args>]\n");
dp-arm4972ec52016-05-25 16:20:20 +01001162 printf("Global options supported:\n");
1163 printf(" --verbose\tEnable verbose output for all commands.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001164 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001165 printf("Commands supported:\n");
1166 printf(" info\t\tList images contained in FIP.\n");
1167 printf(" create\tCreate a new FIP with the given images.\n");
1168 printf(" update\tUpdate an existing FIP with the given images.\n");
1169 printf(" unpack\tUnpack images from FIP.\n");
1170 printf(" remove\tRemove images from FIP.\n");
1171 printf(" version\tShow fiptool version.\n");
1172 printf(" help\t\tShow help for given command.\n");
1173 exit(1);
1174}
1175
1176int main(int argc, char *argv[])
1177{
1178 int i, ret = 0;
1179
dp-arm5cd10ae2016-11-07 10:45:59 +00001180 while (1) {
1181 int c, opt_index = 0;
1182 static struct option opts[] = {
1183 { "verbose", no_argument, NULL, 'v' },
1184 { NULL, no_argument, NULL, 0 }
1185 };
1186
1187 /*
1188 * Set POSIX mode so getopt stops at the first non-option
1189 * which is the subcommand.
1190 */
1191 c = getopt_long(argc, argv, "+v", opts, &opt_index);
1192 if (c == -1)
1193 break;
dp-arm4972ec52016-05-25 16:20:20 +01001194
dp-arm5cd10ae2016-11-07 10:45:59 +00001195 switch (c) {
1196 case 'v':
1197 verbose = 1;
1198 break;
1199 default:
Masahiro Yamada48a24972016-10-26 13:24:26 +09001200 usage();
dp-arm5cd10ae2016-11-07 10:45:59 +00001201 }
dp-arm4972ec52016-05-25 16:20:20 +01001202 }
dp-arm5cd10ae2016-11-07 10:45:59 +00001203 argc -= optind;
1204 argv += optind;
1205 /* Reset optind for subsequent getopt processing. */
1206 optind = 0;
1207
1208 if (argc == 0)
1209 usage();
dp-arm4972ec52016-05-25 16:20:20 +01001210
dp-arm90d2f0e2016-11-14 15:54:32 +00001211 fill_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001212 for (i = 0; i < NELEM(cmds); i++) {
1213 if (strcmp(cmds[i].name, argv[0]) == 0) {
1214 ret = cmds[i].handler(argc, argv);
1215 break;
1216 }
1217 }
1218 if (i == NELEM(cmds))
1219 usage();
dp-arm90d2f0e2016-11-14 15:54:32 +00001220 free_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001221 return ret;
1222}