summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorFurquan Shaikh <furquan@google.com>2016-05-19 16:12:16 -0700
committerFurquan Shaikh <furquan@google.com>2016-05-30 23:51:42 +0200
commit233f1b6a386995038e3d6d72db80b4a325bbe39b (patch)
treeed719ce1345aa355bda066b8e305e3c7f7a6c674 /util
parent97d56fa1a2b2103ca6cf01cf45d0b9bf123cbef5 (diff)
ifwitool: Add new tool for managing IFWI images
- Supports following operations: 1. add raw/dir sub-partition 2. extract raw/dir sub-partition 3. print info 4. delete raw sub-partition 5. replace raw/dir sub-partition Change-Id: I683a0ab13cc50eb60eecca34db4a8ffefc8dccbd Signed-off-by: Furquan Shaikh <furquan@google.com> Reviewed-on: https://review.coreboot.org/14896 Tested-by: build bot (Jenkins) Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Diffstat (limited to 'util')
-rw-r--r--util/cbfstool/Makefile2
-rw-r--r--util/cbfstool/Makefile.inc8
-rw-r--r--util/cbfstool/ifwitool.c1975
3 files changed, 1985 insertions, 0 deletions
diff --git a/util/cbfstool/Makefile b/util/cbfstool/Makefile
index 61a1a63257..f49da3ed90 100644
--- a/util/cbfstool/Makefile
+++ b/util/cbfstool/Makefile
@@ -10,6 +10,7 @@ OBJCOPY ?= objcopy
all: $(objutil)/cbfstool/cbfstool \
$(objutil)/cbfstool/fmaptool \
$(objutil)/cbfstool/rmodtool \
+ $(objutil)/cbfstool/ifwitool \
.PHONY: clean
clean:
@@ -17,6 +18,7 @@ clean:
$(RM) $(objutil)/cbfstool/cbfstool $(cbfsobj)
$(RM) $(objutil)/cbfstool/fmaptool $(fmapobj)
$(RM) $(objutil)/cbfstool/rmodtool $(rmodobj)
+ $(RM) $(objutil)/cbfstool/ifwitool $(ifwiobj)
linux_trampoline.c: linux_trampoline.S
rm -f linux_trampoline.c
diff --git a/util/cbfstool/Makefile.inc b/util/cbfstool/Makefile.inc
index aa42f012ad..6c2b100a5e 100644
--- a/util/cbfstool/Makefile.inc
+++ b/util/cbfstool/Makefile.inc
@@ -58,6 +58,10 @@ rmodobj += common.o
rmodobj += elfheaders.o
rmodobj += xdr.o
+ifwiobj :=
+ifwiobj += ifwitool.o
+ifwiobj += common.o
+
TOOLCFLAGS ?= -Werror -Wall -Wextra
TOOLCFLAGS += -Wcast-qual -Wmissing-prototypes -Wredundant-decls -Wshadow
TOOLCFLAGS += -Wstrict-prototypes -Wwrite-strings
@@ -132,6 +136,10 @@ $(objutil)/cbfstool/rmodtool: $(addprefix $(objutil)/cbfstool/,$(rmodobj))
printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n"
$(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(rmodobj))
+$(objutil)/cbfstool/ifwitool: $(addprefix $(objutil)/cbfstool/,$(ifwiobj))
+ printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n"
+ $(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(ifwiobj))
+
# Yacc source is superset of header
$(objutil)/cbfstool/fmd.o: TOOLCFLAGS += -Wno-redundant-decls
$(objutil)/cbfstool/fmd_parser.o: TOOLCFLAGS += -Wno-redundant-decls
diff --git a/util/cbfstool/ifwitool.c b/util/cbfstool/ifwitool.c
new file mode 100644
index 0000000000..0359bc6e2c
--- /dev/null
+++ b/util/cbfstool/ifwitool.c
@@ -0,0 +1,1975 @@
+/*
+ * ifwitool, CLI utility for IFWI manipulation
+ *
+ * Copyright 2016 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <commonlib/endian.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "common.h"
+
+/*
+ * BPDT is Boot Partition Descriptor Table. It is located at the start of a
+ * logical boot partition(LBP). It stores information about the critical
+ * sub-partitions present within the LBP.
+ *
+ * S-BPDT is Secondary Boot Partition Descriptor Table. It is located after the
+ * critical sub-partitions and contains information about the non-critical
+ * sub-partitions present within the LBP.
+ *
+ * Both tables are identified by BPDT_SIGNATURE stored at the start of the
+ * table.
+ */
+#define BPDT_SIGNATURE (0x000055AA)
+
+/* Parameters passed in by caller. */
+static struct param {
+ const char *file_name;
+ const char *subpart_name;
+ const char *image_name;
+ bool dir_ops;
+ const char *dentry_name;
+} param;
+
+struct bpdt_header {
+ /*
+ * This is used to identify start of BPDT. It should always be
+ * BPDT_SIGNATURE.
+ */
+ uint32_t signature;
+ /* Count of BPDT entries present. */
+ uint16_t descriptor_count;
+ /* Version - Currently supported = 1. */
+ uint16_t bpdt_version;
+ /* Unused - Should be 0. */
+ uint32_t xor_redundant_block;
+ /* Version of IFWI build. */
+ uint32_t ifwi_version;
+ /* Version of FIT tool used to create IFWI. */
+ uint64_t fit_tool_version;
+} __attribute__((packed));
+#define BPDT_HEADER_SIZE (sizeof(struct bpdt_header))
+
+struct bpdt_entry {
+ /* Type of sub-partition. */
+ uint16_t type;
+ /* Attributes of sub-partition. */
+ uint16_t flags;
+ /* Offset of sub-partition from beginning of LBP. */
+ uint32_t offset;
+ /* Size in bytes of sub-partition. */
+ uint32_t size;
+} __attribute__((packed));
+#define BPDT_ENTRY_SIZE (sizeof(struct bpdt_entry))
+
+struct bpdt {
+ struct bpdt_header h;
+ /* In practice, this could be an array of 0 to n entries. */
+ struct bpdt_entry e[0];
+} __attribute__((packed));
+
+static inline size_t bpdt_size(struct bpdt_header *h)
+{
+ return (sizeof(*h) + BPDT_ENTRY_SIZE * h->descriptor_count);
+}
+
+/* Minimum size in bytes allocated to BPDT in IFWI. */
+#define BPDT_MIN_SIZE (512)
+
+/* Header to define directory header for sub-partition. */
+struct subpart_dir_header {
+ /* Should be SUBPART_DIR_MARKER. */
+ uint32_t marker;
+ /* Number of directory entries in the sub-partition. */
+ uint32_t num_entries;
+ /* Currenty supported - 1. */
+ uint8_t header_version;
+ /* Currenty supported - 1. */
+ uint8_t entry_version;
+ /* Length of directory header in bytes. */
+ uint8_t header_length;
+ /*
+ * 8-bit XOR checksum from first byte of header to last byte of
+ * directory entry.
+ */
+ uint8_t checksum;
+ /* ASCII short name of sub-partition. */
+ uint8_t name[4];
+} __attribute__((packed));
+#define SUBPART_DIR_HEADER_SIZE \
+ (sizeof(struct subpart_dir_header))
+#define SUBPART_DIR_MARKER 0x44504324
+#define SUBPART_DIR_HEADER_VERSION_SUPPORTED 1
+#define SUBPART_DIR_ENTRY_VERSION_SUPPORTED 1
+
+/* Structure for each directory entry for sub-partition. */
+struct subpart_dir_entry {
+ /* Name of directory entry - Not guaranteed to be NULL-terminated. */
+ uint8_t name[12];
+ /* Offset of entry from beginning of sub-partition. */
+ uint32_t offset;
+ /* Length in bytes of sub-directory entry. */
+ uint32_t length;
+ /* Must be zero. */
+ uint32_t rsvd;
+} __attribute__((packed));
+#define SUBPART_DIR_ENTRY_SIZE \
+ (sizeof(struct subpart_dir_entry))
+
+struct subpart_dir {
+ struct subpart_dir_header h;
+ /* In practice, this could be an array of 0 to n entries. */
+ struct subpart_dir_entry e[0];
+} __attribute__((packed));
+
+static inline size_t subpart_dir_size(struct subpart_dir_header *h)
+{
+ return (sizeof(*h) + SUBPART_DIR_ENTRY_SIZE * h->num_entries);
+}
+
+struct manifest_header {
+ uint32_t header_type;
+ uint32_t header_length;
+ uint32_t header_version;
+ uint32_t flags;
+ uint32_t vendor;
+ uint32_t date;
+ uint32_t size;
+ uint32_t id;
+ uint32_t rsvd;
+ uint64_t version;
+ uint32_t svn;
+ uint64_t rsvd1;
+ uint8_t rsvd2[64];
+ uint32_t modulus_size;
+ uint32_t exponent_size;
+ uint8_t public_key[256];
+ uint32_t exponent;
+ uint8_t signature[256];
+} __attribute__((packed));
+
+#define DWORD_SIZE 4
+#define MANIFEST_HDR_SIZE (sizeof(struct manifest_header))
+#define MANIFEST_ID_MAGIC (0x324e4d24)
+
+struct module {
+ uint8_t name[12];
+ uint8_t type;
+ uint8_t hash_alg;
+ uint16_t hash_size;
+ uint32_t metadata_size;
+ uint8_t metadata_hash[32];
+} __attribute__((packed));
+
+#define MODULE_SIZE (sizeof(struct module))
+
+struct signed_pkg_info_ext {
+ uint32_t ext_type;
+ uint32_t ext_length;
+ uint8_t name[4];
+ uint32_t vcn;
+ uint8_t bitmap[16];
+ uint32_t svn;
+ uint8_t rsvd[16];
+} __attribute__((packed));
+
+#define SIGNED_PKG_INFO_EXT_TYPE 0x15
+#define SIGNED_PKG_INFO_EXT_SIZE \
+ (sizeof(struct signed_pkg_info_ext))
+
+/*
+ * Attributes for various IFWI sub-partitions.
+ * LIES_WITHIN_BPDT_4K = Sub-Partition should lie within the same 4K block as
+ * BPDT.
+ * NON_CRITICAL_SUBPART = Sub-Partition entry should be present in S-BPDT.
+ * CONTAINS_DIR = Sub-Partition contains directory.
+ * AUTO_GENERATED = Sub-Partition is generated by the tool.
+ * MANDATORY_BPDT_ENTRY = Even if sub-partition is deleted, BPDT should contain
+ * an entry for it with size 0 and offset 0.
+ */
+enum subpart_attributes {
+ LIES_WITHIN_BPDT_4K = (1 << 0),
+ NON_CRITICAL_SUBPART = (1 << 1),
+ CONTAINS_DIR = (1 << 2),
+ AUTO_GENERATED = (1 << 3),
+ MANDATORY_BPDT_ENTRY = (1 << 4),
+};
+
+/* Type value for various IFWI sub-partitions. */
+enum bpdt_entry_type {
+ SMIP_TYPE = 0,
+ CSE_RBE_TYPE = 1,
+ CSE_BUP_TYPE = 2,
+ UCODE_TYPE = 3,
+ IBB_TYPE = 4,
+ S_BPDT_TYPE = 5,
+ OBB_TYPE = 6,
+ CSE_MAIN_TYPE = 7,
+ ISH_TYPE = 8,
+ CSE_IDLM_TYPE = 9,
+ IFP_OVERRIDE_TYPE = 10,
+ DEBUG_TOKENS_TYPE = 11,
+ UFS_PHY_TYPE = 12,
+ UFS_GPP_TYPE = 13,
+ PMC_TYPE = 14,
+ IUNIT_TYPE = 15,
+ NVM_CONFIG_TYPE = 16,
+ UEP_TYPE = 17,
+ UFS_RATE_B_TYPE = 18,
+ MAX_SUBPARTS = 19,
+};
+
+/*
+ * There are two order requirements for an IFWI image:
+ * 1. Order in which the sub-partitions lie within the BPDT entries.
+ * 2. Order in which the sub-partitions lie within the image.
+ *
+ * header_order defines #1 i.e. the order in which the sub-partitions should
+ * appear in the BPDT entries. pack_order defines #2 i.e. the order in which
+ * sub-partitions appear in the IFWI image. pack_order controls the offset and
+ * thus sub-partitions would have increasing offsets as we loop over pack_order.
+ */
+const enum bpdt_entry_type bpdt_header_order[MAX_SUBPARTS] = {
+ CSE_IDLM_TYPE,
+ IFP_OVERRIDE_TYPE,
+ S_BPDT_TYPE,
+ CSE_RBE_TYPE,
+ UFS_PHY_TYPE,
+ UFS_GPP_TYPE,
+ CSE_BUP_TYPE,
+ UEP_TYPE,
+ NVM_CONFIG_TYPE,
+ SMIP_TYPE,
+ PMC_TYPE,
+ UCODE_TYPE,
+ IBB_TYPE,
+ DEBUG_TOKENS_TYPE,
+ UFS_RATE_B_TYPE,
+ ISH_TYPE,
+ CSE_MAIN_TYPE,
+ OBB_TYPE,
+ IUNIT_TYPE,
+};
+
+const enum bpdt_entry_type bpdt_pack_order[MAX_SUBPARTS] = {
+ CSE_IDLM_TYPE,
+ UFS_PHY_TYPE,
+ UFS_GPP_TYPE,
+ IFP_OVERRIDE_TYPE,
+ UEP_TYPE,
+ IBB_TYPE,
+ NVM_CONFIG_TYPE,
+ SMIP_TYPE,
+ CSE_RBE_TYPE,
+ PMC_TYPE,
+ CSE_BUP_TYPE,
+ UCODE_TYPE,
+ DEBUG_TOKENS_TYPE,
+ UFS_RATE_B_TYPE,
+ S_BPDT_TYPE,
+ ISH_TYPE,
+ CSE_MAIN_TYPE,
+ OBB_TYPE,
+ IUNIT_TYPE,
+};
+
+/* Utility functions. */
+enum ifwi_ret {
+ COMMAND_ERR = -1,
+ NO_ACTION_REQUIRED = 0,
+ REPACK_REQUIRED = 1,
+};
+
+struct dir_ops {
+ enum ifwi_ret (*dir_add)(int);
+};
+
+static enum ifwi_ret ibbp_dir_add(int);
+
+const struct subpart_info {
+ const char *name;
+ const char *readable_name;
+ uint32_t attr;
+ struct dir_ops dir_ops;
+} subparts[MAX_SUBPARTS] = {
+ /* OEM SMIP */
+ [SMIP_TYPE] = {"SMIP", "SMIP", CONTAINS_DIR, {NULL}},
+ /* CSE RBE */
+ [CSE_RBE_TYPE] = {"RBEP", "CSE_RBE", CONTAINS_DIR |
+ MANDATORY_BPDT_ENTRY, {NULL}},
+ /* CSE BUP */
+ [CSE_BUP_TYPE] = {"FTPR", "CSE_BUP", CONTAINS_DIR |
+ MANDATORY_BPDT_ENTRY, {NULL}},
+ /* uCode */
+ [UCODE_TYPE] = {"UCOD", "Microcode", CONTAINS_DIR, {NULL}},
+ /* IBB */
+ [IBB_TYPE] = {"IBBP", "Bootblock", CONTAINS_DIR, {ibbp_dir_add}},
+ /* S-BPDT */
+ [S_BPDT_TYPE] = {"S_BPDT", "S-BPDT", AUTO_GENERATED |
+ MANDATORY_BPDT_ENTRY, {NULL}},
+ /* OBB */
+ [OBB_TYPE] = {"OBBP", "OEM boot block", CONTAINS_DIR |
+ NON_CRITICAL_SUBPART, {NULL}},
+ /* CSE Main */
+ [CSE_MAIN_TYPE] = {"NFTP", "CSE_MAIN", CONTAINS_DIR |
+ NON_CRITICAL_SUBPART, {NULL}},
+ /* ISH */
+ [ISH_TYPE] = {"ISHP", "ISH", NON_CRITICAL_SUBPART, {NULL}},
+ /* CSE IDLM */
+ [CSE_IDLM_TYPE] = {"DLMP", "CSE_IDLM", CONTAINS_DIR |
+ MANDATORY_BPDT_ENTRY, {NULL}},
+ /* IFP Override */
+ [IFP_OVERRIDE_TYPE] = {"IFP_OVERRIDE", "IFP_OVERRIDE",
+ LIES_WITHIN_BPDT_4K | MANDATORY_BPDT_ENTRY,
+ {NULL}},
+ /* Debug Tokens */
+ [DEBUG_TOKENS_TYPE] = {"DEBUG_TOKENS", "Debug Tokens", 0, {NULL}},
+ /* UFS Phy Configuration */
+ [UFS_PHY_TYPE] = {"UFS_PHY", "UFS Phy", LIES_WITHIN_BPDT_4K |
+ MANDATORY_BPDT_ENTRY, {NULL}},
+ /* UFS GPP LUN ID */
+ [UFS_GPP_TYPE] = {"UFS_GPP", "UFS GPP", LIES_WITHIN_BPDT_4K |
+ MANDATORY_BPDT_ENTRY, {NULL}},
+ /* PMC */
+ [PMC_TYPE] = {"PMCP", "PMC firmware", CONTAINS_DIR, {NULL}},
+ /* IUNIT */
+ [IUNIT_TYPE] = {"IUNP", "IUNIT", NON_CRITICAL_SUBPART, {NULL}},
+ /* NVM Config */
+ [NVM_CONFIG_TYPE] = {"NVM_CONFIG", "NVM Config", 0, {NULL}},
+ /* UEP */
+ [UEP_TYPE] = {"UEP", "UEP", LIES_WITHIN_BPDT_4K | MANDATORY_BPDT_ENTRY,
+ {NULL}},
+ /* UFS Rate B Config */
+ [UFS_RATE_B_TYPE] = {"UFS_RATE_B", "UFS Rate B Config", 0, {NULL}},
+};
+
+struct ifwi_image {
+ /* Data read from input file. */
+ struct buffer input_buff;
+
+ /* BPDT header and entries. */
+ struct buffer bpdt;
+ size_t input_ifwi_start_offset;
+ size_t input_ifwi_end_offset;
+
+ /* Subpartition content. */
+ struct buffer subpart_buf[MAX_SUBPARTS];
+} ifwi_image;
+
+static void alloc_buffer(struct buffer *b, size_t s, const char *n)
+{
+ if (buffer_create(b, s, n) == 0)
+ return;
+
+ ERROR("Buffer allocation failure for %s (size = %zx).\n", n, s);
+ exit(-1);
+}
+
+/*
+ * Read header/entry members in little-endian format.
+ * Returns the offset upto which the read was performed.
+*/
+static size_t read_member(void *src, size_t offset, size_t size_bytes,
+ void *dst)
+{
+ switch (size_bytes) {
+ case 1:
+ *(uint8_t *)dst = read_at_le8(src, offset);
+ break;
+ case 2:
+ *(uint16_t *)dst = read_at_le16(src, offset);
+ break;
+ case 4:
+ *(uint32_t *)dst = read_at_le32(src, offset);
+ break;
+ case 8:
+ *(uint64_t *)dst = read_at_le64(src, offset);
+ break;
+ default:
+ ERROR("Read size not supported %zd\n", size_bytes);
+ exit(-1);
+ }
+
+ return (offset + size_bytes);
+}
+
+/*
+ * Convert to little endian format.
+ * Returns the offset upto which the fixup was performed.
+ */
+static size_t fix_member(void *data, size_t offset, size_t size_bytes)
+{
+ uint8_t *src = (uint8_t *)data + offset;
+
+ switch (size_bytes) {
+ case 1:
+ write_at_le8(data, *(uint8_t *)src, offset);
+ break;
+ case 2:
+ write_at_le16(data, *(uint16_t *)src, offset);
+ break;
+ case 4:
+ write_at_le32(data, *(uint32_t *)src, offset);
+ break;
+ case 8:
+ write_at_le64(data, *(uint64_t *)src, offset);
+ break;
+ default:
+ ERROR("Write size not supported %zd\n", size_bytes);
+ exit(-1);
+ }
+ return (offset + size_bytes);
+}
+
+
+static void print_subpart_dir(struct subpart_dir *s)
+{
+ if (verbose == 0)
+ return;
+
+ size_t i;
+
+ printf("%-25s 0x%-23.8x\n", "Marker", s->h.marker);
+ printf("%-25s %-25d\n", "Num entries", s->h.num_entries);
+ printf("%-25s %-25d\n", "Header Version", s->h.header_version);
+ printf("%-25s %-25d\n", "Entry Version", s->h.entry_version);
+ printf("%-25s 0x%-23x\n", "Header Length", s->h.header_length);
+ printf("%-25s 0x%-23x\n", "Checksum", s->h.checksum);
+ printf("%-25s ", "Name");
+ for (i = 0; i < sizeof(s->h.name); i++)
+ printf("%c", s->h.name[i]);
+
+ printf("\n");
+
+ printf("%-25s%-25s%-25s%-25s%-25s\n", "Entry #", "Name", "Offset",
+ "Length", "Rsvd");
+
+ printf("=============================================================="
+ "===========================================================\n");
+
+ for (i = 0; i < s->h.num_entries; i++) {
+ printf("%-25zd%-25.12s0x%-23x0x%-23x0x%-23x\n", i+1,
+ s->e[i].name, s->e[i].offset, s->e[i].length,
+ s->e[i].rsvd);
+ }
+
+ printf("=============================================================="
+ "===========================================================\n");
+}
+
+static void bpdt_print_header(struct bpdt_header *h, const char *name)
+{
+ if (verbose == 0)
+ return;
+
+ printf("%-25s %-25s\n", "Header", name);
+ printf("%-25s 0x%-23.8x\n", "Signature", h->signature);
+ printf("%-25s %-25d\n", "Descriptor count", h->descriptor_count);
+ printf("%-25s %-25d\n", "BPDT Version", h->bpdt_version);
+ printf("%-25s 0x%-23x\n", "XOR checksum", h->xor_redundant_block);
+ printf("%-25s 0x%-23x\n", "IFWI Version", h->ifwi_version);
+ printf("%-25s 0x%-23lx\n", "FIT Tool Version", h->fit_tool_version);
+}
+
+static void bpdt_print_entries(struct bpdt_entry *e, size_t count,
+ const char *name)
+{
+ if (verbose == 0)
+ return;
+
+ printf("%s entries\n", name);
+
+ printf("%-25s%-25s%-25s%-25s%-25s%-25s%-25s%-25s\n", "Entry #",
+ "Sub-Partition", "Name", "Type", "Flags", "Offset", "Size",
+ "File Offset");
+
+ printf("=============================================================="
+ "=============================================================="
+ "=============================================================="
+ "===============\n");
+
+
+ size_t i;
+ for (i = 0; i < count; i++) {
+ printf("%-25zd%-25s%-25s%-25d0x%-23.08x0x%-23x0x%-23x0x%-23zx"
+ "\n", i+1, subparts[e[i].type].name,
+ subparts[e[i].type].readable_name, e[i].type, e[i].flags,
+ e[i].offset, e[i].size,
+ e[i].offset + ifwi_image.input_ifwi_start_offset);
+ }
+
+ printf("=============================================================="
+ "=============================================================="
+ "=============================================================="
+ "===============\n");
+
+}
+
+static void bpdt_validate_header(struct bpdt_header *h, const char *name)
+{
+ assert (h->signature == BPDT_SIGNATURE);
+
+ if (h->bpdt_version != 1) {
+ ERROR("Invalid header : %s\n", name);
+ exit(-1);
+ }
+
+ DEBUG("Validated header : %s\n", name);
+}
+
+static void bpdt_read_header(void *data, struct bpdt_header *h,
+ const char *name)
+{
+ size_t offset = 0;
+
+ offset = read_member(data, offset, sizeof(h->signature), &h->signature);
+ offset = read_member(data, offset, sizeof(h->descriptor_count),
+ &h->descriptor_count);
+ offset = read_member(data, offset, sizeof(h->bpdt_version),
+ &h->bpdt_version);
+ offset = read_member(data, offset, sizeof(h->xor_redundant_block),
+ &h->xor_redundant_block);
+ offset = read_member(data, offset, sizeof(h->ifwi_version),
+ &h->ifwi_version);
+ offset = read_member(data, offset, sizeof(h->fit_tool_version),
+ &h->fit_tool_version);
+
+ bpdt_validate_header(h, name);
+ bpdt_print_header(h, name);
+}
+
+static void bpdt_read_entries(void *data, struct bpdt *bpdt, const char *name)
+{
+ size_t i, offset = 0;
+ struct bpdt_entry *e = &bpdt->e[0];
+ size_t count = bpdt->h.descriptor_count;
+
+ for (i = 0; i < count; i++) {
+ offset = read_member(data, offset, sizeof(e[i].type),
+ &e[i].type);
+ offset = read_member(data, offset, sizeof(e[i].flags),
+ &e[i].flags);
+ offset = read_member(data, offset, sizeof(e[i].offset),
+ &e[i].offset);
+ offset = read_member(data, offset, sizeof(e[i].size),
+ &e[i].size);
+ }
+
+ bpdt_print_entries(e, count, name);
+}
+
+/*
+ * Given type of sub-partition, identify BPDT entry for it.
+ * Sub-Partition could lie either within BPDT or S-BPDT.
+ */
+static struct bpdt_entry *__find_entry_by_type(struct bpdt_entry *e,
+ size_t count, int type)
+{
+ size_t i;
+ for (i = 0; i < count; i++) {
+ if (e[i].type == type)
+ break;
+ }
+
+ if (i == count)
+ return NULL;
+
+ return &e[i];
+}
+
+static struct bpdt_entry *find_entry_by_type(int type)
+{
+ struct bpdt *b = buffer_get(&ifwi_image.bpdt);
+ if (b == NULL)
+ return NULL;
+
+ struct bpdt_entry *curr = __find_entry_by_type(&b->e[0],
+ b->h.descriptor_count,
+ type);
+
+ if (curr)
+ return curr;
+
+ b = buffer_get(&ifwi_image.subpart_buf[S_BPDT_TYPE]);
+ if (b == NULL)
+ return NULL;
+
+ return __find_entry_by_type(&b->e[0], b->h.descriptor_count, type);
+}
+
+/*
+ * Find sub-partition type given its name. If the name does not exist, returns
+ * -1.
+ */
+static int find_type_by_name(const char *name)
+{
+ int i;
+
+ for (i = 0; i < MAX_SUBPARTS; i++) {
+ if ((strlen(subparts[i].name) == strlen(name)) &&
+ (!strcmp(subparts[i].name, name)))
+ break;
+ }
+
+ if (i == MAX_SUBPARTS) {
+ ERROR("Invalid sub-partition name %s.\n", name);
+ return -1;
+ }
+
+ return i;
+}
+
+/*
+ * Read the content of a sub-partition from input file and store it in
+ * ifwi_image.subpart_buf[SUB-PARTITION_TYPE].
+ *
+ * Returns the maximum offset occupied by the sub-partitions.
+ */
+static size_t read_subpart_buf(void *data, size_t size, struct bpdt_entry *e,
+ size_t count)
+{
+ size_t i, type;
+ struct buffer *buf;
+ size_t max_offset = 0;
+
+ for (i = 0; i < count; i++) {
+ type = e[i].type;
+
+ if (type >= MAX_SUBPARTS) {
+ ERROR("Invalid sub-partition type %zd.\n", type);
+ exit(-1);
+ }
+
+ if (buffer_size(&ifwi_image.subpart_buf[type])) {
+ ERROR("Multiple sub-partitions of type %zd(%s).\n",
+ type, subparts[type].name);
+ exit(-1);
+ }
+
+ if (e[i].size == 0) {
+ INFO("Dummy sub-partition %zd(%s). Skipping.\n", type,
+ subparts[type].name);
+ continue;
+ }
+
+ assert((e[i].offset + e[i].size) <= size);
+
+ /*
+ * Sub-partitions in IFWI image are not in the same order as
+ * in BPDT entries. BPDT entires are in header_order whereas
+ * sub-partition offsets in the image are in pack_order.
+ */
+ if ((e[i].offset + e[i].size) > max_offset)
+ max_offset = e[i].offset + e[i].size;
+
+ /*
+ * S-BPDT sub-partition contains information about all the
+ * non-critical sub-partitions. Thus, size of S-BPDT
+ * sub-partition equals size of S-BPDT plus size of all the
+ * non-critical sub-partitions. Thus, reading whole of S-BPDT
+ * here would be redundant as the non-critical partitions are
+ * read and allocated buffers separately. Also, S-BPDT requires
+ * special handling for reading header and entries.
+ */
+ if (type == S_BPDT_TYPE)
+ continue;
+
+ buf = &ifwi_image.subpart_buf[type];
+
+ alloc_buffer(buf, e[i].size, subparts[type].name);
+ memcpy(buffer_get(buf), (uint8_t *)data + e[i].offset,
+ e[i].size);
+ }
+
+ assert(max_offset);
+ return max_offset;
+}
+
+/*
+ * Allocate buffer for bpdt header, entries and all sub-partition content.
+ * Returns offset in data where BPDT ends.
+ */
+static size_t alloc_bpdt_buffer(void *data, size_t size, size_t offset,
+ struct buffer *b, const char *name)
+{
+ struct bpdt_header bpdt_header;
+ assert((offset + BPDT_HEADER_SIZE) < size);
+ bpdt_read_header((uint8_t *)data + offset, &bpdt_header, name);
+
+ /* Buffer to read BPDT header and entries. */
+ alloc_buffer(b, bpdt_size(&bpdt_header), name);
+
+ struct bpdt *bpdt = buffer_get(b);
+ memcpy(&bpdt->h, &bpdt_header, BPDT_HEADER_SIZE);
+
+ /*
+ * If no entries are present, maximum offset occupied is (offset +
+ * BPDT_HEADER_SIZE).
+ */
+ if (bpdt->h.descriptor_count == 0)
+ return (offset + BPDT_HEADER_SIZE);
+
+ /* Read all entries. */
+ assert((offset + bpdt_size(&bpdt->h)) < size);
+ bpdt_read_entries((uint8_t *)data + offset + BPDT_HEADER_SIZE, bpdt,
+ name);
+
+ /* Read all sub-partition content in subpart_buf. */
+ return read_subpart_buf(data, size, &bpdt->e[0],
+ bpdt->h.descriptor_count);
+}
+
+static void parse_sbpdt(void *data, size_t size)
+{
+ struct bpdt_entry *s;
+ s = find_entry_by_type(S_BPDT_TYPE);
+ if (!s)
+ return;
+
+ assert(size > s->offset);
+
+ alloc_bpdt_buffer(data, size, s->offset,
+ &ifwi_image.subpart_buf[S_BPDT_TYPE],
+ "S-BPDT");
+}
+
+static uint8_t calc_checksum(struct subpart_dir *s)
+{
+ size_t size = subpart_dir_size(&s->h);
+ uint8_t *data = (uint8_t *)s;
+ uint8_t checksum = 0;
+ size_t i;
+
+ uint8_t old_checksum = s->h.checksum;
+ s->h.checksum = 0;
+
+ for (i = 0; i < size; i++) {
+ checksum ^= data[i];
+ }
+
+ s->h.checksum = old_checksum;
+ return checksum;
+}
+
+static void validate_subpart_dir(struct subpart_dir *s, const char *name,
+ bool checksum_check)
+{
+ if ((s->h.marker != SUBPART_DIR_MARKER) ||
+ (s->h.header_version != SUBPART_DIR_HEADER_VERSION_SUPPORTED) ||
+ (s->h.entry_version != SUBPART_DIR_ENTRY_VERSION_SUPPORTED) ||
+ (s->h.header_length != SUBPART_DIR_HEADER_SIZE)) {
+ ERROR("Invalid subpart_dir for %s.\n", name);
+ exit(-1);
+ }
+
+ if (checksum_check == 0)
+ return;
+
+ uint8_t checksum = calc_checksum(s);
+
+ if (checksum != s->h.checksum)
+ ERROR("Invalid checksum for %s(Expected=0x%x,Current=0x%x).\n",
+ name, checksum, s->h.checksum);
+}
+
+static void validate_subpart_dir_without_checksum(struct subpart_dir *s,
+ const char *name)
+{
+ validate_subpart_dir(s, name, 0);
+}
+
+static void validate_subpart_dir_with_checksum(struct subpart_dir *s,
+ const char *name)
+{
+ validate_subpart_dir(s, name, 1);
+}
+
+static void parse_subpart_dir(struct buffer *subpart_dir_buf,
+ struct buffer *input_buf, const char *name)
+{
+ struct subpart_dir_header hdr;
+ size_t offset = 0;
+ uint8_t *data = buffer_get(input_buf);
+ size_t size = buffer_size(input_buf);
+
+ /* Read Subpart_Dir header. */
+ assert(size >= SUBPART_DIR_HEADER_SIZE);
+ offset = read_member(data, offset, sizeof(hdr.marker), &hdr.marker);
+ offset = read_member(data, offset, sizeof(hdr.num_entries),
+ &hdr.num_entries);
+ offset = read_member(data, offset, sizeof(hdr.header_version),
+ &hdr.header_version);
+ offset = read_member(data, offset, sizeof(hdr.entry_version),
+ &hdr.entry_version);
+ offset = read_member(data, offset, sizeof(hdr.header_length),
+ &hdr.header_length);
+ offset = read_member(data, offset, sizeof(hdr.checksum), &hdr.checksum);
+ memcpy(hdr.name, data + offset, sizeof(hdr.name));
+ offset += sizeof(hdr.name);
+
+ validate_subpart_dir_without_checksum((struct subpart_dir *)&hdr, name);
+
+ assert(size > subpart_dir_size(&hdr));
+ alloc_buffer(subpart_dir_buf, subpart_dir_size(&hdr), "Subpart Dir");
+ memcpy(buffer_get(subpart_dir_buf), &hdr, SUBPART_DIR_HEADER_SIZE);
+
+ /* Read Subpart Dir entries. */
+ struct subpart_dir *subpart_dir = buffer_get(subpart_dir_buf);
+ struct subpart_dir_entry *e = &subpart_dir->e[0];
+ uint32_t i;
+ for (i = 0; i < hdr.num_entries; i++) {
+ memcpy(e[i].name, data + offset, sizeof(e[i].name));
+ offset += sizeof(e[i].name);
+ offset = read_member(data, offset, sizeof(e[i].offset),
+ &e[i].offset);
+ offset = read_member(data, offset, sizeof(e[i].length),
+ &e[i].length);
+ offset = read_member(data, offset, sizeof(e[i].rsvd),
+ &e[i].rsvd);
+ }
+
+ validate_subpart_dir_with_checksum(subpart_dir, name);
+
+ print_subpart_dir(subpart_dir);
+}
+
+/* Parse input image file to identify different sub-partitions. */
+static int ifwi_parse(void)
+{
+ DEBUG("Parsing IFWI image...\n");
+ const char *image_name = param.image_name;
+
+ /* Read input file. */
+ struct buffer *buff = &ifwi_image.input_buff;
+ if (buffer_from_file(buff, image_name)) {
+ ERROR("Failed to read input file %s.\n", image_name);
+ return -1;
+ }
+
+ INFO("Buffer %p size 0x%zx\n", buff->data, buff->size);
+
+ /* Look for BPDT signature at 4K intervals. */
+ size_t offset = 0;
+ void *data = buffer_get(buff);
+
+ while (offset < buffer_size(buff)) {
+ if (read_at_le32(data, offset) == BPDT_SIGNATURE)
+ break;
+ offset += 4 * KiB;
+ }
+
+ if (offset >= buffer_size(buff)) {
+ ERROR("Image does not contain BPDT!!\n");
+ return -1;
+ }
+
+ ifwi_image.input_ifwi_start_offset = offset;
+ INFO("BPDT starts at offset 0x%zx.\n", offset);
+
+ data = (uint8_t *)data + offset;
+ size_t ifwi_size = buffer_size(buff) - offset;
+
+ /* Read BPDT and sub-partitions. */
+ uintptr_t end_offset;
+ end_offset = ifwi_image.input_ifwi_start_offset +
+ alloc_bpdt_buffer(data, ifwi_size, 0, &ifwi_image.bpdt, "BPDT");
+
+ /* Parse S-BPDT, if any. */
+ parse_sbpdt(data, ifwi_size);
+
+ /*
+ * Store end offset of IFWI. Required for copying any trailing non-IFWI
+ * part of the image.
+ * ASSUMPTION: IFWI image always ends on a 4K boundary.
+ */
+ ifwi_image.input_ifwi_end_offset = ALIGN(end_offset, 4 * KiB);
+ DEBUG("Parsing done.\n");
+
+ return 0;
+}
+
+/*
+ * This function is used by repack to count the number of BPDT and S-BPDT
+ * entries that are present. It frees the current buffers used by the entries
+ * and allocates fresh buffers that can be used for repacking. Returns BPDT
+ * entries which are empty and need to be filled in.
+ */
+static void __bpdt_reset(struct buffer *b, size_t count, size_t size)
+{
+ size_t bpdt_size = BPDT_HEADER_SIZE + count * BPDT_ENTRY_SIZE;
+ assert(size >= bpdt_size);
+
+ /*
+ * If buffer does not have the required size, allocate a fresh buffer.
+ */
+ if (buffer_size(b) != size) {
+ struct buffer temp;
+ alloc_buffer(&temp, size, b->name);
+ memcpy(buffer_get(&temp), buffer_get(b), buffer_size(b));
+ buffer_delete(b);
+ *b = temp;
+ }
+
+ struct bpdt *bpdt = buffer_get(b);
+ uint8_t *ptr = (uint8_t *)&bpdt->e[0];
+ size_t entries_size = BPDT_ENTRY_SIZE * count;
+
+ /* Zero out BPDT entries. */
+ memset(ptr, 0, entries_size);
+ /* Fill any pad-space with FF. */
+ memset(ptr + entries_size, 0xFF, size - bpdt_size);
+
+ bpdt->h.descriptor_count = count;
+}
+
+static void bpdt_reset(void)
+{
+ size_t i;
+ size_t bpdt_count = 0, sbpdt_count = 0, dummy_bpdt_count = 0;
+
+ /* Count number of BPDT and S-BPDT entries. */
+ for (i = 0; i < MAX_SUBPARTS; i++) {
+ if (buffer_size(&ifwi_image.subpart_buf[i]) == 0) {
+ if (subparts[i].attr & MANDATORY_BPDT_ENTRY) {
+ bpdt_count++;
+ dummy_bpdt_count++;
+ }
+ continue;
+ }
+
+ if (subparts[i].attr & NON_CRITICAL_SUBPART)
+ sbpdt_count++;
+ else
+ bpdt_count++;
+ }
+
+ DEBUG("Count: BPDT = %zd, Dummy BPDT = %zd, S-BPDT = %zd\n", bpdt_count,
+ dummy_bpdt_count, sbpdt_count);
+
+ /* Update BPDT if required. */
+ size_t bpdt_size = MAX(BPDT_MIN_SIZE,
+ BPDT_HEADER_SIZE + bpdt_count * BPDT_ENTRY_SIZE);
+ __bpdt_reset(&ifwi_image.bpdt, bpdt_count, bpdt_size);
+
+ /* Update S-BPDT if required. */
+ bpdt_size = ALIGN(BPDT_HEADER_SIZE + sbpdt_count * BPDT_ENTRY_SIZE,
+ 4 * KiB);
+ __bpdt_reset(&ifwi_image.subpart_buf[S_BPDT_TYPE], sbpdt_count,
+ bpdt_size);
+}
+
+/* Initialize BPDT entries in header order. */
+static void bpdt_entries_init_header_order(void)
+{
+ int i, type;
+ size_t size;
+
+ struct bpdt *bpdt, *sbpdt, *curr;
+ size_t bpdt_curr = 0, sbpdt_curr = 0, *count_ptr;
+
+ bpdt = buffer_get(&ifwi_image.bpdt);
+ sbpdt = buffer_get(&ifwi_image.subpart_buf[S_BPDT_TYPE]);
+
+ for (i = 0; i < MAX_SUBPARTS; i++) {
+ type = bpdt_header_order[i];
+ size = buffer_size(&ifwi_image.subpart_buf[type]);
+
+ if ((size == 0) && !(subparts[type].attr &
+ MANDATORY_BPDT_ENTRY))
+ continue;
+
+ if (subparts[type].attr & NON_CRITICAL_SUBPART) {
+ curr = sbpdt;
+ count_ptr = &sbpdt_curr;
+ } else {
+ curr = bpdt;
+ count_ptr = &bpdt_curr;
+ }
+
+ assert(*count_ptr < curr->h.descriptor_count);
+ curr->e[*count_ptr].type = type;
+ curr->e[*count_ptr].flags = 0;
+ curr->e[*count_ptr].offset = 0;
+ curr->e[*count_ptr].size = size;
+
+ (*count_ptr)++;
+ }
+}
+
+static void pad_buffer(struct buffer *b, size_t size)
+{
+ size_t buff_size = buffer_size(b);
+
+ assert(buff_size <= size);
+
+ if (buff_size == size)
+ return;
+
+ struct buffer temp;
+ alloc_buffer(&temp, size, b->name);
+ uint8_t *data = buffer_get(&temp);
+
+ memcpy(data, buffer_get(b), buff_size);
+ memset(data + buff_size, 0xFF, size - buff_size);
+
+ *b = temp;
+}
+
+/* Initialize offsets of entries using pack order. */
+static void bpdt_entries_init_pack_order(void)
+{
+ int i, type;
+ struct bpdt_entry *curr;
+ size_t curr_offset, curr_end;
+
+ curr_offset = MAX(BPDT_MIN_SIZE, buffer_size(&ifwi_image.bpdt));
+
+ /*
+ * There are two types of sub-partitions that need to be handled here:
+ * 1. Sub-partitions that lie within the same 4K as BPDT
+ * 2. Sub-partitions that lie outside the 4K of BPDT
+ *
+ * For sub-partitions of type # 1, there is no requirement on the start
+ * or end of the sub-partition. They need to be packed in without any
+ * holes left in between. If there is any empty space left after the end
+ * of the last sub-partition in 4K of BPDT, then that space needs to be
+ * padded with FF bytes, but the size of the last sub-partition remains
+ * unchanged.
+ *
+ * For sub-partitions of type # 2, both the start and end should be a
+ * multiple of 4K. If not, then it needs to be padded with FF bytes and
+ * size adjusted such that the sub-partition ends on 4K boundary.
+ */
+
+ /* #1 Sub-partitions that lie within same 4K as BPDT. */
+ struct buffer *last_bpdt_buff = &ifwi_image.bpdt;
+
+ for (i = 0; i < MAX_SUBPARTS; i++) {
+ type = bpdt_pack_order[i];
+ curr = find_entry_by_type(type);
+
+ if ((curr == NULL) || (curr->size == 0))
+ continue;
+
+ if (!(subparts[type].attr & LIES_WITHIN_BPDT_4K))
+ continue;
+
+ curr->offset = curr_offset;
+ curr_offset = curr->offset + curr->size;
+ last_bpdt_buff = &ifwi_image.subpart_buf[type];
+ DEBUG("type=%d, curr_offset=0x%zx, curr->offset=0x%x, "
+ "curr->size=0x%x, buff_size=0x%zx\n", type, curr_offset,
+ curr->offset, curr->size,
+ buffer_size(&ifwi_image.subpart_buf[type]));
+ }
+
+ /* Pad ff bytes if there is any empty space left in BPDT 4K. */
+ curr_end = ALIGN(curr_offset, 4 * KiB);
+ pad_buffer(last_bpdt_buff,
+ buffer_size(last_bpdt_buff) + (curr_end - curr_offset));
+ curr_offset = curr_end;
+
+ /* #2 Sub-partitions that lie outside of BPDT 4K. */
+ for (i = 0; i < MAX_SUBPARTS; i++) {
+ type = bpdt_pack_order[i];
+ curr = find_entry_by_type(type);
+
+ if ((curr == NULL) || (curr->size == 0))
+ continue;
+
+ if (subparts[type].attr & LIES_WITHIN_BPDT_4K)
+ continue;
+
+ assert(curr_offset == ALIGN(curr_offset, 4 * KiB));
+ curr->offset = curr_offset;
+ curr_end = ALIGN(curr->offset + curr->size, 4 * KiB);
+ curr->size = curr_end - curr->offset;
+
+ pad_buffer(&ifwi_image.subpart_buf[type], curr->size);
+
+ curr_offset = curr_end;
+ DEBUG("type=%d, curr_offset=0x%zx, curr->offset=0x%x, "
+ "curr->size=0x%x, buff_size=0x%zx\n", type, curr_offset,
+ curr->offset, curr->size,
+ buffer_size(&ifwi_image.subpart_buf[type]));
+ }
+
+ /*
+ * Update size of S-BPDT to include size of all non-critical
+ * sub-partitions.
+ *
+ * Assumption: S-BPDT always lies at the end of IFWI image.
+ */
+ curr = find_entry_by_type(S_BPDT_TYPE);
+ assert(curr);
+
+ assert(curr_offset == ALIGN(curr_offset, 4 * KiB));
+ curr->size = curr_offset - curr->offset;
+}
+
+/* Convert all members of BPDT to little-endian format. */
+static void bpdt_fixup_write_buffer(struct buffer *buf)
+{
+ struct bpdt *s = buffer_get(buf);
+
+ struct bpdt_header *h = &s->h;
+ struct bpdt_entry *e = &s->e[0];
+
+ size_t count = h->descriptor_count;
+
+ size_t offset = 0;
+
+ offset = fix_member(&h->signature, offset, sizeof(h->signature));
+ offset = fix_member(&h->descriptor_count, offset,
+ sizeof(h->descriptor_count));
+ offset = fix_member(&h->bpdt_version, offset, sizeof(h->bpdt_version));
+ offset = fix_member(&h->xor_redundant_block, offset,
+ sizeof(h->xor_redundant_block));
+ offset = fix_member(&h->ifwi_version, offset, sizeof(h->ifwi_version));
+ offset = fix_member(&h->fit_tool_version, offset,
+ sizeof(h->fit_tool_version));
+
+ uint32_t i;
+ for (i = 0; i < count; i++) {
+ offset = fix_member(&e[i].type, offset, sizeof(e[i].type));
+ offset = fix_member(&e[i].flags, offset, sizeof(e[i].flags));
+ offset = fix_member(&e[i].offset, offset, sizeof(e[i].offset));
+ offset = fix_member(&e[i].size, offset, sizeof(e[i].size));
+ }
+}
+
+/* Write BPDT to output buffer after fixup. */
+static void bpdt_write(struct buffer *dst, size_t offset, struct buffer *src)
+{
+ bpdt_fixup_write_buffer(src);
+ memcpy(buffer_get(dst) + offset, buffer_get(src), buffer_size(src));
+}
+
+/*
+ * Follows these steps to re-create image:
+ * 1. Write any non-IFWI prefix.
+ * 2. Write out BPDT header and entries.
+ * 3. Write sub-partition buffers to respective offsets.
+ * 4. Write any non-IFWI suffix.
+ *
+ * While performing the above steps, make sure that any empty holes are filled
+ * with FF.
+ */
+static void ifwi_write(const char *image_name)
+{
+ struct bpdt_entry *s = find_entry_by_type(S_BPDT_TYPE);
+ assert(s);
+
+ size_t ifwi_start, ifwi_end, file_end;
+
+ ifwi_start = ifwi_image.input_ifwi_start_offset;
+ ifwi_end = ifwi_start + ALIGN(s->offset + s->size, 4 * KiB);
+ file_end = ifwi_end + (buffer_size(&ifwi_image.input_buff) -
+ ifwi_image.input_ifwi_end_offset);
+
+ struct buffer b;
+
+ alloc_buffer(&b, file_end, "Final-IFWI");
+
+ uint8_t *input_data = buffer_get(&ifwi_image.input_buff);
+ uint8_t *output_data = buffer_get(&b);
+
+ DEBUG("ifwi_start:0x%zx, ifwi_end:0x%zx, file_end:0x%zx\n", ifwi_start,
+ ifwi_end, file_end);
+
+ /* Copy non-IFWI prefix, if any. */
+ memcpy(output_data, input_data, ifwi_start);
+
+ DEBUG("Copied non-IFWI prefix (offset=0x0, size=0x%zx).\n", ifwi_start);
+
+ struct buffer ifwi;
+ buffer_splice(&ifwi, &b, ifwi_start, ifwi_end - ifwi_start);
+ uint8_t *ifwi_data = buffer_get(&ifwi);
+
+ /* Copy sub-partitions using pack_order. */
+ struct bpdt_entry *curr;
+ struct buffer *subpart_buf;
+ int i, type;
+ for (i = 0; i < MAX_SUBPARTS; i++) {
+ type = bpdt_pack_order[i];
+
+ if (type == S_BPDT_TYPE)
+ continue;
+
+ curr = find_entry_by_type(type);
+
+ if ((curr == NULL) || (curr->size == 0))
+ continue;
+
+ subpart_buf = &ifwi_image.subpart_buf[type];
+
+ DEBUG("curr->offset=0x%x, curr->size=0x%x, type=%d, "
+ "write_size=0x%zx\n", curr->offset, curr->size, type,
+ buffer_size(subpart_buf));
+
+ assert((curr->offset + buffer_size(subpart_buf)) <=
+ buffer_size(&ifwi));
+
+ memcpy(ifwi_data + curr->offset, buffer_get(subpart_buf),
+ buffer_size(subpart_buf));
+ }
+
+ /* Copy non-IFWI suffix, if any. */
+ if (ifwi_end != file_end) {
+ memcpy(output_data + ifwi_end,
+ input_data + ifwi_image.input_ifwi_end_offset,
+ file_end - ifwi_end);
+ DEBUG("Copied non-IFWI suffix (offset=0x%zx,size=0x%zx).\n",
+ ifwi_end, file_end - ifwi_end);
+ }
+
+ /*
+ * Convert BPDT to little-endian format and write it to output buffer.
+ * S-BPDT is written first and then BPDT.
+ */
+ bpdt_write(&ifwi, s->offset, &ifwi_image.subpart_buf[S_BPDT_TYPE]);
+ bpdt_write(&ifwi, 0, &ifwi_image.bpdt);
+
+ if (buffer_write_file(&b, image_name)) {
+ ERROR("File write error\n");
+ exit(-1);
+ }
+
+ buffer_delete(&b);
+ printf("Image written successfully to %s.\n", image_name);
+}
+
+/*
+ * Calculate size and offset of each sub-partition again since it might have
+ * changed because of add/delete operation. Also, re-create BPDT and S-BPDT
+ * entries and write back the new IFWI image to file.
+ */
+static void ifwi_repack(void)
+{
+ bpdt_reset();
+ bpdt_entries_init_header_order();
+ bpdt_entries_init_pack_order();
+
+ struct bpdt *b = buffer_get(&ifwi_image.bpdt);
+ bpdt_print_entries(&b->e[0], b->h.descriptor_count, "BPDT");
+
+ b = buffer_get(&ifwi_image.subpart_buf[S_BPDT_TYPE]);
+ bpdt_print_entries(&b->e[0], b->h.descriptor_count, "S-BPDT");
+
+ DEBUG("Repack done.. writing image.\n");
+ ifwi_write(param.image_name);
+}
+
+static void init_subpart_dir_header(struct subpart_dir_header *hdr,
+ size_t count, const char *name)
+{
+ memset(hdr, 0, sizeof(*hdr));
+
+ hdr->marker = SUBPART_DIR_MARKER;
+ hdr->num_entries = count;
+ hdr->header_version = SUBPART_DIR_HEADER_VERSION_SUPPORTED;
+ hdr->entry_version = SUBPART_DIR_ENTRY_VERSION_SUPPORTED;
+ hdr->header_length = SUBPART_DIR_HEADER_SIZE;
+ memcpy(hdr->name, name, sizeof(hdr->name));
+}
+
+static size_t init_subpart_dir_entry(struct subpart_dir_entry *e,
+ struct buffer *b, size_t offset)
+{
+ memset(e, 0, sizeof(*e));
+
+ assert(strlen(b->name) <= sizeof(e->name));
+ strncpy((char *)e->name, (char *)b->name, sizeof(e->name));
+ e->offset = offset;
+ e->length = buffer_size(b);
+
+ return (offset + buffer_size(b));
+}
+
+static void init_manifest_header(struct manifest_header *hdr, size_t size)
+{
+ memset(hdr, 0, sizeof(*hdr));
+
+ hdr->header_type = 0x4;
+ assert((MANIFEST_HDR_SIZE % DWORD_SIZE) == 0);
+ hdr->header_length = MANIFEST_HDR_SIZE / DWORD_SIZE;
+ hdr->header_version = 0x10000;
+ hdr->vendor = 0x8086;
+
+ struct tm *local_time;
+ time_t curr_time;
+ char buffer[11];
+
+ curr_time = time(NULL);
+ local_time = localtime(&curr_time);
+ strftime(buffer, sizeof(buffer), "0x%Y%m%d", local_time);
+ hdr->date = strtoul(buffer, NULL, 16);
+
+ assert((size % DWORD_SIZE) == 0);
+ hdr->size = size / DWORD_SIZE;
+ hdr->id = MANIFEST_ID_MAGIC;
+}
+
+static void init_signed_pkg_info_ext(struct signed_pkg_info_ext *ext,
+ size_t count, const char *name)
+{
+ memset(ext, 0, sizeof(*ext));
+
+ ext->ext_type = SIGNED_PKG_INFO_EXT_TYPE;
+ ext->ext_length = SIGNED_PKG_INFO_EXT_SIZE + count * MODULE_SIZE;
+ memcpy(ext->name, name, sizeof(ext->name));
+}
+
+static void subpart_dir_fixup_write_buffer(struct buffer *buf)
+{
+ struct subpart_dir *s = buffer_get(buf);
+ struct subpart_dir_header *h = &s->h;
+ struct subpart_dir_entry *e = &s->e[0];
+
+ size_t count = h->num_entries;
+ size_t offset = 0;
+
+ offset = fix_member(&h->marker, offset, sizeof(h->marker));
+ offset = fix_member(&h->num_entries, offset, sizeof(h->num_entries));
+ offset = fix_member(&h->header_version, offset,
+ sizeof(h->header_version));
+ offset = fix_member(&h->entry_version, offset,
+ sizeof(h->entry_version));
+ offset = fix_member(&h->header_length, offset,
+ sizeof(h->header_length));
+ offset = fix_member(&h->checksum, offset, sizeof(h->checksum));
+ offset += sizeof(h->name);
+
+ uint32_t i;
+ for (i = 0; i < count; i++) {
+ offset += sizeof(e[i].name);
+ offset = fix_member(&e[i].offset, offset, sizeof(e[i].offset));
+ offset = fix_member(&e[i].length, offset, sizeof(e[i].length));
+ offset = fix_member(&e[i].rsvd, offset, sizeof(e[i].rsvd));
+ }
+}
+
+static void create_subpart(struct buffer *dst, struct buffer *info[],
+ size_t count, const char *name)
+{
+ struct buffer subpart_dir_buff;
+ size_t size = SUBPART_DIR_HEADER_SIZE + count * SUBPART_DIR_ENTRY_SIZE;
+
+ alloc_buffer(&subpart_dir_buff, size, "subpart-dir");
+
+ struct subpart_dir_header *h = buffer_get(&subpart_dir_buff);
+ struct subpart_dir_entry *e = (struct subpart_dir_entry *)(h + 1);
+
+ init_subpart_dir_header(h, count, name);
+
+ size_t curr_offset = size;
+ size_t i;
+
+ for (i = 0; i < count; i++) {
+ curr_offset = init_subpart_dir_entry(&e[i], info[i],
+ curr_offset);
+ }
+
+ alloc_buffer(dst, curr_offset, name);
+ uint8_t *data = buffer_get(dst);
+
+ for (i = 0; i < count; i++) {
+ memcpy(data + e[i].offset, buffer_get(info[i]),
+ buffer_size(info[i]));
+ }
+
+ h->checksum = calc_checksum(buffer_get(&subpart_dir_buff));
+
+ struct subpart_dir *dir = buffer_get(&subpart_dir_buff);
+
+ print_subpart_dir(dir);
+
+ subpart_dir_fixup_write_buffer(&subpart_dir_buff);
+ memcpy(data, dir, buffer_size(&subpart_dir_buff));
+
+ buffer_delete(&subpart_dir_buff);
+}
+
+static enum ifwi_ret ibbp_dir_add(int type)
+{
+#define DUMMY_IBB_SIZE (4 * KiB)
+
+ assert (type == IBB_TYPE);
+
+ /*
+ * Entry # 1 - IBBP.man
+ * Contains manifest header and signed pkg info extension.
+ */
+ struct buffer manifest;
+ size_t size = MANIFEST_HDR_SIZE + SIGNED_PKG_INFO_EXT_SIZE;
+ alloc_buffer(&manifest, size, "IBBP.man");
+
+ struct manifest_header *man_hdr = buffer_get(&manifest);
+ init_manifest_header(man_hdr, size);
+
+ struct signed_pkg_info_ext *ext;
+ ext = (struct signed_pkg_info_ext *)(man_hdr + 1);
+
+ init_signed_pkg_info_ext(ext, 0, subparts[type].name);
+
+ /* Entry # 2 - IBBL */
+ struct buffer ibbl;
+ if (buffer_from_file(&ibbl, param.file_name))
+ return COMMAND_ERR;
+
+ /* Entry # 3 - IBB */
+ struct buffer ibb;
+ alloc_buffer(&ibb, DUMMY_IBB_SIZE, "IBB");
+ memset(buffer_get(&ibb), 0xFF, DUMMY_IBB_SIZE);
+
+ /* Create subpartition. */
+ struct buffer *info[] = {
+ &manifest, &ibbl, &ibb,
+ };
+ create_subpart(&ifwi_image.subpart_buf[type], &info[0],
+ ARRAY_SIZE(info), subparts[type].name);
+
+ return REPACK_REQUIRED;
+}
+
+static enum ifwi_ret ifwi_raw_add(int type)
+{
+ if (buffer_from_file(&ifwi_image.subpart_buf[type], param.file_name))
+ return COMMAND_ERR;
+
+ printf("Sub-partition %s(%d) added from file %s.\n", param.subpart_name,
+ type, param.file_name);
+ return REPACK_REQUIRED;
+}
+
+static enum ifwi_ret ifwi_dir_add(int type)
+{
+ if (!(subparts[type].attr & CONTAINS_DIR) ||
+ (subparts[type].dir_ops.dir_add == NULL)) {
+ ERROR("Sub-Partition %s(%d) does not support dir ops.\n",
+ subparts[type].name, type);
+ return COMMAND_ERR;
+ }
+
+ if (!param.dentry_name) {
+ ERROR("%s: -e option required\n", __func__);
+ return COMMAND_ERR;
+ }
+
+ enum ifwi_ret ret = subparts[type].dir_ops.dir_add(type);
+ if (ret != COMMAND_ERR)
+ printf("Sub-partition %s(%d) entry %s added from file %s.\n",
+ param.subpart_name, type, param.dentry_name,
+ param.file_name);
+ else
+ ERROR("Sub-partition dir operation failed.\n");
+
+ return ret;
+}
+
+static enum ifwi_ret ifwi_add(void)
+{
+ if (!param.file_name) {
+ ERROR("%s: -f option required\n", __func__);
+ return COMMAND_ERR;
+ }
+
+ if (!param.subpart_name) {
+ ERROR("%s: -n option required\n", __func__);
+ return COMMAND_ERR;
+ }
+
+ int type = find_type_by_name(param.subpart_name);
+ if (type == -1)
+ return COMMAND_ERR;
+
+ const struct subpart_info *curr_subpart = &subparts[type];
+
+ if (curr_subpart->attr & AUTO_GENERATED) {
+ ERROR("Cannot add auto-generated sub-partitions.\n");
+ return COMMAND_ERR;
+ }
+
+ if (buffer_size(&ifwi_image.subpart_buf[type])) {
+ ERROR("Image already contains sub-partition %s(%d).\n",
+ param.subpart_name, type);
+ return COMMAND_ERR;
+ }
+
+ if (param.dir_ops)
+ return ifwi_dir_add(type);
+
+ return ifwi_raw_add(type);
+}
+
+static enum ifwi_ret ifwi_delete(void)
+{
+ if (!param.subpart_name) {
+ ERROR("%s: -n option required\n", __func__);
+ return COMMAND_ERR;
+ }
+
+ int type = find_type_by_name(param.subpart_name);
+ if (type == -1)
+ return COMMAND_ERR;
+
+ const struct subpart_info *curr_subpart = &subparts[type];
+
+ if (curr_subpart->attr & AUTO_GENERATED) {
+ ERROR("Cannot delete auto-generated sub-partitions.\n");
+ return COMMAND_ERR;
+ }
+
+ if (buffer_size(&ifwi_image.subpart_buf[type]) == 0) {
+ printf("Image does not contain sub-partition %s(%d).\n",
+ param.subpart_name, type);
+ return NO_ACTION_REQUIRED;
+ }
+
+ buffer_delete(&ifwi_image.subpart_buf[type]);
+ printf("Sub-Partition %s(%d) deleted.\n", subparts[type].name, type);
+ return REPACK_REQUIRED;
+}
+
+static enum ifwi_ret ifwi_dir_extract(int type)
+{
+ if (!(subparts[type].attr & CONTAINS_DIR)) {
+ ERROR("Sub-Partition %s(%d) does not support dir ops.\n",
+ subparts[type].name, type);
+ return COMMAND_ERR;
+ }
+
+ if (!param.dentry_name) {
+ ERROR("%s: -e option required.\n", __func__);
+ return COMMAND_ERR;
+ }
+
+ struct buffer subpart_dir_buff;
+ parse_subpart_dir(&subpart_dir_buff, &ifwi_image.subpart_buf[type],
+ subparts[type].name);
+
+ uint32_t i;
+ struct subpart_dir *s = buffer_get(&subpart_dir_buff);
+
+ for (i = 0; i < s->h.num_entries; i++) {
+ if (!strncmp((char *)s->e[i].name, param.dentry_name,
+ sizeof(s->e[i].name)))
+ break;
+ }
+
+ if (i == s->h.num_entries) {
+ ERROR("Entry %s not found in subpartition for %s.\n",
+ param.dentry_name, param.subpart_name);
+ exit(-1);
+ }
+
+ struct buffer dst;
+
+ DEBUG("Splicing buffer at 0x%x size 0x%x\n", s->e[i].offset,
+ s->e[i].length);
+ buffer_splice(&dst, &ifwi_image.subpart_buf[type], s->e[i].offset,
+ s->e[i].length);
+
+ if (buffer_write_file(&dst, param.file_name))
+ return COMMAND_ERR;
+
+ printf("Sub-Partition %s(%d), entry(%s) stored in %s.\n",
+ param.subpart_name, type, param.dentry_name, param.file_name);
+
+ return NO_ACTION_REQUIRED;
+}
+
+static enum ifwi_ret ifwi_raw_extract(int type)
+{
+ if (buffer_write_file(&ifwi_image.subpart_buf[type], param.file_name))
+ return COMMAND_ERR;
+
+ printf("Sub-Partition %s(%d) stored in %s.\n", param.subpart_name, type,
+ param.file_name);
+
+ return NO_ACTION_REQUIRED;
+}
+
+static enum ifwi_ret ifwi_extract(void)
+{
+ if (!param.file_name) {
+ ERROR("%s: -f option required\n", __func__);
+ return COMMAND_ERR;
+ }
+
+ if (!param.subpart_name) {
+ ERROR("%s: -n option required\n", __func__);
+ return COMMAND_ERR;
+ }
+
+ int type = find_type_by_name(param.subpart_name);
+ if (type == -1)
+ return COMMAND_ERR;
+
+ if (type == S_BPDT_TYPE) {
+ INFO("Tool does not support raw extract for %s\n",
+ param.subpart_name);
+ return NO_ACTION_REQUIRED;
+ }
+
+ if (buffer_size(&ifwi_image.subpart_buf[type]) == 0) {
+ ERROR("Image does not contain sub-partition %s(%d).\n",
+ param.subpart_name, type);
+ return COMMAND_ERR;
+ }
+
+ INFO("Extracting sub-partition %s(%d).\n", param.subpart_name, type);
+ if (param.dir_ops)
+ return ifwi_dir_extract(type);
+
+ return ifwi_raw_extract(type);
+}
+
+static enum ifwi_ret ifwi_print(void)
+{
+ verbose += 2;
+
+ struct bpdt *b = buffer_get(&ifwi_image.bpdt);
+
+ bpdt_print_header(&b->h, "BPDT");
+ bpdt_print_entries(&b->e[0], b->h.descriptor_count, "BPDT");
+
+ b = buffer_get(&ifwi_image.subpart_buf[S_BPDT_TYPE]);
+ bpdt_print_header(&b->h, "S-BPDT");
+ bpdt_print_entries(&b->e[0], b->h.descriptor_count, "S-BPDT");
+
+ if (param.dir_ops == 0) {
+ verbose -= 2;
+ return NO_ACTION_REQUIRED;
+ }
+
+ int i;
+ struct buffer subpart_dir_buf;
+ for (i = 0; i < MAX_SUBPARTS ; i++) {
+ if (!(subparts[i].attr & CONTAINS_DIR) ||
+ (buffer_size(&ifwi_image.subpart_buf[i]) == 0))
+ continue;
+
+ parse_subpart_dir(&subpart_dir_buf, &ifwi_image.subpart_buf[i],
+ subparts[i].name);
+ buffer_delete(&subpart_dir_buf);
+ }
+
+ verbose -= 2;
+
+ return NO_ACTION_REQUIRED;
+}
+
+static enum ifwi_ret ifwi_raw_replace(int type)
+{
+ buffer_delete(&ifwi_image.subpart_buf[type]);
+ return ifwi_raw_add(type);
+}
+
+static enum ifwi_ret ifwi_dir_replace(int type)
+{
+ if (!(subparts[type].attr & CONTAINS_DIR)) {
+ ERROR("Sub-Partition %s(%d) does not support dir ops.\n",
+ subparts[type].name, type);
+ return COMMAND_ERR;
+ }
+
+ if (!param.dentry_name) {
+ ERROR("%s: -e option required.\n", __func__);
+ return COMMAND_ERR;
+ }
+
+ struct buffer subpart_dir_buf;
+ parse_subpart_dir(&subpart_dir_buf, &ifwi_image.subpart_buf[type],
+ subparts[type].name);
+
+ uint32_t i;
+ struct subpart_dir *s = buffer_get(&subpart_dir_buf);
+
+ for (i = 0; i < s->h.num_entries; i++) {
+ if (!strcmp((char *)s->e[i].name, param.dentry_name))
+ break;
+ }
+
+ if (i == s->h.num_entries) {
+ ERROR("Entry %s not found in subpartition for %s.\n",
+ param.dentry_name, param.subpart_name);
+ exit(-1);
+ }
+
+ struct buffer b;
+ if (buffer_from_file(&b, param.file_name)) {
+ ERROR("Failed to read %s\n", param.file_name);
+ exit(-1);
+ }
+
+ struct buffer dst;
+ size_t dst_size = buffer_size(&ifwi_image.subpart_buf[type] +
+ buffer_size(&b) - s->e[i].length);
+ size_t subpart_start = s->e[i].offset;
+ size_t subpart_end = s->e[i].offset + s->e[i].length;
+
+ alloc_buffer(&dst, dst_size, ifwi_image.subpart_buf[type].name);
+
+ uint8_t *src_data = buffer_get(&ifwi_image.subpart_buf[type]);
+ uint8_t *dst_data = buffer_get(&dst);
+ size_t curr_offset = 0;
+
+ /* Copy data before the sub-partition entry. */
+ memcpy(dst_data + curr_offset, src_data, subpart_start);
+ curr_offset += subpart_start;
+
+ /* Copy sub-partition entry. */
+ memcpy(dst_data + curr_offset, buffer_get(&b), buffer_size(&b));
+ curr_offset += buffer_size(&b);
+
+ /* Copy remaining data. */
+ memcpy(dst_data + curr_offset, src_data + subpart_end,
+ buffer_size(&ifwi_image.subpart_buf[type]) - subpart_end);
+
+ /* Update sub-partition buffer. */
+ int offset = s->e[i].offset;
+ buffer_delete(&ifwi_image.subpart_buf[type]);
+ ifwi_image.subpart_buf[type] = dst;
+
+ /* Update length of entry in the subpartition. */
+ s->e[i].length = buffer_size(&b);
+ buffer_delete(&b);
+
+ /* Adjust offsets of affected entries in subpartition. */
+ offset = s->e[i].offset - offset;
+ for (; i < s->h.num_entries; i++) {
+ s->e[i].offset += offset;
+ }
+
+ /* Re-calculate checksum. */
+ s->h.checksum = calc_checksum(s);
+
+ /* Convert members to litte-endian. */
+ subpart_dir_fixup_write_buffer(&subpart_dir_buf);
+
+ memcpy(dst_data, buffer_get(&subpart_dir_buf),
+ buffer_size(&subpart_dir_buf));
+
+ buffer_delete(&subpart_dir_buf);
+
+ printf("Sub-partition %s(%d) entry %s replaced from file %s.\n",
+ param.subpart_name, type, param.dentry_name, param.file_name);
+
+ return REPACK_REQUIRED;
+}
+
+static enum ifwi_ret ifwi_replace(void)
+{
+ if (!param.file_name) {
+ ERROR("%s: -f option required\n", __func__);
+ return COMMAND_ERR;
+ }
+
+ if (!param.subpart_name) {
+ ERROR("%s: -n option required\n", __func__);
+ return COMMAND_ERR;
+ }
+
+ int type = find_type_by_name(param.subpart_name);
+ if (type == -1)
+ return COMMAND_ERR;
+
+ const struct subpart_info *curr_subpart = &subparts[type];
+
+ if (curr_subpart->attr & AUTO_GENERATED) {
+ ERROR("Cannot replace auto-generated sub-partitions.\n");
+ return COMMAND_ERR;
+ }
+
+ if (buffer_size(&ifwi_image.subpart_buf[type]) == 0) {
+ ERROR("Image does not contain sub-partition %s(%d).\n",
+ param.subpart_name, type);
+ return COMMAND_ERR;
+ }
+
+ if (param.dir_ops)
+ return ifwi_dir_replace(type);
+
+ return ifwi_raw_replace(type);
+}
+
+static enum ifwi_ret ifwi_create(void)
+{
+ /*
+ * Create peels off any non-IFWI content present in the input buffer and
+ * creates output file with only the IFWI present.
+ */
+
+ if (!param.file_name) {
+ ERROR("%s: -f option required\n", __func__);
+ return COMMAND_ERR;
+ }
+
+ /* Peel off any non-IFWI prefix. */
+ buffer_seek(&ifwi_image.input_buff,
+ ifwi_image.input_ifwi_start_offset);
+ /* Peel off any non-IFWI suffix. */
+ buffer_set_size(&ifwi_image.input_buff,
+ ifwi_image.input_ifwi_end_offset -
+ ifwi_image.input_ifwi_start_offset);
+
+ /*
+ * Adjust start and end offset of IFWI now that non-IFWI prefix is gone.
+ */
+ ifwi_image.input_ifwi_end_offset -= ifwi_image.input_ifwi_start_offset;
+ ifwi_image.input_ifwi_start_offset = 0;
+
+ param.image_name = param.file_name;
+
+ return REPACK_REQUIRED;
+}
+
+struct command {
+ const char *name;
+ const char *optstring;
+ enum ifwi_ret (*function)(void);
+};
+
+static const struct command commands[] = {
+ {"add", "f:n:e:dvh?", ifwi_add},
+ {"create", "f:vh?", ifwi_create},
+ {"delete", "f:n:vh?", ifwi_delete},
+ {"extract", "f:n:e:dvh?", ifwi_extract},
+ {"print", "dh?", ifwi_print},
+ {"replace", "f:n:e:dvh?", ifwi_replace},
+};
+
+static struct option long_options[] = {
+ {"subpart_dentry", required_argument, 0, 'e'},
+ {"file", required_argument, 0, 'f'},
+ {"help", required_argument, 0, 'h'},
+ {"name", required_argument, 0, 'n'},
+ {"dir_ops", no_argument, 0, 'd'},
+ {"verbose", no_argument, 0, 'v'},
+ {NULL, 0, 0, 0 }
+};
+
+static void usage(const char *name)
+{
+ printf("ifwitool: Utility for IFWI manipulation\n\n"
+ "USAGE:\n"
+ " %s [-h]\n"
+ " %s FILE COMMAND [PARAMETERS]\n\n"
+ "COMMANDs:\n"
+ " add -f FILE -n NAME [-d -e ENTRY]\n"
+ " create -f FILE\n"
+ " delete -n NAME\n"
+ " extract -f FILE -n NAME [-d -e ENTRY]\n"
+ " print [-d]\n"
+ " replace -f FILE -n NAME [-d -e ENTRY]\n"
+ "OPTIONs:\n"
+ " -f FILE : File to read/write/create/extract\n"
+ " -d : Perform directory operation\n"
+ " -e ENTRY: Name of directory entry to operate on\n"
+ " -v : Verbose level\n"
+ " -h : Help message\n"
+ " -n NAME : Name of sub-partition to operate on\n",
+ name, name
+ );
+
+ printf("\nNAME should be one of:\n");
+ int i;
+ for (i = 0; i < MAX_SUBPARTS; i++)
+ printf("%s(%s)\n", subparts[i].name, subparts[i].readable_name);
+ printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+ if (argc < 3) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ param.image_name = argv[1];
+ char *cmd = argv[2];
+ optind += 2;
+
+ uint32_t i;
+
+ for (i = 0; i < ARRAY_SIZE(commands); i++) {
+ if (strcmp(cmd, commands[i].name) != 0)
+ continue;
+
+ int c;
+
+ while (1) {
+ int option_index;
+
+ c = getopt_long(argc, argv, commands[i].optstring,
+ long_options, &option_index);
+
+ if (c == -1)
+ break;
+
+ /* Filter out illegal long options. */
+ if (strchr(commands[i].optstring, c) == NULL) {
+ ERROR("%s: invalid option -- '%c'\n", argv[0],
+ c);
+ c = '?';
+ }
+
+ switch(c) {
+ case 'n':
+ param.subpart_name = optarg;
+ break;
+ case 'f':
+ param.file_name = optarg;
+ break;
+ case 'd':
+ param.dir_ops = 1;
+ break;
+ case 'e':
+ param.dentry_name = optarg;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'h':
+ case '?':
+ usage(argv[0]);
+ return 1;
+ default:
+ break;
+ }
+ }
+
+ if (ifwi_parse()) {
+ ERROR("%s: ifwi parsing failed\n", argv[0]);
+ return 1;
+ }
+
+ enum ifwi_ret ret = commands[i].function();
+
+ if (ret == COMMAND_ERR) {
+ ERROR("%s: failed execution\n", argv[0]);
+ return 1;
+ }
+
+ if (ret == REPACK_REQUIRED)
+ ifwi_repack();
+
+ return 0;
+ }
+
+ ERROR("%s: invalid command\n", argv[0]);
+ return 1;
+}