Aaron Durbin (adurbin@chromium.org) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/14896
-gerrit
commit 70a3497d02bd92f7b40b756b9c05f8a38ac9651c Author: Furquan Shaikh furquan@google.com Date: Thu May 19 16:12:16 2016 -0700
ifwitool: Add new tool for managing IFWI images
- Supports following operations: 1. add raw image 2. hooks to add subpart image 3. extract raw/subpart image 4. print 5. delete raw image 6. replace raw/subpart image
Change-Id: I683a0ab13cc50eb60eecca34db4a8ffefc8dccbd Signed-off-by: Furquan Shaikh furquan@google.com --- util/cbfstool/Makefile | 2 + util/cbfstool/Makefile.inc | 8 + util/cbfstool/ifwitool.c | 1601 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1611 insertions(+)
diff --git a/util/cbfstool/Makefile b/util/cbfstool/Makefile index 61a1a63..f49da3e 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 aa42f01..6c2b100 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 0000000..b3893aa --- /dev/null +++ b/util/cbfstool/ifwitool.c @@ -0,0 +1,1601 @@ +/* + * 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 "common.h" + +#define BPDT_SIGNATURE (0x000055AA) +#define KiB 1024 + +/* Parameters passed in by caller. */ +static struct param { + const char *file_name; + const char *comp_name; + const char *image_name; + bool subpart_ops; +} param; + +struct bpdt_header { + uint32_t signature; + uint16_t descriptor_count; + uint16_t bpdt_version; + uint32_t xor_redundant_block; + uint32_t ifwi_version; + uint64_t fit_tool_version; +} __attribute__((packed)); +#define BPDT_HEADER_SIZE (sizeof(struct bpdt_header)) + +struct bpdt_entry { + uint16_t type; + uint16_t flags; + uint32_t offset; + uint32_t size; +} __attribute__((packed)); +#define BPDT_ENTRY_SIZE (sizeof(struct bpdt_entry)) + +struct bpdt { + struct bpdt_header h; + struct bpdt_entry e[1]; +} __attribute__((packed)); + +#define BPDT_SIZE(c) (BPDT_HEADER_SIZE + (c) * \ + BPDT_ENTRY_SIZE) + +struct subpart_header { + uint32_t marker; + uint32_t num_entries; + uint8_t header_version; + uint8_t entry_version; + uint8_t header_length; + uint8_t checksum; + uint8_t name[4]; +} __attribute__((packed)); +#define SUBPART_HEADER_SIZE (sizeof(struct subpart_header)) +#define SUBPART_MARKER 0x44504324 +#define SUBPART_HEADER_VERSION_SUPPORTED 1 +#define SUBPART_ENTRY_VERSION_SUPPORTED 1 + +struct subpart_entry { + uint8_t name[12]; + uint32_t offset; + uint32_t length; + uint32_t rsvd; +} __attribute__((packed)); +#define SUBPART_ENTRY_SIZE (sizeof(struct subpart_entry)) + +struct subpart { + struct subpart_header h; + struct subpart_entry e[1]; +} __attribute__((packed)); +#define SUBPART_SIZE(c) (SUBPART_HEADER_SIZE + (c) * \ + SUBPART_ENTRY_SIZE) + +/* + * Attributes for various IFWI components. + * LIES_WITHIN_BPDT_4K = Component should lie within the same 4K block as BPDT. + * NON_CRITICAL_COMP = Component entry should be present in S-BPDT. + * CONTAINS_SUBPART = Component might require subpart operation (TODO(furquan)) + * AUTO_GENERATED = Component is generated by the tool. + * MANDATORY_BPDT_ENTRY = Even if component is deleted, BPDT should contain an + * entry for it with size 0 and offset 0. + */ +enum comp_attributes { + LIES_WITHIN_BPDT_4K = (1 << 0), + NON_CRITICAL_COMP = (1 << 1), + CONTAINS_SUBPART = (1 << 2), + AUTO_GENERATED = (1 << 3), + MANDATORY_BPDT_ENTRY = (1 << 4), +}; + +/* Type value for various IFWI components. */ +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_COMPS = 19, +}; + +/* + * There are two order requirements for an IFWI image: + * 1. Order in which the entries lie within the BPDT. + * 2. Order in which the components lie within the image. + * + * header_order defines #1 i.e. the order in which the entries should appear in + * the BPDT. pack_order defines #2 i.e. the order in which components appear in + * the IFWI image. pack_order controls the offset and thus components would have + * increasing offsets as we loop over pack_order. + */ +enum bpdt_entry_type bpdt_header_order[MAX_COMPS] = { + 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, +}; + +enum bpdt_entry_type bpdt_pack_order[MAX_COMPS] = { + 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 { + COMM_ERR = -1, + NO_ACTION_REQUIRED = 0, + REPACK_REQUIRED = 1, +}; + +struct cbp_ops { + enum ifwi_ret (*cbp_add)(int); +}; + +struct bpdt_comp { + const char *name; + uint32_t attr; + const char *subpart_name; + struct cbp_ops cbp_ops; +} comp[MAX_COMPS] = { + /* OEM SMIP */ + [SMIP_TYPE] = {"SMIP", CONTAINS_SUBPART, "SMIP", {NULL}}, + /* CSE RBE */ + [CSE_RBE_TYPE] = {"CSE_RBE", CONTAINS_SUBPART | MANDATORY_BPDT_ENTRY, + "RBEP", {NULL}}, + /* CSE BUP */ + [CSE_BUP_TYPE] = {"CSE_BUP", CONTAINS_SUBPART | MANDATORY_BPDT_ENTRY, + "FTPR", {NULL}}, + /* uCode */ + [UCODE_TYPE] = {"uCODE", CONTAINS_SUBPART, "UCOD", {NULL}}, + /* IBB */ + [IBB_TYPE] = {"bootblock", CONTAINS_SUBPART, "IBBP", {NULL}}, + /* S-BPDT */ + [S_BPDT_TYPE] = {"S_BPDT", AUTO_GENERATED | MANDATORY_BPDT_ENTRY, + NULL, {NULL}}, + /* OBB */ + [OBB_TYPE] = {"OBB", CONTAINS_SUBPART | NON_CRITICAL_COMP, "OBBP", + {NULL}}, + /* CSE Main */ + [CSE_MAIN_TYPE] = {"CSE_MAIN", CONTAINS_SUBPART | NON_CRITICAL_COMP, + "NFTP", {NULL}}, + /* ISH */ + [ISH_TYPE] = {"ISH", NON_CRITICAL_COMP, "ISHP", {NULL}}, + /* CSE IDLM */ + [CSE_IDLM_TYPE] = {"CSE_IDLM", CONTAINS_SUBPART | MANDATORY_BPDT_ENTRY, + "DLMP", {NULL}}, + /* IFP Override */ + [IFP_OVERRIDE_TYPE] = {"IFP_OVERRIDE", LIES_WITHIN_BPDT_4K | + MANDATORY_BPDT_ENTRY, NULL, {NULL}}, + /* Debug Tokens */ + [DEBUG_TOKENS_TYPE] = {"DEBUG_TOKENS", 0, NULL, {NULL}}, + /* UFS Phy Configuration */ + [UFS_PHY_TYPE] = {"UFS_PHY", LIES_WITHIN_BPDT_4K | MANDATORY_BPDT_ENTRY, + NULL, {NULL}}, + /* UFS GPP LUN ID */ + [UFS_GPP_TYPE] = {"UFS_GPP", LIES_WITHIN_BPDT_4K | MANDATORY_BPDT_ENTRY, + NULL, {NULL}}, + /* PMC */ + [PMC_TYPE] = {"PMC", CONTAINS_SUBPART, "PMCP", {NULL}}, + /* IUNIT */ + [IUNIT_TYPE] = {"IUNIT", NON_CRITICAL_COMP, "IUNP", {NULL}}, + /* NVM Config */ + [NVM_CONFIG_TYPE] = {"NVM_CONFIG", 0, NULL, {NULL}}, + /* UEP */ + [UEP_TYPE] = {"UEP", LIES_WITHIN_BPDT_4K | MANDATORY_BPDT_ENTRY, NULL, + {NULL}}, + /* UFS Rate B Config */ + [UFS_RATE_B_TYPE] = {"UFS_RATE_B", 0, NULL, {NULL}}, +}; + +/* + * Stores information about various components read from the input file. + * Also need to save any non-IFWI prefix and suffix in the image so that they + * can be restored when output image is created. + */ +struct ifwi_image { + struct buffer bpdt; + + struct buffer non_ifwi_prefix; + struct buffer non_ifwi_suffix; + + struct buffer subpart; + + struct buffer comp_buf[MAX_COMPS]; +} 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. */ +static void 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); + } + + *offset += size_bytes; +} + +#define READ_MEMBER(m) read_member(data, &offset, sizeof(m), &m) + +/* Convert to little endian format. */ +static void 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); + } + *offset += size_bytes; +} + +#define FIX_MEMBER(m) fix_member(&m, &offset, sizeof(m)) + +static void print_subpart(const char *name) +{ + if (verbose == 0) + return; + + size_t i; + struct subpart *s = buffer_get(&ifwi_image.subpart); + + printf("\n%-25s %-25s\n", "Subpart Header", name); + 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%s entries\n", name); + + 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\n", "Entry #", "Name", + "Type", "Flags", "Offset", "Size", "File Offset"); + + printf("==============================================================" + "==============================================================" + "================================================\n"); + + size_t i; + for (i = 0; i < count; i++) { + printf("%-25zd%-25s%-25d0x%-23.08x0x%-23x0x%-23x0x%-23zx\n", + i+1, comp[e[i].type].name, e[i].type, e[i].flags, + e[i].offset, e[i].size, + e[i].offset + buffer_size(&ifwi_image.non_ifwi_prefix)); + } + + 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; + + READ_MEMBER(h->signature); + READ_MEMBER(h->descriptor_count); + READ_MEMBER(h->bpdt_version); + READ_MEMBER(h->xor_redundant_block); + READ_MEMBER(h->ifwi_version); + READ_MEMBER(h->fit_tool_version); + + bpdt_validate_header(h, name); + bpdt_print_header(h, name); +} + +static void bpdt_read_entries(void *h, struct bpdt_entry *e, size_t count, + const char *name) +{ + size_t i, offset = 0; + void *data = (struct bpdt_header *)h + 1; + + for (i = 0; i < count; i++) { + READ_MEMBER(e[i].type); + READ_MEMBER(e[i].flags); + READ_MEMBER(e[i].offset); + READ_MEMBER(e[i].size); + } + + bpdt_print_entries(e, count, name); +} + +/* + * Given type of component, identify BPDT entry for it. + * Component 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); + assert(b); + + 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.comp_buf[S_BPDT_TYPE]); + assert(b); + + return __find_entry_by_type(&b->e[0], b->h.descriptor_count, type); +} + +/* + * Find component 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_COMPS; i++) { + if ((strlen(comp[i].name) == strlen(name)) && + (!strcmp(comp[i].name, name))) + break; + } + + if (i == MAX_COMPS) { + ERROR("Invalid component name %s.\n", name); + return -1; + } + + return i; +} + +/* + * Read the content of a component from input file and store it in + * ifwi_image.comp_buf[COMPONENT_TYPE]. + * + * Returns the maximum offset occupied by the components. + */ +static size_t read_comp_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_COMPS) { + ERROR("Invalid component type %zd.\n", type); + exit(-1); + } + + if (buffer_size(&ifwi_image.comp_buf[type])) { + ERROR("Multiple components of type %zd(%s).\n", type, + comp[type].name); + exit(-1); + } + + if (e[i].size == 0) { + INFO("Dummy component %zd(%s). Skipping.\n", type, + comp[type].name); + continue; + } + + assert((e[i].offset + e[i].size) <= size); + + if ((e[i].offset + e[i].size) > max_offset) + max_offset = e[i].offset + e[i].size; + + /* Needs special handling for S-BPDT component. */ + if (type == S_BPDT_TYPE) + continue; + + buf = &ifwi_image.comp_buf[type]; + + alloc_buffer(buf, e[i].size, comp[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 component 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.descriptor_count), 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.descriptor_count)) < size); + bpdt_read_entries((uint8_t *)data + offset, &bpdt->e[0], + bpdt->h.descriptor_count, name); + + /* Read all component content in comp_buf. */ + return read_comp_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.comp_buf[S_BPDT_TYPE], + "S-BPDT"); +} + +static uint8_t calc_checksum(struct subpart *s) +{ + size_t size = SUBPART_SIZE(s->h.num_entries); + 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(const char *name) +{ + struct subpart *s = buffer_get(&ifwi_image.subpart); + + if ((s->h.marker != SUBPART_MARKER) || + (s->h.header_version != SUBPART_HEADER_VERSION_SUPPORTED) || + (s->h.entry_version != SUBPART_ENTRY_VERSION_SUPPORTED) || + (s->h.header_length != SUBPART_HEADER_SIZE)) { + ERROR("Invalid subpart for %s.\n", name); + exit(-1); + } + + 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 parse_subpart(struct buffer *b, const char *name) +{ + struct buffer *s = &ifwi_image.subpart; + buffer_delete(s); + + struct subpart_header hdr; + size_t offset = 0; + uint8_t *data = buffer_get(b); + size_t size = buffer_size(b); + + /* Read Subpart header. */ + assert(size >= SUBPART_HEADER_SIZE); + READ_MEMBER(hdr.marker); + READ_MEMBER(hdr.num_entries); + READ_MEMBER(hdr.header_version); + READ_MEMBER(hdr.entry_version); + READ_MEMBER(hdr.header_length); + READ_MEMBER(hdr.checksum); + memcpy(hdr.name, data + offset, sizeof(hdr.name)); + offset += sizeof(hdr.name); + + assert(size > SUBPART_SIZE(hdr.num_entries)); + alloc_buffer(s, SUBPART_SIZE(hdr.num_entries), "Subpart"); + memcpy(buffer_get(s), &hdr, SUBPART_HEADER_SIZE); + + /* Read Subpart entries. */ + struct subpart *subpart = buffer_get(s); + struct subpart_entry *e = &subpart->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); + READ_MEMBER(e[i].offset); + READ_MEMBER(e[i].length); + READ_MEMBER(e[i].rsvd); + } + + validate_subpart(name); + print_subpart(name); +} + +/* Parse input image file to identify different components. */ +static int ifwi_parse(void) +{ + DEBUG("Parsing IFWI image...\n"); + const char *image_name = param.image_name; + + /* Read input file. */ + struct buffer 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; + } + + /* Read non-IFWI prefix, if present. */ + if (offset) { + INFO("File contains non-IFWI prefix (size=0x%zx)\n", offset); + alloc_buffer(&ifwi_image.non_ifwi_prefix, offset, + "Non-IFWI Prefix"); + memcpy(buffer_get(&ifwi_image.non_ifwi_prefix), data, offset); + } + + data = (uint8_t *)data + offset; + size_t ifwi_size = buffer_size(&buff) - offset; + + /* Read BPDT and components. */ + uintptr_t end_offset = alloc_bpdt_buffer(data, ifwi_size, 0, + &ifwi_image.bpdt, "BPDT"); + + /* Parse S-BPDT, if any. */ + parse_sbpdt(data, ifwi_size); + + /* + * Check if there is any non-IFWI suffix. + * Assumption: non-IFWI suffix always starts on 4k boundary. + */ + end_offset = ALIGN(end_offset, 4 * KiB); + + if (end_offset >= ifwi_size) + return 0; + + size_t size = ifwi_size - end_offset; + + INFO("File contains non-IFWI suffix (offset = 0x%zx, size=0x%zx)\n", + end_offset, size); + alloc_buffer(&ifwi_image.non_ifwi_suffix, size, + "Non-IFWI Suffix"); + memcpy(buffer_get(&ifwi_image.non_ifwi_suffix), + (uint8_t *)data + end_offset, size); + + buffer_delete(&buff); + + 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) +{ + struct bpdt *bpdt = buffer_get(b); + + /* If count has not changed, just zero out BPDT entries. */ + if (bpdt->h.descriptor_count == count) { + memset(&bpdt->e[0], 0, BPDT_ENTRY_SIZE * count); + return; + } + + /* Else, re-allocate buffer of new size and then zero out entries. */ + char *name = strdup(b->name); + assert(name); + + struct bpdt_header temp; + bpdt->h.descriptor_count = count; + memcpy(&temp, &bpdt->h, BPDT_HEADER_SIZE); + + buffer_delete(b); + alloc_buffer(b, BPDT_SIZE(count), name); + + bpdt = buffer_get(b); + + memcpy(&bpdt->h, &temp, BPDT_HEADER_SIZE); + if (count == 0) + return; + + memset(&bpdt->e[0], 0, BPDT_ENTRY_SIZE * 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_COMPS; i++) { + if (buffer_size(&ifwi_image.comp_buf[i]) == 0) { + if (comp[i].attr & MANDATORY_BPDT_ENTRY) { + bpdt_count++; + dummy_bpdt_count++; + } + continue; + } + + if (comp[i].attr & NON_CRITICAL_COMP) + 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. */ + __bpdt_reset(&ifwi_image.bpdt, bpdt_count); + + /* Update S-BPDT if required. */ + __bpdt_reset(&ifwi_image.comp_buf[S_BPDT_TYPE], sbpdt_count); +} + +/* 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.comp_buf[S_BPDT_TYPE]); + + for (i = 0; i < MAX_COMPS; i++) { + type = bpdt_header_order[i]; + size = buffer_size(&ifwi_image.comp_buf[type]); + + if ((size == 0) && !(comp[type].attr & MANDATORY_BPDT_ENTRY)) + continue; + + if (comp[type].attr & NON_CRITICAL_COMP) { + 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)++; + } +} + +/* Initialize offsets of entries using pack order. */ +static void bpdt_entries_init_pack_order(void) +{ + int i, type; + struct bpdt_entry *curr; + uint32_t alignment; + size_t curr_offset; + size_t max_offset = 0; + + curr_offset = MAX(512, buffer_size(&ifwi_image.bpdt)); + + for (i = 0; i < MAX_COMPS; i++) { + type = bpdt_pack_order[i]; + curr = find_entry_by_type(type); + + if ((curr == NULL) || (curr->size == 0)) + continue; + + if (comp[type].attr & LIES_WITHIN_BPDT_4K) + alignment = 1; + else + alignment = 4 * KiB; + + curr->offset = ALIGN(curr_offset, alignment); + curr_offset = curr->offset + curr->size; + + if (max_offset < curr_offset) + max_offset = curr_offset; + } + + /* + * Update size of S-BPDT to include size of all non-critical + * components. + * + * Assumption: S-BPDT always lies at the end of IFWI image. + */ + curr = find_entry_by_type(S_BPDT_TYPE); + assert(curr); + + curr->size = max_offset - curr->offset; +} + +/* + * Copy data from src to dst. + * curr_offset identifies the offset upto which data is written to in dst. + * write_offset identifies the offset at which data from src is to be written to + * dst. + * + * This function fills up FF in any hole present between end of last + * write(curr_offset) and offset for current src(write_offset). + * + * IMPORTANT: data_copy needs to be called in ascending order of write_offset to + * properly fill up holes with FF. + */ +static void data_copy(struct buffer *dst, size_t *curr_offset, + size_t write_offset, struct buffer *src) +{ + uint8_t *data_dst = buffer_get(dst); + uint8_t *data_src = buffer_get(src); + + assert(data_dst); + + if (buffer_size(src) == 0) + return; + + if (*curr_offset < write_offset) + memset(data_dst + *curr_offset, 0xFF, + write_offset - *curr_offset); + + memcpy(data_dst + write_offset, data_src, buffer_size(src)); + + *curr_offset = write_offset + buffer_size(src); +} + +/* + * Identify the next entry that lies immediately above or at the passed in + * offset. Component could lie either within BPDT or S-BPDT. + * + * entries = Entries in BPDT or S-BPDT + * count = Total number of entries in BPDT or S-BPDT + * offset = Offset at or above which the next component lies at + */ +static struct bpdt_entry *__get_next_entry(struct bpdt *bpdt, size_t offset) +{ + uint32_t i; + size_t lowest_offset = SIZE_MAX; + struct bpdt_entry *next = NULL; + + for (i = 0; i < bpdt->h.descriptor_count; i++) { + if (bpdt->e[i].offset == offset) { + next = &bpdt->e[i]; + break; + } + + if ((bpdt->e[i].offset > offset) && + (bpdt->e[i].offset < lowest_offset)) { + lowest_offset = bpdt->e[i].offset; + next = &bpdt->e[i]; + } + } + + return next; +} + +static struct bpdt_entry *get_next_entry(size_t offset) +{ + struct bpdt_entry *next; + next = __get_next_entry(buffer_get(&ifwi_image.bpdt), offset); + + if (next) + return next; + + return __get_next_entry(buffer_get(&ifwi_image.comp_buf[S_BPDT_TYPE]), + 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; + + FIX_MEMBER(h->signature); + FIX_MEMBER(h->descriptor_count); + FIX_MEMBER(h->bpdt_version); + FIX_MEMBER(h->xor_redundant_block); + FIX_MEMBER(h->ifwi_version); + FIX_MEMBER(h->fit_tool_version); + + uint32_t i; + for (i = 0; i < count; i++) { + FIX_MEMBER(e[i].type); + FIX_MEMBER(e[i].flags); + FIX_MEMBER(e[i].offset); + FIX_MEMBER(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 component 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 bpdt_offset = buffer_size(&ifwi_image.non_ifwi_prefix); + size_t non_ifwi_suffix_offset = s->offset + s->size; + size_t non_ifwi_suffix_size = buffer_size(&ifwi_image.non_ifwi_suffix); + + if (non_ifwi_suffix_size) + non_ifwi_suffix_offset = ALIGN(s->offset + s->size, 4096); + + size_t total_size = bpdt_offset + non_ifwi_suffix_offset + + non_ifwi_suffix_size; + + struct buffer b; + size_t curr_offset = 0; + + alloc_buffer(&b, total_size, "Final-IFWI"); + + /* Copy non-IFWI prefix, if any. */ + data_copy(&b, &curr_offset, 0, &ifwi_image.non_ifwi_prefix); + + struct buffer bpdt; + buffer_splice(&bpdt, &b, curr_offset, buffer_size(&b) - curr_offset); + + curr_offset = buffer_size(&ifwi_image.bpdt);; + + /* Copy components. */ + struct bpdt_entry *curr; + while (1) { + curr = get_next_entry(curr_offset); + if (curr == NULL) + break; + + assert(curr->size != 0); + data_copy(&bpdt, &curr_offset, curr->offset, + &ifwi_image.comp_buf[curr->type]); + } + + assert (curr_offset == (s->offset + s->size)); + + /* + * Copy non-IFWI suffix, if any. + * Assumption: Non-IFWI suffix starts on 4k boundary. + */ + if (non_ifwi_suffix_size == 0) { + buffer_delete(&b); + return; + } + + data_copy(&bpdt, &curr_offset, non_ifwi_suffix_offset, + &ifwi_image.non_ifwi_suffix); + + /* + * Convert BPDT to little-endian format and write it to output buffer. + * S-BPDT is written first and then BPDT. + */ + bpdt_write(&bpdt, s->offset, &ifwi_image.comp_buf[S_BPDT_TYPE]); + bpdt_write(&bpdt, 0, &ifwi_image.bpdt); + + if (buffer_write_file(&b, image_name)) { + ERROR("File write error\n"); + exit(-1); + } + + buffer_delete(&b); +} + +/* + * Calculate size and offset of each component 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.comp_buf[S_BPDT_TYPE]); + bpdt_print_entries(&b->e[0], b->h.descriptor_count, "S-BPDT"); + + ifwi_write(param.image_name); +} + +static enum ifwi_ret ifwi_raw_add(int type) +{ + if (buffer_from_file(&ifwi_image.comp_buf[type], param.file_name)) + return COMM_ERR; + + return REPACK_REQUIRED; +} + +static enum ifwi_ret ifwi_cbp_add(int type) +{ + if (!(comp[type].attr & CONTAINS_SUBPART) || + (comp[type].cbp_ops.cbp_add == NULL)) { + ERROR("Component %s(%d) does not support cbp ops.\n", + comp[type].name, type); + return COMM_ERR; + } + + return comp[type].cbp_ops.cbp_add(type); +} + +static enum ifwi_ret ifwi_add(void) +{ + if (!param.file_name) { + ERROR("%s: -f option required\n", __func__); + return COMM_ERR; + } + + if (!param.comp_name) { + ERROR("%s: -n option required\n", __func__); + return COMM_ERR; + } + + int type = find_type_by_name(param.comp_name); + if (type == -1) + return COMM_ERR; + + struct bpdt_comp *curr_comp = &comp[type]; + + if (curr_comp->attr & AUTO_GENERATED) { + ERROR("Cannot delete auto-generated components.\n"); + return COMM_ERR; + } + + if (buffer_size(&ifwi_image.comp_buf[type])) { + ERROR("Image already contains component %s(%d).\n", + param.comp_name, type); + return COMM_ERR; + } + + if (param.subpart_ops) + return ifwi_cbp_add(type); + + return ifwi_raw_add(type); +} + +static enum ifwi_ret ifwi_delete(void) +{ + if (!param.comp_name) { + ERROR("%s: -n option required\n", __func__); + return COMM_ERR; + } + + int type = find_type_by_name(param.comp_name); + if (type == -1) + return COMM_ERR; + + struct bpdt_comp *curr_comp = &comp[type]; + + if (curr_comp->attr & AUTO_GENERATED) { + ERROR("Cannot delete auto-generated components.\n"); + return COMM_ERR; + } + + if (buffer_size(&ifwi_image.comp_buf[type]) == 0) { + ERROR("Image does not contain component %s(%d).\n", + param.comp_name, type); + return COMM_ERR; + } + + buffer_delete(&ifwi_image.comp_buf[type]); + + INFO("Component %s(%d) deleted.\n", comp[type].name, type); + + return REPACK_REQUIRED; +} + +static enum ifwi_ret ifwi_cbp_extract(int type) +{ + if (!(comp[type].attr & CONTAINS_SUBPART)) { + ERROR("Component %s(%d) does not support cbp ops.\n", + comp[type].name, type); + return COMM_ERR; + } + + parse_subpart(&ifwi_image.comp_buf[type], comp[type].name); + + uint32_t i; + struct subpart *s = buffer_get(&ifwi_image.subpart); + + for (i = 0; i < s->h.num_entries; i++) { + if (!strcmp((char *)s->e[i].name, param.file_name)) + break; + } + + if (i == s->h.num_entries) { + ERROR("File %s not found in subpartition for %s.\n", + param.file_name, param.comp_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.comp_buf[type], s->e[i].offset, + s->e[i].length); + + if (buffer_write_file(&dst, param.file_name)) + return COMM_ERR; + + INFO("Component %s(%d) stored in %s.\n", param.comp_name, type, + param.file_name); + + return NO_ACTION_REQUIRED; +} + +static enum ifwi_ret ifwi_raw_extract(int type) +{ + if (buffer_write_file(&ifwi_image.comp_buf[type], param.file_name)) + return COMM_ERR; + + INFO("Component %s(%d) stored in %s.\n", param.comp_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 COMM_ERR; + } + + if (!param.comp_name) { + ERROR("%s: -n option required\n", __func__); + return COMM_ERR; + } + + int type = find_type_by_name(param.comp_name); + if (type == -1) + return COMM_ERR; + + if (type == S_BPDT_TYPE) { + ERROR("Nothing to extract.\n"); + return NO_ACTION_REQUIRED; + } + + if (buffer_size(&ifwi_image.comp_buf[type]) == 0) { + ERROR("Image does not contain component %s(%d).\n", + param.comp_name, type); + return COMM_ERR; + } + + INFO("Extracting component %s(%d).\n", param.comp_name, type); + if (param.subpart_ops) + return ifwi_cbp_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.comp_buf[S_BPDT_TYPE]); + bpdt_print_header(&b->h, "S-BPDT"); + bpdt_print_entries(&b->e[0], b->h.descriptor_count, "S-BPDT"); + + int i; + for (i = 0; i < MAX_COMPS ; i++) { + if (!(comp[i].attr & CONTAINS_SUBPART) || + (buffer_size(&ifwi_image.comp_buf[i]) == 0)) + continue; + + parse_subpart(&ifwi_image.comp_buf[i], comp[i].name); + } + + verbose -= 2; + + return NO_ACTION_REQUIRED; +} + +static enum ifwi_ret ifwi_raw_replace(int type) +{ + if (buffer_size(&ifwi_image.comp_buf[type]) == 0) { + INFO("Image does not contain component %s(%d).\n", + param.comp_name, type); + } else + buffer_delete(&ifwi_image.comp_buf[type]); + + return ifwi_raw_add(type); +} + +static void subpart_fixup_write_buffer(struct buffer *buf) +{ + struct subpart *s = buffer_get(buf); + struct subpart_header *h = &s->h; + struct subpart_entry *e = &s->e[0]; + + size_t count = h->num_entries; + size_t offset = 0; + + FIX_MEMBER(h->marker); + FIX_MEMBER(h->num_entries); + FIX_MEMBER(h->header_version); + FIX_MEMBER(h->entry_version); + FIX_MEMBER(h->header_length); + FIX_MEMBER(h->checksum); + offset += sizeof(h->name); + + uint32_t i; + for (i = 0; i < count; i++) { + offset += sizeof(e[i].name); + FIX_MEMBER(e[i].offset); + FIX_MEMBER(e[i].length); + FIX_MEMBER(e[i].rsvd); + } +} + +static enum ifwi_ret ifwi_cbp_replace(int type) +{ + if (buffer_size(&ifwi_image.comp_buf[type]) == 0) { + ERROR("Image does not contain component %s(%d).\n", + param.comp_name, type); + return COMM_ERR; + } + + if (!(comp[type].attr & CONTAINS_SUBPART)) { + ERROR("Component %s(%d) does not support cbp ops.\n", + comp[type].name, type); + return COMM_ERR; + } + + parse_subpart(&ifwi_image.comp_buf[type], comp[type].name); + + uint32_t i; + struct subpart *s = buffer_get(&ifwi_image.subpart); + + for (i = 0; i < s->h.num_entries; i++) { + if (!strcmp((char *)s->e[i].name, param.file_name)) + break; + } + + if (i == s->h.num_entries) { + ERROR("File %s not found in subpartition for %s.\n", + param.file_name, param.comp_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.comp_buf[type] + + buffer_size(&b) - s->e[i].length); + size_t comp_start = s->e[i].offset; + size_t comp_end = s->e[i].offset + s->e[i].length; + + alloc_buffer(&dst, dst_size, ifwi_image.comp_buf[type].name); + + uint8_t *src_data = buffer_get(&ifwi_image.comp_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, comp_start); + curr_offset += comp_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 + comp_end, + buffer_size(&ifwi_image.comp_buf[type]) - comp_end); + + /* Update component buffer. */ + int offset = s->e[i].offset; + buffer_delete(&ifwi_image.comp_buf[type]); + ifwi_image.comp_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_fixup_write_buffer(&ifwi_image.subpart); + + memcpy(dst_data, buffer_get(&ifwi_image.subpart), + buffer_size(&ifwi_image.subpart)); + + buffer_delete(&ifwi_image.subpart); + + return REPACK_REQUIRED; +} + +static enum ifwi_ret ifwi_replace(void) +{ + if (!param.file_name) { + ERROR("%s: -f option required\n", __func__); + return COMM_ERR; + } + + if (!param.comp_name) { + ERROR("%s: -n option required\n", __func__); + return COMM_ERR; + } + + int type = find_type_by_name(param.comp_name); + if (type == -1) + return COMM_ERR; + + struct bpdt_comp *curr_comp = &comp[type]; + + if (curr_comp->attr & AUTO_GENERATED) { + ERROR("Cannot replace auto-generated components.\n"); + return COMM_ERR; + } + + if (param.subpart_ops) + return ifwi_cbp_replace(type); + + return ifwi_raw_replace(type); +} + +static enum ifwi_ret ifwi_create(void) +{ + if (!param.file_name) { + ERROR("%s: -f option required\n", __func__); + return COMM_ERR; + } + + buffer_delete(&ifwi_image.non_ifwi_prefix); + buffer_delete(&ifwi_image.non_ifwi_suffix); + + 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:svh?", ifwi_add}, + {"create", "f:vh?", ifwi_create}, + {"delete", "f:n:vh?", ifwi_delete}, + {"extract", "f:n:svh?", ifwi_extract}, + {"print", "h?", ifwi_print}, + {"replace", "f:n:svh?", ifwi_replace}, +}; + +static struct option long_options[] = { + {"file", required_argument, 0, 'f'}, + {"help", required_argument, 0, 'h'}, + {"name", required_argument, 0, 'n'}, + {"subpart_ops", no_argument, 0, 's'}, + {"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\n" + " create -f FILE\n" + " delete -n NAME\n" + " extract -f FILE -n NAME\n" + " print\n" + " replace -f FILE -n NAME\n", + name, name + ); +} + +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.comp_name = optarg; + break; + case 'f': + param.file_name = optarg; + break; + case 's': + param.subpart_ops = 1; + 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 == COMM_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; +}