blob: 1dcb7e8e616ffad2d8f09646c6e212bf2b63cca0 [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;
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900495 uint64_t entry_offset, buf_size, payload_size = 0;
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
529 /* Append a null uuid entry to mark the end of ToC entries. */
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900530 memset(toc_entry, 0, sizeof(*toc_entry));
dp-arm4972ec52016-05-25 16:20:20 +0100531 toc_entry->offset_address = entry_offset;
dp-arm4972ec52016-05-25 16:20:20 +0100532
533 /* Generate the FIP file. */
Evan Lloydb1939392017-01-13 14:13:09 +0000534 fp = fopen(filename, "wb");
dp-arm4972ec52016-05-25 16:20:20 +0100535 if (fp == NULL)
536 log_err("fopen %s", filename);
537
538 if (verbose)
539 log_dbgx("Metadata size: %zu bytes", buf_size);
540
Masahiro Yamada23f9b9e2017-01-27 03:54:02 +0900541 xfwrite(buf, buf_size, fp, filename);
dp-arm4972ec52016-05-25 16:20:20 +0100542 free(buf);
543
544 if (verbose)
545 log_dbgx("Payload size: %zu bytes", payload_size);
546
dp-armafa1efa2017-02-14 15:22:13 +0000547 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
548 image_t *image = desc->image;
549
550 if (image == NULL)
551 continue;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900552 if (fseek(fp, image->toc_e.offset_address, SEEK_SET))
553 log_errx("Failed to set file position");
554
Masahiro Yamada2fe0dad2017-01-27 03:56:58 +0900555 xfwrite(image->buffer, image->toc_e.size, fp, filename);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900556 }
dp-arm4972ec52016-05-25 16:20:20 +0100557
558 fclose(fp);
559 return 0;
560}
561
562/*
563 * This function is shared between the create and update subcommands.
564 * The difference between the two subcommands is that when the FIP file
565 * is created, the parsing of an existing FIP is skipped. This results
566 * in update_fip() creating the new FIP file from scratch because the
567 * internal image table is not populated.
568 */
569static void update_fip(void)
570{
dp-arm90d2f0e2016-11-14 15:54:32 +0000571 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100572
573 /* Add or replace images in the FIP file. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000574 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-armafa1efa2017-02-14 15:22:13 +0000575 image_t *image;
dp-arm516dfcb2016-11-03 13:59:26 +0000576
dp-arm90d2f0e2016-11-14 15:54:32 +0000577 if (desc->action != DO_PACK)
dp-arm4972ec52016-05-25 16:20:20 +0100578 continue;
579
dp-armafa1efa2017-02-14 15:22:13 +0000580 image = read_image_from_file(&desc->uuid,
dp-arm90d2f0e2016-11-14 15:54:32 +0000581 desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000582 if (desc->image != NULL) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000583 if (verbose) {
dp-arm516dfcb2016-11-03 13:59:26 +0000584 log_dbgx("Replacing %s with %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000585 desc->cmdline_name,
586 desc->action_arg);
587 }
dp-armafa1efa2017-02-14 15:22:13 +0000588 free(desc->image);
589 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100590 } else {
591 if (verbose)
592 log_dbgx("Adding image %s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000593 desc->action_arg);
dp-armafa1efa2017-02-14 15:22:13 +0000594 desc->image = image;
dp-arm4972ec52016-05-25 16:20:20 +0100595 }
dp-arm4972ec52016-05-25 16:20:20 +0100596 }
597}
598
dp-arm90d2f0e2016-11-14 15:54:32 +0000599static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
dp-arm4972ec52016-05-25 16:20:20 +0100600{
601 unsigned long long flags;
602 char *endptr;
603
604 errno = 0;
605 flags = strtoull(arg, &endptr, 16);
606 if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
607 log_errx("Invalid platform ToC flags: %s", arg);
608 /* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
609 *toc_flags |= flags << 32;
610}
611
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900612static int is_power_of_2(unsigned long x)
613{
614 return x && !(x & (x - 1));
615}
616
617static unsigned long get_image_align(char *arg)
618{
619 char *endptr;
620 unsigned long align;
621
622 errno = 0;
Andreas Färber242a7b72017-04-21 19:39:10 +0200623 align = strtoul(arg, &endptr, 0);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900624 if (*endptr != '\0' || !is_power_of_2(align) || errno != 0)
625 log_errx("Invalid alignment: %s", arg);
626
627 return align;
628}
629
dp-arm516dfcb2016-11-03 13:59:26 +0000630static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
631{
632 char *p;
633
634 for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
635 if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
636 p += strlen("uuid=");
637 uuid_from_str(uuid, p);
638 } else if (strncmp(p, "file=", strlen("file=")) == 0) {
639 p += strlen("file=");
640 snprintf(filename, len, "%s", p);
641 }
642 }
643}
644
dp-arm4972ec52016-05-25 16:20:20 +0100645static int create_cmd(int argc, char *argv[])
646{
dp-arm90d2f0e2016-11-14 15:54:32 +0000647 struct option *opts = NULL;
648 size_t nr_opts = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100649 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900650 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100651
652 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100653 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100654
dp-arm90d2f0e2016-11-14 15:54:32 +0000655 opts = fill_common_opts(opts, &nr_opts, required_argument);
656 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100657 OPT_PLAT_TOC_FLAGS);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900658 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000659 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000660 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100661
662 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000663 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100664
dp-arm516dfcb2016-11-03 13:59:26 +0000665 c = getopt_long(argc, argv, "b:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100666 if (c == -1)
667 break;
668
669 switch (c) {
670 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000671 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100672
dp-arm90d2f0e2016-11-14 15:54:32 +0000673 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000674 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100675 break;
676 }
677 case OPT_PLAT_TOC_FLAGS:
678 parse_plat_toc_flags(optarg, &toc_flags);
679 break;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900680 case OPT_ALIGN:
681 align = get_image_align(optarg);
682 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000683 case 'b': {
684 char name[_UUID_STR_LEN + 1];
685 char filename[PATH_MAX] = { 0 };
686 uuid_t uuid = { 0 };
687 image_desc_t *desc;
688
689 parse_blob_opt(optarg, &uuid,
690 filename, sizeof(filename));
691
692 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
693 filename[0] == '\0')
694 create_usage();
695
696 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000697 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000698 uuid_to_str(name, sizeof(name), &uuid);
699 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000700 add_image_desc(desc);
701 }
dp-armfb732312016-12-30 09:55:48 +0000702 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000703 break;
704 }
dp-arm4972ec52016-05-25 16:20:20 +0100705 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100706 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100707 }
708 }
709 argc -= optind;
710 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000711 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100712
713 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100714 create_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100715
716 update_fip();
717
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900718 pack_images(argv[0], toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100719 return 0;
720}
721
722static void create_usage(void)
723{
724 toc_entry_t *toc_entry = toc_entries;
725
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900726 printf("fiptool create [opts] FIP_FILENAME\n");
727 printf("\n");
728 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900729 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900730 printf(" --blob uuid=...,file=...\tAdd an image with the given UUID pointed to by file.\n");
731 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 +0900732 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100733 printf("Specific images are packed with the following options:\n");
734 for (; toc_entry->cmdline_name != NULL; toc_entry++)
735 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
736 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100737 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100738}
739
740static int update_cmd(int argc, char *argv[])
741{
dp-arm90d2f0e2016-11-14 15:54:32 +0000742 struct option *opts = NULL;
743 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000744 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100745 fip_toc_header_t toc_header = { 0 };
746 unsigned long long toc_flags = 0;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900747 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100748 int pflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100749
750 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100751 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100752
dp-arm90d2f0e2016-11-14 15:54:32 +0000753 opts = fill_common_opts(opts, &nr_opts, required_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900754 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +0000755 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000756 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
757 opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
dp-arm4972ec52016-05-25 16:20:20 +0100758 OPT_PLAT_TOC_FLAGS);
dp-arm90d2f0e2016-11-14 15:54:32 +0000759 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100760
761 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000762 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100763
dp-arm516dfcb2016-11-03 13:59:26 +0000764 c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100765 if (c == -1)
766 break;
767
768 switch (c) {
769 case OPT_TOC_ENTRY: {
dp-arm90d2f0e2016-11-14 15:54:32 +0000770 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100771
dp-arm90d2f0e2016-11-14 15:54:32 +0000772 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000773 set_image_desc_action(desc, DO_PACK, optarg);
dp-arm4972ec52016-05-25 16:20:20 +0100774 break;
775 }
dp-arm90d2f0e2016-11-14 15:54:32 +0000776 case OPT_PLAT_TOC_FLAGS:
dp-arm4972ec52016-05-25 16:20:20 +0100777 parse_plat_toc_flags(optarg, &toc_flags);
778 pflag = 1;
779 break;
dp-arm516dfcb2016-11-03 13:59:26 +0000780 case 'b': {
781 char name[_UUID_STR_LEN + 1];
782 char filename[PATH_MAX] = { 0 };
783 uuid_t uuid = { 0 };
784 image_desc_t *desc;
785
786 parse_blob_opt(optarg, &uuid,
787 filename, sizeof(filename));
788
789 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
790 filename[0] == '\0')
791 update_usage();
792
793 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000794 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000795 uuid_to_str(name, sizeof(name), &uuid);
796 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000797 add_image_desc(desc);
798 }
dp-armfb732312016-12-30 09:55:48 +0000799 set_image_desc_action(desc, DO_PACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000800 break;
801 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900802 case OPT_ALIGN:
803 align = get_image_align(optarg);
804 break;
dp-arm4972ec52016-05-25 16:20:20 +0100805 case 'o':
806 snprintf(outfile, sizeof(outfile), "%s", optarg);
807 break;
808 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100809 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100810 }
811 }
812 argc -= optind;
813 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000814 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100815
816 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100817 update_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100818
819 if (outfile[0] == '\0')
820 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
821
Masahiro Yamadaebb6e372016-12-25 12:41:41 +0900822 if (access(argv[0], F_OK) == 0)
dp-arm4972ec52016-05-25 16:20:20 +0100823 parse_fip(argv[0], &toc_header);
824
825 if (pflag)
826 toc_header.flags &= ~(0xffffULL << 32);
827 toc_flags = (toc_header.flags |= toc_flags);
828
829 update_fip();
830
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900831 pack_images(outfile, toc_flags, align);
dp-arm4972ec52016-05-25 16:20:20 +0100832 return 0;
833}
834
835static void update_usage(void)
836{
837 toc_entry_t *toc_entry = toc_entries;
838
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900839 printf("fiptool update [opts] FIP_FILENAME\n");
840 printf("\n");
841 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900842 printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900843 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 +0100844 printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900845 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 +0900846 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100847 printf("Specific images are packed with the following options:\n");
848 for (; toc_entry->cmdline_name != NULL; toc_entry++)
849 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
850 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +0100851 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100852}
853
854static int unpack_cmd(int argc, char *argv[])
855{
dp-arm90d2f0e2016-11-14 15:54:32 +0000856 struct option *opts = NULL;
857 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000858 char outdir[PATH_MAX] = { 0 };
dp-arm90d2f0e2016-11-14 15:54:32 +0000859 image_desc_t *desc;
dp-arm4972ec52016-05-25 16:20:20 +0100860 int fflag = 0;
861 int unpack_all = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100862
863 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +0100864 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100865
dp-arm90d2f0e2016-11-14 15:54:32 +0000866 opts = fill_common_opts(opts, &nr_opts, required_argument);
dp-arm516dfcb2016-11-03 13:59:26 +0000867 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +0000868 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
869 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
870 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +0100871
872 while (1) {
dp-armfe92b892016-11-07 11:13:54 +0000873 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100874
dp-arm516dfcb2016-11-03 13:59:26 +0000875 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +0100876 if (c == -1)
877 break;
878
879 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +0000880 case OPT_TOC_ENTRY: {
881 image_desc_t *desc;
882
883 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +0000884 set_image_desc_action(desc, DO_UNPACK, optarg);
dp-arm90d2f0e2016-11-14 15:54:32 +0000885 unpack_all = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100886 break;
dp-arm90d2f0e2016-11-14 15:54:32 +0000887 }
dp-arm516dfcb2016-11-03 13:59:26 +0000888 case 'b': {
889 char name[_UUID_STR_LEN + 1];
890 char filename[PATH_MAX] = { 0 };
891 uuid_t uuid = { 0 };
892 image_desc_t *desc;
893
894 parse_blob_opt(optarg, &uuid,
895 filename, sizeof(filename));
896
897 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
898 filename[0] == '\0')
899 unpack_usage();
900
901 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +0000902 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +0000903 uuid_to_str(name, sizeof(name), &uuid);
904 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +0000905 add_image_desc(desc);
906 }
dp-armfb732312016-12-30 09:55:48 +0000907 set_image_desc_action(desc, DO_UNPACK, filename);
dp-arm516dfcb2016-11-03 13:59:26 +0000908 unpack_all = 0;
909 break;
910 }
dp-arm4972ec52016-05-25 16:20:20 +0100911 case 'f':
912 fflag = 1;
913 break;
914 case 'o':
915 snprintf(outdir, sizeof(outdir), "%s", optarg);
916 break;
917 default:
dp-arm29f1b5c2016-09-15 09:58:50 +0100918 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100919 }
920 }
921 argc -= optind;
922 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +0000923 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +0100924
925 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +0100926 unpack_usage();
dp-arm4972ec52016-05-25 16:20:20 +0100927
928 parse_fip(argv[0], NULL);
929
930 if (outdir[0] != '\0')
931 if (chdir(outdir) == -1)
932 log_err("chdir %s", outdir);
933
dp-arm4972ec52016-05-25 16:20:20 +0100934 /* Unpack all specified images. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000935 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm516dfcb2016-11-03 13:59:26 +0000936 char file[PATH_MAX];
dp-armafa1efa2017-02-14 15:22:13 +0000937 image_t *image = desc->image;
dp-arm715ef422016-08-30 14:18:58 +0100938
dp-arm90d2f0e2016-11-14 15:54:32 +0000939 if (!unpack_all && desc->action != DO_UNPACK)
dp-arm4972ec52016-05-25 16:20:20 +0100940 continue;
941
942 /* Build filename. */
dp-arm90d2f0e2016-11-14 15:54:32 +0000943 if (desc->action_arg == NULL)
dp-arm4972ec52016-05-25 16:20:20 +0100944 snprintf(file, sizeof(file), "%s.bin",
dp-arm90d2f0e2016-11-14 15:54:32 +0000945 desc->cmdline_name);
dp-arm4972ec52016-05-25 16:20:20 +0100946 else
947 snprintf(file, sizeof(file), "%s",
dp-arm90d2f0e2016-11-14 15:54:32 +0000948 desc->action_arg);
dp-arm4972ec52016-05-25 16:20:20 +0100949
dp-arm715ef422016-08-30 14:18:58 +0100950 if (image == NULL) {
951 if (!unpack_all)
dp-arm516dfcb2016-11-03 13:59:26 +0000952 log_warnx("%s does not exist in %s",
dp-arm715ef422016-08-30 14:18:58 +0100953 file, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +0100954 continue;
955 }
956
957 if (access(file, F_OK) != 0 || fflag) {
958 if (verbose)
959 log_dbgx("Unpacking %s", file);
dp-arm715ef422016-08-30 14:18:58 +0100960 write_image_to_file(image, file);
dp-arm4972ec52016-05-25 16:20:20 +0100961 } else {
962 log_warnx("File %s already exists, use --force to overwrite it",
963 file);
964 }
dp-arm4972ec52016-05-25 16:20:20 +0100965 }
966
dp-arm4972ec52016-05-25 16:20:20 +0100967 return 0;
968}
969
970static void unpack_usage(void)
971{
972 toc_entry_t *toc_entry = toc_entries;
973
Masahiro Yamada7abd0a22017-01-14 11:04:36 +0900974 printf("fiptool unpack [opts] FIP_FILENAME\n");
975 printf("\n");
976 printf("Options:\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +0900977 printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID to file.\n");
978 printf(" --force\t\t\tIf the output file already exists, use --force to overwrite it.\n");
dp-arm516dfcb2016-11-03 13:59:26 +0000979 printf(" --out path\t\t\tSet the output directory path.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +0900980 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100981 printf("Specific images are unpacked with the following options:\n");
982 for (; toc_entry->cmdline_name != NULL; toc_entry++)
983 printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
984 toc_entry->name);
Masahiro Yamada1b900152017-02-02 16:34:14 +0900985 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +0100986 printf("If no options are provided, all images will be unpacked.\n");
dp-arm29f1b5c2016-09-15 09:58:50 +0100987 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +0100988}
989
990static int remove_cmd(int argc, char *argv[])
991{
dp-arm90d2f0e2016-11-14 15:54:32 +0000992 struct option *opts = NULL;
993 size_t nr_opts = 0;
dp-arm516dfcb2016-11-03 13:59:26 +0000994 char outfile[PATH_MAX] = { 0 };
dp-arm4972ec52016-05-25 16:20:20 +0100995 fip_toc_header_t toc_header;
dp-arm90d2f0e2016-11-14 15:54:32 +0000996 image_desc_t *desc;
Masahiro Yamada4d87eb42016-12-25 13:52:22 +0900997 unsigned long align = 1;
dp-arm4972ec52016-05-25 16:20:20 +0100998 int fflag = 0;
dp-arm4972ec52016-05-25 16:20:20 +0100999
1000 if (argc < 2)
dp-arm29f1b5c2016-09-15 09:58:50 +01001001 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001002
dp-arm90d2f0e2016-11-14 15:54:32 +00001003 opts = fill_common_opts(opts, &nr_opts, no_argument);
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001004 opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
dp-arm516dfcb2016-11-03 13:59:26 +00001005 opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
dp-arm90d2f0e2016-11-14 15:54:32 +00001006 opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
1007 opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
1008 opts = add_opt(opts, &nr_opts, NULL, 0, 0);
dp-arm4972ec52016-05-25 16:20:20 +01001009
1010 while (1) {
dp-armfe92b892016-11-07 11:13:54 +00001011 int c, opt_index = 0;
dp-arm4972ec52016-05-25 16:20:20 +01001012
dp-arm516dfcb2016-11-03 13:59:26 +00001013 c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
dp-arm4972ec52016-05-25 16:20:20 +01001014 if (c == -1)
1015 break;
1016
1017 switch (c) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001018 case OPT_TOC_ENTRY: {
1019 image_desc_t *desc;
1020
1021 desc = lookup_image_desc_from_opt(opts[opt_index].name);
dp-armfb732312016-12-30 09:55:48 +00001022 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm4972ec52016-05-25 16:20:20 +01001023 break;
dp-arm90d2f0e2016-11-14 15:54:32 +00001024 }
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001025 case OPT_ALIGN:
1026 align = get_image_align(optarg);
1027 break;
dp-arm516dfcb2016-11-03 13:59:26 +00001028 case 'b': {
1029 char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
1030 uuid_t uuid = { 0 };
1031 image_desc_t *desc;
1032
1033 parse_blob_opt(optarg, &uuid,
1034 filename, sizeof(filename));
1035
1036 if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
1037 remove_usage();
1038
1039 desc = lookup_image_desc_from_uuid(&uuid);
dp-armfb732312016-12-30 09:55:48 +00001040 if (desc == NULL) {
dp-arm516dfcb2016-11-03 13:59:26 +00001041 uuid_to_str(name, sizeof(name), &uuid);
1042 desc = new_image_desc(&uuid, name, "blob");
dp-arm516dfcb2016-11-03 13:59:26 +00001043 add_image_desc(desc);
1044 }
dp-armfb732312016-12-30 09:55:48 +00001045 set_image_desc_action(desc, DO_REMOVE, NULL);
dp-arm516dfcb2016-11-03 13:59:26 +00001046 break;
1047 }
dp-arm4972ec52016-05-25 16:20:20 +01001048 case 'f':
1049 fflag = 1;
1050 break;
1051 case 'o':
1052 snprintf(outfile, sizeof(outfile), "%s", optarg);
1053 break;
1054 default:
dp-arm29f1b5c2016-09-15 09:58:50 +01001055 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001056 }
1057 }
1058 argc -= optind;
1059 argv += optind;
dp-arm90d2f0e2016-11-14 15:54:32 +00001060 free(opts);
dp-arm4972ec52016-05-25 16:20:20 +01001061
1062 if (argc == 0)
dp-arm29f1b5c2016-09-15 09:58:50 +01001063 remove_usage();
dp-arm4972ec52016-05-25 16:20:20 +01001064
1065 if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
1066 log_errx("File %s already exists, use --force to overwrite it",
1067 outfile);
1068
1069 if (outfile[0] == '\0')
1070 snprintf(outfile, sizeof(outfile), "%s", argv[0]);
1071
1072 parse_fip(argv[0], &toc_header);
1073
dp-arm90d2f0e2016-11-14 15:54:32 +00001074 for (desc = image_desc_head; desc != NULL; desc = desc->next) {
dp-arm90d2f0e2016-11-14 15:54:32 +00001075 if (desc->action != DO_REMOVE)
dp-arm4972ec52016-05-25 16:20:20 +01001076 continue;
dp-arm516dfcb2016-11-03 13:59:26 +00001077
dp-armafa1efa2017-02-14 15:22:13 +00001078 if (desc->image != NULL) {
dp-arm4972ec52016-05-25 16:20:20 +01001079 if (verbose)
dp-arm516dfcb2016-11-03 13:59:26 +00001080 log_dbgx("Removing %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001081 desc->cmdline_name);
dp-armafa1efa2017-02-14 15:22:13 +00001082 free(desc->image);
1083 desc->image = NULL;
dp-arm4972ec52016-05-25 16:20:20 +01001084 } else {
dp-arm516dfcb2016-11-03 13:59:26 +00001085 log_warnx("%s does not exist in %s",
dp-arm90d2f0e2016-11-14 15:54:32 +00001086 desc->cmdline_name, argv[0]);
dp-arm4972ec52016-05-25 16:20:20 +01001087 }
1088 }
1089
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001090 pack_images(outfile, toc_header.flags, align);
dp-arm4972ec52016-05-25 16:20:20 +01001091 return 0;
1092}
1093
1094static void remove_usage(void)
1095{
1096 toc_entry_t *toc_entry = toc_entries;
1097
Masahiro Yamada7abd0a22017-01-14 11:04:36 +09001098 printf("fiptool remove [opts] FIP_FILENAME\n");
1099 printf("\n");
1100 printf("Options:\n");
Masahiro Yamada4d87eb42016-12-25 13:52:22 +09001101 printf(" --align <value>\tEach image is aligned to <value> (default: 1).\n");
dp-arm516dfcb2016-11-03 13:59:26 +00001102 printf(" --blob uuid=...\tRemove an image with the given UUID.\n");
Masahiro Yamada1eee6a82017-02-02 16:37:37 +09001103 printf(" --force\t\tIf the output FIP file already exists, use --force to overwrite it.\n");
dp-arm4972ec52016-05-25 16:20:20 +01001104 printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001105 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001106 printf("Specific images are removed with the following options:\n");
1107 for (; toc_entry->cmdline_name != NULL; toc_entry++)
1108 printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
1109 toc_entry->name);
dp-arm29f1b5c2016-09-15 09:58:50 +01001110 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001111}
1112
1113static int version_cmd(int argc, char *argv[])
1114{
1115#ifdef VERSION
1116 puts(VERSION);
1117#else
1118 /* If built from fiptool directory, VERSION is not set. */
1119 puts("Unknown version");
1120#endif
1121 return 0;
1122}
1123
1124static void version_usage(void)
1125{
1126 printf("fiptool version\n");
dp-arm29f1b5c2016-09-15 09:58:50 +01001127 exit(1);
dp-arm4972ec52016-05-25 16:20:20 +01001128}
1129
1130static int help_cmd(int argc, char *argv[])
1131{
1132 int i;
1133
1134 if (argc < 2)
1135 usage();
1136 argc--, argv++;
1137
1138 for (i = 0; i < NELEM(cmds); i++) {
1139 if (strcmp(cmds[i].name, argv[0]) == 0 &&
dp-arm29f1b5c2016-09-15 09:58:50 +01001140 cmds[i].usage != NULL)
dp-arm4972ec52016-05-25 16:20:20 +01001141 cmds[i].usage();
dp-arm4972ec52016-05-25 16:20:20 +01001142 }
1143 if (i == NELEM(cmds))
1144 printf("No help for subcommand '%s'\n", argv[0]);
1145 return 0;
1146}
1147
1148static void usage(void)
1149{
Masahiro Yamada252c3362017-01-13 02:13:06 +09001150 printf("usage: fiptool [--verbose] <command> [<args>]\n");
dp-arm4972ec52016-05-25 16:20:20 +01001151 printf("Global options supported:\n");
1152 printf(" --verbose\tEnable verbose output for all commands.\n");
Masahiro Yamada1b900152017-02-02 16:34:14 +09001153 printf("\n");
dp-arm4972ec52016-05-25 16:20:20 +01001154 printf("Commands supported:\n");
1155 printf(" info\t\tList images contained in FIP.\n");
1156 printf(" create\tCreate a new FIP with the given images.\n");
1157 printf(" update\tUpdate an existing FIP with the given images.\n");
1158 printf(" unpack\tUnpack images from FIP.\n");
1159 printf(" remove\tRemove images from FIP.\n");
1160 printf(" version\tShow fiptool version.\n");
1161 printf(" help\t\tShow help for given command.\n");
1162 exit(1);
1163}
1164
1165int main(int argc, char *argv[])
1166{
1167 int i, ret = 0;
1168
dp-arm5cd10ae2016-11-07 10:45:59 +00001169 while (1) {
1170 int c, opt_index = 0;
1171 static struct option opts[] = {
1172 { "verbose", no_argument, NULL, 'v' },
1173 { NULL, no_argument, NULL, 0 }
1174 };
1175
1176 /*
1177 * Set POSIX mode so getopt stops at the first non-option
1178 * which is the subcommand.
1179 */
1180 c = getopt_long(argc, argv, "+v", opts, &opt_index);
1181 if (c == -1)
1182 break;
dp-arm4972ec52016-05-25 16:20:20 +01001183
dp-arm5cd10ae2016-11-07 10:45:59 +00001184 switch (c) {
1185 case 'v':
1186 verbose = 1;
1187 break;
1188 default:
Masahiro Yamada48a24972016-10-26 13:24:26 +09001189 usage();
dp-arm5cd10ae2016-11-07 10:45:59 +00001190 }
dp-arm4972ec52016-05-25 16:20:20 +01001191 }
dp-arm5cd10ae2016-11-07 10:45:59 +00001192 argc -= optind;
1193 argv += optind;
1194 /* Reset optind for subsequent getopt processing. */
1195 optind = 0;
1196
1197 if (argc == 0)
1198 usage();
dp-arm4972ec52016-05-25 16:20:20 +01001199
dp-arm90d2f0e2016-11-14 15:54:32 +00001200 fill_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001201 for (i = 0; i < NELEM(cmds); i++) {
1202 if (strcmp(cmds[i].name, argv[0]) == 0) {
1203 ret = cmds[i].handler(argc, argv);
1204 break;
1205 }
1206 }
1207 if (i == NELEM(cmds))
1208 usage();
dp-arm90d2f0e2016-11-14 15:54:32 +00001209 free_image_descs();
dp-arm4972ec52016-05-25 16:20:20 +01001210 return ret;
1211}