Masahiro Yamada 1c75d5dfb0 fiptool: support --align option to add desired alignment to image offset
The current fiptool packs all the images without any padding between
them.  So, the offset to each image has no alignment.  This is not
efficient, for example, when the FIP is read from a block-oriented
device.

For example, (e)MMC is accessed by block-addressing.  The block size
is 512 byte.  So, the best case is each image is aligned by 512 byte
since the DMA engine can transfer the whole of the image to its load
address directly.  The worst case is the offset does not have even
DMA-capable alignment (this is where we stand now).  In this case,
we need to transfer every block to a bounce buffer, then do memcpy()
from the bounce buffer to our final destination.  At least, this
should work with the abstraction by the block I/O layer, but the
CPU-intervention for the whole data transfer makes it really slow.

This commit adds a new option --align to the fiptool.  This option,
if given, requests the tool to align each component in the FIP file
by the specified byte.  Also, add a new Make option FIP_ALIGN for
easier access to this feature; users can give something like
FIP_ALIGN=512 from the command line, or add "FIP_ALIGN := 512" to
their platform.mk file.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
2017-01-28 14:47:44 +09:00

1320 lines
32 KiB
C

/*
* Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/sha.h>
#include "fiptool.h"
#include "firmware_image_package.h"
#include "tbbr_config.h"
#define OPT_TOC_ENTRY 0
#define OPT_PLAT_TOC_FLAGS 1
#define OPT_ALIGN 2
static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid);
static image_t *lookup_image_from_uuid(const uuid_t *uuid);
static int info_cmd(int argc, char *argv[]);
static void info_usage(void);
static int create_cmd(int argc, char *argv[]);
static void create_usage(void);
static int update_cmd(int argc, char *argv[]);
static void update_usage(void);
static int unpack_cmd(int argc, char *argv[]);
static void unpack_usage(void);
static int remove_cmd(int argc, char *argv[]);
static void remove_usage(void);
static int version_cmd(int argc, char *argv[]);
static void version_usage(void);
static int help_cmd(int argc, char *argv[]);
static void usage(void);
/* Available subcommands. */
static cmd_t cmds[] = {
{ .name = "info", .handler = info_cmd, .usage = info_usage },
{ .name = "create", .handler = create_cmd, .usage = create_usage },
{ .name = "update", .handler = update_cmd, .usage = update_usage },
{ .name = "unpack", .handler = unpack_cmd, .usage = unpack_usage },
{ .name = "remove", .handler = remove_cmd, .usage = remove_usage },
{ .name = "version", .handler = version_cmd, .usage = version_usage },
{ .name = "help", .handler = help_cmd, .usage = NULL },
};
static image_desc_t *image_desc_head;
static size_t nr_image_descs;
static image_t *image_head;
static size_t nr_images;
static uuid_t uuid_null = { 0 };
static int verbose;
static void vlog(int prio, const char *msg, va_list ap)
{
char *prefix[] = { "DEBUG", "WARN", "ERROR" };
fprintf(stderr, "%s: ", prefix[prio]);
vfprintf(stderr, msg, ap);
fputc('\n', stderr);
}
static void log_dbgx(const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
vlog(LOG_DBG, msg, ap);
va_end(ap);
}
static void log_warnx(const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
vlog(LOG_WARN, msg, ap);
va_end(ap);
}
static void log_err(const char *msg, ...)
{
char buf[512];
va_list ap;
va_start(ap, msg);
snprintf(buf, sizeof(buf), "%s: %s", msg, strerror(errno));
vlog(LOG_ERR, buf, ap);
va_end(ap);
exit(1);
}
static void log_errx(const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
vlog(LOG_ERR, msg, ap);
va_end(ap);
exit(1);
}
static char *xstrdup(const char *s, const char *msg)
{
char *d;
d = strdup(s);
if (d == NULL)
log_errx("strdup: %s", msg);
return d;
}
static void *xmalloc(size_t size, const char *msg)
{
void *d;
d = malloc(size);
if (d == NULL)
log_errx("malloc: %s", msg);
return d;
}
static void *xzalloc(size_t size, const char *msg)
{
return memset(xmalloc(size, msg), 0, size);
}
static void xfwrite(void *buf, size_t size, FILE *fp, const char *filename)
{
if (fwrite(buf, 1, size, fp) != size)
log_errx("Failed to write %s", filename);
}
static image_desc_t *new_image_desc(const uuid_t *uuid,
const char *name, const char *cmdline_name)
{
image_desc_t *desc;
desc = xzalloc(sizeof(*desc),
"failed to allocate memory for image descriptor");
memcpy(&desc->uuid, uuid, sizeof(uuid_t));
desc->name = xstrdup(name,
"failed to allocate memory for image name");
desc->cmdline_name = xstrdup(cmdline_name,
"failed to allocate memory for image command line name");
desc->action = DO_UNSPEC;
return desc;
}
static void set_image_desc_action(image_desc_t *desc, int action,
const char *arg)
{
assert(desc != NULL);
if (desc->action_arg != DO_UNSPEC)
free(desc->action_arg);
desc->action = action;
desc->action_arg = NULL;
if (arg != NULL)
desc->action_arg = xstrdup(arg,
"failed to allocate memory for argument");
}
static void free_image_desc(image_desc_t *desc)
{
free(desc->name);
free(desc->cmdline_name);
free(desc->action_arg);
free(desc);
}
static void add_image_desc(image_desc_t *desc)
{
image_desc_t **p = &image_desc_head;
while (*p)
p = &(*p)->next;
assert(*p == NULL);
*p = desc;
nr_image_descs++;
}
static void free_image_descs(void)
{
image_desc_t *desc = image_desc_head, *tmp;
while (desc != NULL) {
tmp = desc->next;
free_image_desc(desc);
desc = tmp;
nr_image_descs--;
}
assert(nr_image_descs == 0);
}
static void fill_image_descs(void)
{
toc_entry_t *toc_entry;
for (toc_entry = toc_entries;
toc_entry->cmdline_name != NULL;
toc_entry++) {
image_desc_t *desc;
desc = new_image_desc(&toc_entry->uuid,
toc_entry->name,
toc_entry->cmdline_name);
add_image_desc(desc);
}
}
static void add_image(image_t *image)
{
image_t **p = &image_head;
while (*p)
p = &(*p)->next;
assert(*p == NULL);
*p = image;
nr_images++;
}
static void replace_image(image_t *image)
{
image_t **p = &image_head;
while (*p) {
if (!memcmp(&(*p)->toc_e.uuid, &image->toc_e.uuid,
sizeof(image->toc_e.uuid)))
break;
p = &(*p)->next;
}
assert(*p != NULL);
image->next = (*p)->next;
*p = image;
}
static void free_image(image_t *image)
{
free(image->buffer);
free(image);
}
static void remove_image(image_t *image)
{
image_t *tmp, **p = &image_head;
while (*p) {
if (*p == image)
break;
p = &(*p)->next;
}
assert(*p != NULL);
tmp = *p;
*p = tmp->next;
free_image(tmp);
nr_images--;
}
static void free_images(void)
{
image_t *image = image_head, *tmp;
while (image != NULL) {
tmp = image->next;
free_image(image);
image = tmp;
nr_images--;
}
assert(nr_images == 0);
}
static image_desc_t *lookup_image_desc_from_uuid(const uuid_t *uuid)
{
image_desc_t *desc;
for (desc = image_desc_head; desc != NULL; desc = desc->next)
if (memcmp(&desc->uuid, uuid, sizeof(uuid_t)) == 0)
return desc;
return NULL;
}
static image_desc_t *lookup_image_desc_from_opt(const char *opt)
{
image_desc_t *desc;
for (desc = image_desc_head; desc != NULL; desc = desc->next)
if (strcmp(desc->cmdline_name, opt) == 0)
return desc;
return NULL;
}
static image_t *lookup_image_from_uuid(const uuid_t *uuid)
{
image_t *image;
for (image = image_head; image != NULL; image = image->next)
if (!memcmp(&image->toc_e.uuid, uuid, sizeof(*uuid)))
return image;
return NULL;
}
static void uuid_to_str(char *s, size_t len, const uuid_t *u)
{
assert(len >= (_UUID_STR_LEN + 1));
snprintf(s, len, "%08X-%04X-%04X-%04X-%04X%04X%04X",
u->time_low,
u->time_mid,
u->time_hi_and_version,
((uint16_t)u->clock_seq_hi_and_reserved << 8) | u->clock_seq_low,
((uint16_t)u->node[0] << 8) | u->node[1],
((uint16_t)u->node[2] << 8) | u->node[3],
((uint16_t)u->node[4] << 8) | u->node[5]);
}
static void uuid_from_str(uuid_t *u, const char *s)
{
int n;
if (s == NULL)
log_errx("UUID cannot be NULL");
if (strlen(s) != _UUID_STR_LEN)
log_errx("Invalid UUID: %s", s);
n = sscanf(s,
"%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
&u->time_low, &u->time_mid, &u->time_hi_and_version,
&u->clock_seq_hi_and_reserved, &u->clock_seq_low, &u->node[0],
&u->node[1], &u->node[2], &u->node[3], &u->node[4], &u->node[5]);
/*
* Given the format specifier above, we expect 11 items to be scanned
* for a properly formatted UUID.
*/
if (n != 11)
log_errx("Invalid UUID: %s", s);
}
static int parse_fip(const char *filename, fip_toc_header_t *toc_header_out)
{
struct stat st;
FILE *fp;
char *buf, *bufend;
fip_toc_header_t *toc_header;
fip_toc_entry_t *toc_entry;
int terminated = 0;
fp = fopen(filename, "r");
if (fp == NULL)
log_err("fopen %s", filename);
if (fstat(fileno(fp), &st) == -1)
log_err("fstat %s", filename);
buf = xmalloc(st.st_size, "failed to load file into memory");
if (fread(buf, 1, st.st_size, fp) != st.st_size)
log_errx("Failed to read %s", filename);
bufend = buf + st.st_size;
fclose(fp);
if (st.st_size < sizeof(fip_toc_header_t))
log_errx("FIP %s is truncated", filename);
toc_header = (fip_toc_header_t *)buf;
toc_entry = (fip_toc_entry_t *)(toc_header + 1);
if (toc_header->name != TOC_HEADER_NAME)
log_errx("%s is not a FIP file", filename);
/* Return the ToC header if the caller wants it. */
if (toc_header_out != NULL)
*toc_header_out = *toc_header;
/* Walk through each ToC entry in the file. */
while ((char *)toc_entry + sizeof(*toc_entry) - 1 < bufend) {
image_t *image;
image_desc_t *desc;
/* Found the ToC terminator, we are done. */
if (memcmp(&toc_entry->uuid, &uuid_null, sizeof(uuid_t)) == 0) {
terminated = 1;
break;
}
/*
* Build a new image out of the ToC entry and add it to the
* table of images.
*/
image = xzalloc(sizeof(*image),
"failed to allocate memory for image");
image->toc_e = *toc_entry;
image->buffer = xmalloc(toc_entry->size,
"failed to allocate image buffer, is FIP file corrupted?");
/* Overflow checks before memory copy. */
if (toc_entry->size > (uint64_t)-1 - toc_entry->offset_address)
log_errx("FIP %s is corrupted", filename);
if (toc_entry->size + toc_entry->offset_address > st.st_size)
log_errx("FIP %s is corrupted", filename);
memcpy(image->buffer, buf + toc_entry->offset_address,
toc_entry->size);
/* If this is an unknown image, create a descriptor for it. */
desc = lookup_image_desc_from_uuid(&toc_entry->uuid);
if (desc == NULL) {
char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
uuid_to_str(name, sizeof(name), &toc_entry->uuid);
snprintf(filename, sizeof(filename), "%s%s",
name, ".bin");
desc = new_image_desc(&toc_entry->uuid, name, "blob");
desc->action = DO_UNPACK;
desc->action_arg = xstrdup(filename,
"failed to allocate memory for blob filename");
add_image_desc(desc);
}
add_image(image);
toc_entry++;
}
if (terminated == 0)
log_errx("FIP %s does not have a ToC terminator entry",
filename);
free(buf);
return 0;
}
static image_t *read_image_from_file(const uuid_t *uuid, const char *filename)
{
struct stat st;
image_t *image;
FILE *fp;
assert(uuid != NULL);
fp = fopen(filename, "r");
if (fp == NULL)
log_err("fopen %s", filename);
if (fstat(fileno(fp), &st) == -1)
log_errx("fstat %s", filename);
image = xzalloc(sizeof(*image), "failed to allocate memory for image");
image->toc_e.uuid = *uuid;
image->buffer = xmalloc(st.st_size, "failed to allocate image buffer");
if (fread(image->buffer, 1, st.st_size, fp) != st.st_size)
log_errx("Failed to read %s", filename);
image->toc_e.size = st.st_size;
fclose(fp);
return image;
}
static int write_image_to_file(const image_t *image, const char *filename)
{
FILE *fp;
fp = fopen(filename, "w");
if (fp == NULL)
log_err("fopen");
xfwrite(image->buffer, image->toc_e.size, fp, filename);
fclose(fp);
return 0;
}
static struct option *add_opt(struct option *opts, size_t *nr_opts,
const char *name, int has_arg, int val)
{
opts = realloc(opts, (*nr_opts + 1) * sizeof(*opts));
if (opts == NULL)
log_err("realloc");
opts[*nr_opts].name = name;
opts[*nr_opts].has_arg = has_arg;
opts[*nr_opts].flag = NULL;
opts[*nr_opts].val = val;
++*nr_opts;
return opts;
}
static struct option *fill_common_opts(struct option *opts, size_t *nr_opts,
int has_arg)
{
image_desc_t *desc;
for (desc = image_desc_head; desc != NULL; desc = desc->next)
opts = add_opt(opts, nr_opts, desc->cmdline_name, has_arg,
OPT_TOC_ENTRY);
return opts;
}
static void md_print(const unsigned char *md, size_t len)
{
size_t i;
for (i = 0; i < len; i++)
printf("%02x", md[i]);
}
static int info_cmd(int argc, char *argv[])
{
image_t *image;
fip_toc_header_t toc_header;
if (argc != 2)
info_usage();
argc--, argv++;
parse_fip(argv[0], &toc_header);
if (verbose) {
log_dbgx("toc_header[name]: 0x%llX",
(unsigned long long)toc_header.name);
log_dbgx("toc_header[serial_number]: 0x%llX",
(unsigned long long)toc_header.serial_number);
log_dbgx("toc_header[flags]: 0x%llX",
(unsigned long long)toc_header.flags);
}
for (image = image_head; image != NULL; image = image->next) {
image_desc_t *desc;
desc = lookup_image_desc_from_uuid(&image->toc_e.uuid);
assert(desc != NULL);
printf("%s: offset=0x%llX, size=0x%llX, cmdline=\"--%s\"",
desc->name,
(unsigned long long)image->toc_e.offset_address,
(unsigned long long)image->toc_e.size,
desc->cmdline_name);
if (verbose) {
unsigned char md[SHA256_DIGEST_LENGTH];
SHA256(image->buffer, image->toc_e.size, md);
printf(", sha256=");
md_print(md, sizeof(md));
}
putchar('\n');
}
free_images();
return 0;
}
static void info_usage(void)
{
printf("fiptool info FIP_FILENAME\n");
exit(1);
}
static int pack_images(const char *filename, uint64_t toc_flags, unsigned long align)
{
FILE *fp;
image_t *image;
fip_toc_header_t *toc_header;
fip_toc_entry_t *toc_entry;
char *buf;
uint64_t entry_offset, buf_size, payload_size = 0;
buf_size = sizeof(fip_toc_header_t) +
sizeof(fip_toc_entry_t) * (nr_images + 1);
buf = calloc(1, buf_size);
if (buf == NULL)
log_err("calloc");
/* Build up header and ToC entries from the image table. */
toc_header = (fip_toc_header_t *)buf;
toc_header->name = TOC_HEADER_NAME;
toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
toc_header->flags = toc_flags;
toc_entry = (fip_toc_entry_t *)(toc_header + 1);
entry_offset = buf_size;
for (image = image_head; image != NULL; image = image->next) {
payload_size += image->toc_e.size;
entry_offset = (entry_offset + align - 1) & ~(align - 1);
image->toc_e.offset_address = entry_offset;
*toc_entry++ = image->toc_e;
entry_offset += image->toc_e.size;
}
/* Append a null uuid entry to mark the end of ToC entries. */
memset(toc_entry, 0, sizeof(*toc_entry));
toc_entry->offset_address = entry_offset;
/* Generate the FIP file. */
fp = fopen(filename, "w");
if (fp == NULL)
log_err("fopen %s", filename);
if (verbose)
log_dbgx("Metadata size: %zu bytes", buf_size);
xfwrite(buf, buf_size, fp, filename);
free(buf);
if (verbose)
log_dbgx("Payload size: %zu bytes", payload_size);
for (image = image_head; image != NULL; image = image->next) {
if (fseek(fp, image->toc_e.offset_address, SEEK_SET))
log_errx("Failed to set file position");
xfwrite(image->buffer, image->toc_e.size, fp, filename);
}
fclose(fp);
return 0;
}
/*
* This function is shared between the create and update subcommands.
* The difference between the two subcommands is that when the FIP file
* is created, the parsing of an existing FIP is skipped. This results
* in update_fip() creating the new FIP file from scratch because the
* internal image table is not populated.
*/
static void update_fip(void)
{
image_desc_t *desc;
/* Add or replace images in the FIP file. */
for (desc = image_desc_head; desc != NULL; desc = desc->next) {
image_t *new_image, *old_image;
if (desc->action != DO_PACK)
continue;
new_image = read_image_from_file(&desc->uuid,
desc->action_arg);
old_image = lookup_image_from_uuid(&desc->uuid);
if (old_image != NULL) {
if (verbose) {
log_dbgx("Replacing %s with %s",
desc->cmdline_name,
desc->action_arg);
}
replace_image(new_image);
} else {
if (verbose)
log_dbgx("Adding image %s",
desc->action_arg);
add_image(new_image);
}
}
}
static void parse_plat_toc_flags(const char *arg, unsigned long long *toc_flags)
{
unsigned long long flags;
char *endptr;
errno = 0;
flags = strtoull(arg, &endptr, 16);
if (*endptr != '\0' || flags > UINT16_MAX || errno != 0)
log_errx("Invalid platform ToC flags: %s", arg);
/* Platform ToC flags is a 16-bit field occupying bits [32-47]. */
*toc_flags |= flags << 32;
}
static int is_power_of_2(unsigned long x)
{
return x && !(x & (x - 1));
}
static unsigned long get_image_align(char *arg)
{
char *endptr;
unsigned long align;
errno = 0;
align = strtoul(arg, &endptr, 10);
if (*endptr != '\0' || !is_power_of_2(align) || errno != 0)
log_errx("Invalid alignment: %s", arg);
return align;
}
static void parse_blob_opt(char *arg, uuid_t *uuid, char *filename, size_t len)
{
char *p;
for (p = strtok(arg, ","); p != NULL; p = strtok(NULL, ",")) {
if (strncmp(p, "uuid=", strlen("uuid=")) == 0) {
p += strlen("uuid=");
uuid_from_str(uuid, p);
} else if (strncmp(p, "file=", strlen("file=")) == 0) {
p += strlen("file=");
snprintf(filename, len, "%s", p);
}
}
}
static int create_cmd(int argc, char *argv[])
{
struct option *opts = NULL;
size_t nr_opts = 0;
unsigned long long toc_flags = 0;
unsigned long align = 1;
if (argc < 2)
create_usage();
opts = fill_common_opts(opts, &nr_opts, required_argument);
opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
OPT_PLAT_TOC_FLAGS);
opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
opts = add_opt(opts, &nr_opts, NULL, 0, 0);
while (1) {
int c, opt_index = 0;
c = getopt_long(argc, argv, "b:", opts, &opt_index);
if (c == -1)
break;
switch (c) {
case OPT_TOC_ENTRY: {
image_desc_t *desc;
desc = lookup_image_desc_from_opt(opts[opt_index].name);
set_image_desc_action(desc, DO_PACK, optarg);
break;
}
case OPT_PLAT_TOC_FLAGS:
parse_plat_toc_flags(optarg, &toc_flags);
break;
case OPT_ALIGN:
align = get_image_align(optarg);
break;
case 'b': {
char name[_UUID_STR_LEN + 1];
char filename[PATH_MAX] = { 0 };
uuid_t uuid = { 0 };
image_desc_t *desc;
parse_blob_opt(optarg, &uuid,
filename, sizeof(filename));
if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
filename[0] == '\0')
create_usage();
desc = lookup_image_desc_from_uuid(&uuid);
if (desc == NULL) {
uuid_to_str(name, sizeof(name), &uuid);
desc = new_image_desc(&uuid, name, "blob");
add_image_desc(desc);
}
set_image_desc_action(desc, DO_PACK, filename);
break;
}
default:
create_usage();
}
}
argc -= optind;
argv += optind;
free(opts);
if (argc == 0)
create_usage();
update_fip();
pack_images(argv[0], toc_flags, align);
free_images();
return 0;
}
static void create_usage(void)
{
toc_entry_t *toc_entry = toc_entries;
printf("fiptool create [opts] FIP_FILENAME\n");
printf("\n");
printf("Options:\n");
printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
printf(" --blob uuid=...,file=...\tAdd an image with the given UUID "
"pointed to by file.\n");
printf(" --plat-toc-flags <value>\t16-bit platform specific flag field "
"occupying bits 32-47 in 64-bit ToC header.\n");
fputc('\n', stderr);
printf("Specific images are packed with the following options:\n");
for (; toc_entry->cmdline_name != NULL; toc_entry++)
printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
toc_entry->name);
exit(1);
}
static int update_cmd(int argc, char *argv[])
{
struct option *opts = NULL;
size_t nr_opts = 0;
char outfile[PATH_MAX] = { 0 };
fip_toc_header_t toc_header = { 0 };
unsigned long long toc_flags = 0;
unsigned long align = 1;
int pflag = 0;
if (argc < 2)
update_usage();
opts = fill_common_opts(opts, &nr_opts, required_argument);
opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
opts = add_opt(opts, &nr_opts, "plat-toc-flags", required_argument,
OPT_PLAT_TOC_FLAGS);
opts = add_opt(opts, &nr_opts, NULL, 0, 0);
while (1) {
int c, opt_index = 0;
c = getopt_long(argc, argv, "b:o:", opts, &opt_index);
if (c == -1)
break;
switch (c) {
case OPT_TOC_ENTRY: {
image_desc_t *desc;
desc = lookup_image_desc_from_opt(opts[opt_index].name);
set_image_desc_action(desc, DO_PACK, optarg);
break;
}
case OPT_PLAT_TOC_FLAGS:
parse_plat_toc_flags(optarg, &toc_flags);
pflag = 1;
break;
case 'b': {
char name[_UUID_STR_LEN + 1];
char filename[PATH_MAX] = { 0 };
uuid_t uuid = { 0 };
image_desc_t *desc;
parse_blob_opt(optarg, &uuid,
filename, sizeof(filename));
if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
filename[0] == '\0')
update_usage();
desc = lookup_image_desc_from_uuid(&uuid);
if (desc == NULL) {
uuid_to_str(name, sizeof(name), &uuid);
desc = new_image_desc(&uuid, name, "blob");
add_image_desc(desc);
}
set_image_desc_action(desc, DO_PACK, filename);
break;
}
case OPT_ALIGN:
align = get_image_align(optarg);
break;
case 'o':
snprintf(outfile, sizeof(outfile), "%s", optarg);
break;
default:
update_usage();
}
}
argc -= optind;
argv += optind;
free(opts);
if (argc == 0)
update_usage();
if (outfile[0] == '\0')
snprintf(outfile, sizeof(outfile), "%s", argv[0]);
if (access(argv[0], F_OK) == 0)
parse_fip(argv[0], &toc_header);
if (pflag)
toc_header.flags &= ~(0xffffULL << 32);
toc_flags = (toc_header.flags |= toc_flags);
update_fip();
pack_images(outfile, toc_flags, align);
free_images();
return 0;
}
static void update_usage(void)
{
toc_entry_t *toc_entry = toc_entries;
printf("fiptool update [opts] FIP_FILENAME\n");
printf("\n");
printf("Options:\n");
printf(" --align <value>\t\tEach image is aligned to <value> (default: 1).\n");
printf(" --blob uuid=...,file=...\tAdd or update an image "
"with the given UUID pointed to by file.\n");
printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n");
printf(" --plat-toc-flags <value>\t16-bit platform specific flag field "
"occupying bits 32-47 in 64-bit ToC header.\n");
fputc('\n', stderr);
printf("Specific images are packed with the following options:\n");
for (; toc_entry->cmdline_name != NULL; toc_entry++)
printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
toc_entry->name);
exit(1);
}
static int unpack_cmd(int argc, char *argv[])
{
struct option *opts = NULL;
size_t nr_opts = 0;
char outdir[PATH_MAX] = { 0 };
image_desc_t *desc;
int fflag = 0;
int unpack_all = 1;
if (argc < 2)
unpack_usage();
opts = fill_common_opts(opts, &nr_opts, required_argument);
opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
opts = add_opt(opts, &nr_opts, NULL, 0, 0);
while (1) {
int c, opt_index = 0;
c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
if (c == -1)
break;
switch (c) {
case OPT_TOC_ENTRY: {
image_desc_t *desc;
desc = lookup_image_desc_from_opt(opts[opt_index].name);
set_image_desc_action(desc, DO_UNPACK, optarg);
unpack_all = 0;
break;
}
case 'b': {
char name[_UUID_STR_LEN + 1];
char filename[PATH_MAX] = { 0 };
uuid_t uuid = { 0 };
image_desc_t *desc;
parse_blob_opt(optarg, &uuid,
filename, sizeof(filename));
if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0 ||
filename[0] == '\0')
unpack_usage();
desc = lookup_image_desc_from_uuid(&uuid);
if (desc == NULL) {
uuid_to_str(name, sizeof(name), &uuid);
desc = new_image_desc(&uuid, name, "blob");
add_image_desc(desc);
}
set_image_desc_action(desc, DO_UNPACK, filename);
unpack_all = 0;
break;
}
case 'f':
fflag = 1;
break;
case 'o':
snprintf(outdir, sizeof(outdir), "%s", optarg);
break;
default:
unpack_usage();
}
}
argc -= optind;
argv += optind;
free(opts);
if (argc == 0)
unpack_usage();
parse_fip(argv[0], NULL);
if (outdir[0] != '\0')
if (chdir(outdir) == -1)
log_err("chdir %s", outdir);
/* Unpack all specified images. */
for (desc = image_desc_head; desc != NULL; desc = desc->next) {
char file[PATH_MAX];
image_t *image;
if (!unpack_all && desc->action != DO_UNPACK)
continue;
/* Build filename. */
if (desc->action_arg == NULL)
snprintf(file, sizeof(file), "%s.bin",
desc->cmdline_name);
else
snprintf(file, sizeof(file), "%s",
desc->action_arg);
image = lookup_image_from_uuid(&desc->uuid);
if (image == NULL) {
if (!unpack_all)
log_warnx("%s does not exist in %s",
file, argv[0]);
continue;
}
if (access(file, F_OK) != 0 || fflag) {
if (verbose)
log_dbgx("Unpacking %s", file);
write_image_to_file(image, file);
} else {
log_warnx("File %s already exists, use --force to overwrite it",
file);
}
}
free_images();
return 0;
}
static void unpack_usage(void)
{
toc_entry_t *toc_entry = toc_entries;
printf("fiptool unpack [opts] FIP_FILENAME\n");
printf("\n");
printf("Options:\n");
printf(" --blob uuid=...,file=...\tUnpack an image with the given UUID "
"to file.\n");
printf(" --force\t\t\tIf the output file already exists, use --force to "
"overwrite it.\n");
printf(" --out path\t\t\tSet the output directory path.\n");
fputc('\n', stderr);
printf("Specific images are unpacked with the following options:\n");
for (; toc_entry->cmdline_name != NULL; toc_entry++)
printf(" --%-16s FILENAME\t%s\n", toc_entry->cmdline_name,
toc_entry->name);
fputc('\n', stderr);
printf("If no options are provided, all images will be unpacked.\n");
exit(1);
}
static int remove_cmd(int argc, char *argv[])
{
struct option *opts = NULL;
size_t nr_opts = 0;
char outfile[PATH_MAX] = { 0 };
fip_toc_header_t toc_header;
image_desc_t *desc;
unsigned long align = 1;
int fflag = 0;
if (argc < 2)
remove_usage();
opts = fill_common_opts(opts, &nr_opts, no_argument);
opts = add_opt(opts, &nr_opts, "align", required_argument, OPT_ALIGN);
opts = add_opt(opts, &nr_opts, "blob", required_argument, 'b');
opts = add_opt(opts, &nr_opts, "force", no_argument, 'f');
opts = add_opt(opts, &nr_opts, "out", required_argument, 'o');
opts = add_opt(opts, &nr_opts, NULL, 0, 0);
while (1) {
int c, opt_index = 0;
c = getopt_long(argc, argv, "b:fo:", opts, &opt_index);
if (c == -1)
break;
switch (c) {
case OPT_TOC_ENTRY: {
image_desc_t *desc;
desc = lookup_image_desc_from_opt(opts[opt_index].name);
set_image_desc_action(desc, DO_REMOVE, NULL);
break;
}
case OPT_ALIGN:
align = get_image_align(optarg);
break;
case 'b': {
char name[_UUID_STR_LEN + 1], filename[PATH_MAX];
uuid_t uuid = { 0 };
image_desc_t *desc;
parse_blob_opt(optarg, &uuid,
filename, sizeof(filename));
if (memcmp(&uuid, &uuid_null, sizeof(uuid_t)) == 0)
remove_usage();
desc = lookup_image_desc_from_uuid(&uuid);
if (desc == NULL) {
uuid_to_str(name, sizeof(name), &uuid);
desc = new_image_desc(&uuid, name, "blob");
add_image_desc(desc);
}
set_image_desc_action(desc, DO_REMOVE, NULL);
break;
}
case 'f':
fflag = 1;
break;
case 'o':
snprintf(outfile, sizeof(outfile), "%s", optarg);
break;
default:
remove_usage();
}
}
argc -= optind;
argv += optind;
free(opts);
if (argc == 0)
remove_usage();
if (outfile[0] != '\0' && access(outfile, F_OK) == 0 && !fflag)
log_errx("File %s already exists, use --force to overwrite it",
outfile);
if (outfile[0] == '\0')
snprintf(outfile, sizeof(outfile), "%s", argv[0]);
parse_fip(argv[0], &toc_header);
for (desc = image_desc_head; desc != NULL; desc = desc->next) {
image_t *image;
if (desc->action != DO_REMOVE)
continue;
image = lookup_image_from_uuid(&desc->uuid);
if (image != NULL) {
if (verbose)
log_dbgx("Removing %s",
desc->cmdline_name);
remove_image(image);
} else {
log_warnx("%s does not exist in %s",
desc->cmdline_name, argv[0]);
}
}
pack_images(outfile, toc_header.flags, align);
free_images();
return 0;
}
static void remove_usage(void)
{
toc_entry_t *toc_entry = toc_entries;
printf("fiptool remove [opts] FIP_FILENAME\n");
printf("\n");
printf("Options:\n");
printf(" --align <value>\tEach image is aligned to <value> (default: 1).\n");
printf(" --blob uuid=...\tRemove an image with the given UUID.\n");
printf(" --force\t\tIf the output FIP file already exists, use --force to "
"overwrite it.\n");
printf(" --out FIP_FILENAME\tSet an alternative output FIP file.\n");
fputc('\n', stderr);
printf("Specific images are removed with the following options:\n");
for (; toc_entry->cmdline_name != NULL; toc_entry++)
printf(" --%-16s\t%s\n", toc_entry->cmdline_name,
toc_entry->name);
exit(1);
}
static int version_cmd(int argc, char *argv[])
{
#ifdef VERSION
puts(VERSION);
#else
/* If built from fiptool directory, VERSION is not set. */
puts("Unknown version");
#endif
return 0;
}
static void version_usage(void)
{
printf("fiptool version\n");
exit(1);
}
static int help_cmd(int argc, char *argv[])
{
int i;
if (argc < 2)
usage();
argc--, argv++;
for (i = 0; i < NELEM(cmds); i++) {
if (strcmp(cmds[i].name, argv[0]) == 0 &&
cmds[i].usage != NULL)
cmds[i].usage();
}
if (i == NELEM(cmds))
printf("No help for subcommand '%s'\n", argv[0]);
return 0;
}
static void usage(void)
{
printf("usage: fiptool [--verbose] <command> [<args>]\n");
printf("Global options supported:\n");
printf(" --verbose\tEnable verbose output for all commands.\n");
fputc('\n', stderr);
printf("Commands supported:\n");
printf(" info\t\tList images contained in FIP.\n");
printf(" create\tCreate a new FIP with the given images.\n");
printf(" update\tUpdate an existing FIP with the given images.\n");
printf(" unpack\tUnpack images from FIP.\n");
printf(" remove\tRemove images from FIP.\n");
printf(" version\tShow fiptool version.\n");
printf(" help\t\tShow help for given command.\n");
exit(1);
}
int main(int argc, char *argv[])
{
int i, ret = 0;
while (1) {
int c, opt_index = 0;
static struct option opts[] = {
{ "verbose", no_argument, NULL, 'v' },
{ NULL, no_argument, NULL, 0 }
};
/*
* Set POSIX mode so getopt stops at the first non-option
* which is the subcommand.
*/
c = getopt_long(argc, argv, "+v", opts, &opt_index);
if (c == -1)
break;
switch (c) {
case 'v':
verbose = 1;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
/* Reset optind for subsequent getopt processing. */
optind = 0;
if (argc == 0)
usage();
fill_image_descs();
for (i = 0; i < NELEM(cmds); i++) {
if (strcmp(cmds[i].name, argv[0]) == 0) {
ret = cmds[i].handler(argc, argv);
break;
}
}
if (i == NELEM(cmds))
usage();
free_image_descs();
return ret;
}