util/cse_serger: Add a new tool for stitching CSE components
This change adds a new tool `cse_serger` which can be used to print, dump and stitch together different components for the CSE region. BUG=b:189177186 Change-Id: I90dd809b47fd16afdc80e66431312721082496aa Signed-off-by: Furquan Shaikh <furquan@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/55503 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
This commit is contained in:
parent
796aeeba96
commit
d7fb6a90e1
|
@ -60,7 +60,7 @@ COREBOOT_EXPORTS += CCACHE_EXTRAFILES
|
|||
#######################################################################
|
||||
# root rule to resolve if in build mode (ie. configuration exists)
|
||||
real-target: $(obj)/config.h coreboot files_added
|
||||
coreboot: $(obj)/coreboot.rom $(obj)/cbfstool $(obj)/rmodtool $(obj)/ifwitool $(obj)/cse_fpt
|
||||
coreboot: $(obj)/coreboot.rom $(obj)/cbfstool $(obj)/rmodtool $(obj)/ifwitool $(obj)/cse_fpt $(obj)/cse_serger
|
||||
|
||||
# This target can be used in site local to run scripts or additional
|
||||
# targets after the build completes by creating a Makefile.inc in the
|
||||
|
@ -545,6 +545,7 @@ IFWITOOL:=$(objutil)/cbfstool/ifwitool
|
|||
IFITTOOL:=$(objutil)/cbfstool/ifittool
|
||||
AMDCOMPRESS:=$(objutil)/cbfstool/amdcompress
|
||||
CSE_FPT:=$(objutil)/cbfstool/cse_fpt
|
||||
CSE_SERGER:=$(objutil)/cbfstool/cse_serger
|
||||
|
||||
$(obj)/cbfstool: $(CBFSTOOL)
|
||||
cp $< $@
|
||||
|
@ -567,6 +568,9 @@ $(obj)/amdcompress: $(AMDCOMPRESS)
|
|||
$(obj)/cse_fpt: $(CSE_FPT)
|
||||
cp $< $@
|
||||
|
||||
$(obj)/cse_serger: $(CSE_SERGER)
|
||||
cp $< $@
|
||||
|
||||
_WINCHECK=$(shell uname -o 2> /dev/null)
|
||||
STACK=
|
||||
ifeq ($(_WINCHECK),Msys)
|
||||
|
@ -693,7 +697,7 @@ install-git-commit-clangfmt:
|
|||
include util/crossgcc/Makefile.inc
|
||||
|
||||
.PHONY: tools
|
||||
tools: $(objutil)/kconfig/conf $(objutil)/kconfig/toada $(CBFSTOOL) $(objutil)/cbfstool/cbfs-compression-tool $(FMAPTOOL) $(RMODTOOL) $(IFWITOOL) $(objutil)/nvramtool/nvramtool $(objutil)/sconfig/sconfig $(IFDTOOL) $(CBOOTIMAGE) $(AMDFWTOOL) $(AMDCOMPRESS) $(FUTILITY) $(BINCFG) $(IFITTOOL) $(objutil)/supermicro/smcbiosinfo $(CSE_FPT)
|
||||
tools: $(objutil)/kconfig/conf $(objutil)/kconfig/toada $(CBFSTOOL) $(objutil)/cbfstool/cbfs-compression-tool $(FMAPTOOL) $(RMODTOOL) $(IFWITOOL) $(objutil)/nvramtool/nvramtool $(objutil)/sconfig/sconfig $(IFDTOOL) $(CBOOTIMAGE) $(AMDFWTOOL) $(AMDCOMPRESS) $(FUTILITY) $(BINCFG) $(IFITTOOL) $(objutil)/supermicro/smcbiosinfo $(CSE_FPT) $(CSE_SERGER)
|
||||
|
||||
###########################################################################
|
||||
# Common recipes for all stages
|
||||
|
|
|
@ -13,7 +13,7 @@ VBOOT_SOURCE ?= $(top)/3rdparty/vboot
|
|||
VBOOT_HOST_BUILD ?= $(abspath $(objutil)/vboot_lib)
|
||||
|
||||
.PHONY: all
|
||||
all: cbfstool ifittool fmaptool rmodtool ifwitool cbfs-compression-tool elogtool cse_fpt
|
||||
all: cbfstool ifittool fmaptool rmodtool ifwitool cbfs-compression-tool elogtool cse_fpt cse_serger
|
||||
|
||||
cbfstool: $(objutil)/cbfstool/cbfstool
|
||||
|
||||
|
@ -31,7 +31,9 @@ elogtool: $(objutil)/cbfstool/elogtool
|
|||
|
||||
cse_fpt: $(objutil)/cbfstool/cse_fpt
|
||||
|
||||
.PHONY: clean cbfstool ifittool fmaptool rmodtool ifwitool cbfs-compression-tool elogtool cse_fpt
|
||||
cse_serger: $(objutil)/cbfstool/cse_serger
|
||||
|
||||
.PHONY: clean cbfstool ifittool fmaptool rmodtool ifwitool cbfs-compression-tool elogtool cse_fpt cse_serger
|
||||
clean:
|
||||
$(RM) fmd_parser.c fmd_parser.h fmd_scanner.c fmd_scanner.h
|
||||
$(RM) $(objutil)/cbfstool/cbfstool $(cbfsobj)
|
||||
|
@ -42,6 +44,7 @@ clean:
|
|||
$(RM) $(objutil)/cbfstool/cbfs-compression-tool $(cbfscompobj)
|
||||
$(RM) $(objutil)/cbfstool/elogtool $(elogobj)
|
||||
$(RM) $(objutil)/cbfstool/cse_fpt $(cse_fpt_obj)
|
||||
$(RM) $(objutil)/cbfstool/cse_serger $(cse_serger_obj)
|
||||
$(RM) -r $(VBOOT_HOST_BUILD)
|
||||
|
||||
linux_trampoline.c: linux_trampoline.S
|
||||
|
@ -65,6 +68,7 @@ install: all
|
|||
$(INSTALL) cbfs-compression-tool $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) elogtool $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) cse_fpt $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) cse_serger $(DESTDIR)$(BINDIR)
|
||||
|
||||
distclean: clean
|
||||
|
||||
|
@ -79,6 +83,7 @@ help:
|
|||
@echo " cbfs-compression-tool - benchmark compression algorithms"
|
||||
@echo " elogtool - Display ELOG events"
|
||||
@echo " cse_fpt - Manage Intel CSE Flash Partition Table (FPT)"
|
||||
@echo " cse_serger - Stitch Intel CSE components"
|
||||
|
||||
ifneq ($(V),1)
|
||||
.SILENT:
|
||||
|
|
|
@ -102,6 +102,12 @@ cse_fpt_obj += cse_fpt.o
|
|||
cse_fpt_obj += common.o
|
||||
cse_fpt_obj += $(foreach var, $(fpt_formats_obj), $(var))
|
||||
|
||||
include $(top)/util/cbfstool/bpdt_formats/Makefile.inc
|
||||
cse_serger_obj :=
|
||||
cse_serger_obj += cse_serger.o
|
||||
cse_serger_obj += common.o
|
||||
cse_serger_obj += $(foreach var, $(bpdt_formats_obj), $(var))
|
||||
|
||||
TOOLCFLAGS ?= -Werror -Wall -Wextra -Wshadow
|
||||
TOOLCFLAGS += -Wcast-qual -Wmissing-prototypes -Wredundant-decls -Wshadow
|
||||
TOOLCFLAGS += -Wstrict-prototypes -Wwrite-strings
|
||||
|
@ -188,6 +194,10 @@ $(objutil)/cbfstool/%.o: $(top)/util/cbfstool/fpt_formats/%.c
|
|||
printf " HOSTCC $(subst $(objutil)/,,$(@))\n"
|
||||
$(HOSTCC) $(TOOLCPPFLAGS) $(TOOLCFLAGS) $(HOSTCFLAGS) -c -o $@ $<
|
||||
|
||||
$(objutil)/cbfstool/%.o: $(top)/util/cbfstool/bpdt_formats/%.c
|
||||
printf " HOSTCC $(subst $(objutil)/,,$(@))\n"
|
||||
$(HOSTCC) $(TOOLCPPFLAGS) $(TOOLCFLAGS) $(HOSTCFLAGS) -c -o $@ $<
|
||||
|
||||
$(objutil)/cbfstool/cbfstool: $(addprefix $(objutil)/cbfstool/,$(cbfsobj)) $(VBOOT_HOSTLIB)
|
||||
printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n"
|
||||
$(HOSTCC) -v $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(cbfsobj)) $(VBOOT_HOSTLIB)
|
||||
|
@ -224,6 +234,10 @@ $(objutil)/cbfstool/cse_fpt: $(addprefix $(objutil)/cbfstool/,$(cse_fpt_obj))
|
|||
printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n"
|
||||
$(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(cse_fpt_obj))
|
||||
|
||||
$(objutil)/cbfstool/cse_serger: $(addprefix $(objutil)/cbfstool/,$(cse_serger_obj))
|
||||
printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n"
|
||||
$(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(cse_serger_obj))
|
||||
|
||||
# Yacc source is superset of header
|
||||
$(objutil)/cbfstool/fmd.o: TOOLCFLAGS += -Wno-redundant-decls
|
||||
$(objutil)/cbfstool/fmd_parser.o: TOOLCFLAGS += -Wno-redundant-decls
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
bpdt_formats_obj += bpdt_1_6.o
|
||||
bpdt_formats_obj += bpdt_1_7.o
|
||||
|
||||
bpdt_formats_obj += subpart_hdr_1.o
|
||||
bpdt_formats_obj += subpart_hdr_2.o
|
||||
|
||||
bpdt_formats_obj += subpart_entry_1.o
|
|
@ -0,0 +1,272 @@
|
|||
/* BPDT version 1.6 support */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "cse_serger.h"
|
||||
|
||||
struct bpdt_header {
|
||||
uint32_t signature; /* BPDT_SIGNATURE */
|
||||
uint16_t descriptor_count;
|
||||
uint16_t version; /* Layout 1.6 = 1 */
|
||||
uint16_t reserved;
|
||||
uint8_t whole_checksum;
|
||||
uint8_t rom_checksum;
|
||||
uint32_t ifwi_version;
|
||||
struct {
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint16_t build;
|
||||
uint16_t hotfix;
|
||||
} fit_tool_version;
|
||||
} __packed;
|
||||
|
||||
struct cse_layout {
|
||||
uint8_t rom_bypass[16];
|
||||
uint32_t data_offset;
|
||||
uint32_t data_size;
|
||||
uint32_t bp1_offset;
|
||||
uint32_t bp1_size;
|
||||
uint32_t bp2_offset;
|
||||
uint32_t bp2_size;
|
||||
uint32_t bp3_offset;
|
||||
uint32_t bp3_size;
|
||||
uint32_t reserved[16];
|
||||
uint8_t checksum;
|
||||
} __packed;
|
||||
|
||||
static bool match_version(const struct buffer *buff)
|
||||
{
|
||||
const uint8_t *data = buffer_get(buff);
|
||||
const uint32_t sig = read_le32(data);
|
||||
const uint16_t version = read_at_le16(data, offsetof(struct bpdt_header, version));
|
||||
|
||||
if (sig != BPDT_SIGNATURE) {
|
||||
ERROR("Invalid BPDT signature(0x%x)!\n", sig);
|
||||
return false;
|
||||
}
|
||||
|
||||
return version == BPDT_VERSION_1_6;
|
||||
}
|
||||
|
||||
static bpdt_hdr_ptr create_bpdt_hdr(void)
|
||||
{
|
||||
struct bpdt_header *h = malloc(sizeof(*h));
|
||||
|
||||
if (!h)
|
||||
return NULL;
|
||||
|
||||
h->signature = BPDT_SIGNATURE;
|
||||
h->descriptor_count = 0;
|
||||
h->version = BPDT_VERSION_1_6;
|
||||
h->reserved = 0;
|
||||
/* TODO(b/202549343): Need to calculate checksum */
|
||||
h->whole_checksum = 0;
|
||||
h->rom_checksum = 0;
|
||||
h->ifwi_version = 0;
|
||||
h->fit_tool_version.major = 0;
|
||||
h->fit_tool_version.minor = 0;
|
||||
h->fit_tool_version.build = 0;
|
||||
h->fit_tool_version.hotfix = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_bpdt_hdr(const bpdt_hdr_ptr ptr)
|
||||
{
|
||||
struct bpdt_header *h = ptr;
|
||||
|
||||
printf(" * BPDT header\n");
|
||||
printf("%-25s 0x%-23.8x\n", "Signature", h->signature);
|
||||
printf("%-25s %-25d\n", "Descriptor count", h->descriptor_count);
|
||||
printf("%-25s %d (Layout 1.6)\n", "BPDT Version", h->version);
|
||||
printf("%-25s 0x%-23x\n", "Reserved", h->reserved);
|
||||
printf("%-25s 0x%-23x\n", "Whole Checksum", h->whole_checksum);
|
||||
printf("%-25s 0x%-23x\n", "ROM Checksum", h->rom_checksum);
|
||||
printf("%-25s 0x%-23x\n", "IFWI Version", h->ifwi_version);
|
||||
printf("%-25s %d.%d.%d.%d(%.2x.%.2x.%.2x.%.2x)\n", "FIT Tool Version",
|
||||
h->fit_tool_version.major, h->fit_tool_version.minor,
|
||||
h->fit_tool_version.build, h->fit_tool_version.hotfix,
|
||||
h->fit_tool_version.major, h->fit_tool_version.minor,
|
||||
h->fit_tool_version.build, h->fit_tool_version.hotfix);
|
||||
}
|
||||
|
||||
static bpdt_hdr_ptr read_bpdt_hdr(struct buffer *buff)
|
||||
{
|
||||
struct bpdt_header *h = malloc(sizeof(*h));
|
||||
|
||||
if (!h)
|
||||
return NULL;
|
||||
|
||||
READ_MEMBER(buff, h->signature);
|
||||
READ_MEMBER(buff, h->descriptor_count);
|
||||
READ_MEMBER(buff, h->version);
|
||||
READ_MEMBER(buff, h->reserved);
|
||||
READ_MEMBER(buff, h->whole_checksum);
|
||||
READ_MEMBER(buff, h->rom_checksum);
|
||||
READ_MEMBER(buff, h->ifwi_version);
|
||||
READ_MEMBER(buff, h->fit_tool_version);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static int write_bpdt_hdr(struct buffer *buff, const bpdt_hdr_ptr ptr)
|
||||
{
|
||||
struct bpdt_header *h = ptr;
|
||||
|
||||
if (buffer_size(buff) < sizeof(struct bpdt_header)) {
|
||||
ERROR("Not enough size in buffer for BPDT header!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
WRITE_MEMBER(buff, h->signature);
|
||||
WRITE_MEMBER(buff, h->descriptor_count);
|
||||
WRITE_MEMBER(buff, h->version);
|
||||
WRITE_MEMBER(buff, h->reserved);
|
||||
WRITE_MEMBER(buff, h->whole_checksum);
|
||||
WRITE_MEMBER(buff, h->rom_checksum);
|
||||
WRITE_MEMBER(buff, h->ifwi_version);
|
||||
WRITE_MEMBER(buff, h->fit_tool_version);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t get_bpdt_entry_count(const bpdt_hdr_ptr ptr)
|
||||
{
|
||||
return ((const struct bpdt_header *)ptr)->descriptor_count;
|
||||
}
|
||||
|
||||
static void inc_bpdt_entry_count(bpdt_hdr_ptr ptr)
|
||||
{
|
||||
struct bpdt_header *h = ptr;
|
||||
h->descriptor_count++;
|
||||
}
|
||||
|
||||
static cse_layout_ptr create_cse_layout(const struct cse_layout_regions *r)
|
||||
{
|
||||
struct cse_layout *l = malloc(sizeof(*l));
|
||||
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
l->data_offset = r->data_partition.offset;
|
||||
l->data_size = r->data_partition.size;
|
||||
l->bp1_offset = r->bp1.offset;
|
||||
l->bp1_size = r->bp1.size;
|
||||
l->bp2_offset = r->bp2.offset;
|
||||
l->bp2_size = r->bp2.size;
|
||||
l->bp3_offset = r->bp3.offset;
|
||||
l->bp3_size = r->bp3.size;
|
||||
l->checksum = 0; /* unused */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_cse_layout(const cse_layout_ptr ptr)
|
||||
{
|
||||
struct cse_layout *l = ptr;
|
||||
|
||||
printf(" * CSE Layout\n\n");
|
||||
printf("ROM Bypass: ");
|
||||
for (size_t i = 0; i < sizeof(l->rom_bypass); i++)
|
||||
printf("0x%x ", l->rom_bypass[i]);
|
||||
printf("\n");
|
||||
printf("Data partition offset: 0x%x\n", l->data_offset);
|
||||
printf("Data partition size: 0x%x\n", l->data_size);
|
||||
printf("BP1 offset: 0x%x\n", l->bp1_offset);
|
||||
printf("BP1 size: 0x%x\n", l->bp1_size);
|
||||
printf("BP2 offset: 0x%x\n", l->bp2_offset);
|
||||
printf("BP2 size: 0x%x\n", l->bp2_size);
|
||||
printf("BP3 offset: 0x%x\n", l->bp3_offset);
|
||||
printf("BP3 size: 0x%x\n", l->bp3_size);
|
||||
printf("Checksum: 0x%x\n", l->checksum);
|
||||
}
|
||||
|
||||
static cse_layout_ptr read_cse_layout(struct buffer *buff)
|
||||
{
|
||||
struct cse_layout *l = malloc(sizeof(*l));
|
||||
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
READ_MEMBER(buff, l->rom_bypass);
|
||||
READ_MEMBER(buff, l->data_offset);
|
||||
READ_MEMBER(buff, l->data_size);
|
||||
READ_MEMBER(buff, l->bp1_offset);
|
||||
READ_MEMBER(buff, l->bp1_size);
|
||||
READ_MEMBER(buff, l->bp2_offset);
|
||||
READ_MEMBER(buff, l->bp2_size);
|
||||
READ_MEMBER(buff, l->bp3_offset);
|
||||
READ_MEMBER(buff, l->bp3_size);
|
||||
READ_MEMBER(buff, l->reserved);
|
||||
READ_MEMBER(buff, l->checksum);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
static int write_cse_layout(struct buffer *buff, const cse_layout_ptr ptr)
|
||||
{
|
||||
struct cse_layout *l = ptr;
|
||||
|
||||
if (buffer_size(buff) < sizeof(struct cse_layout)) {
|
||||
ERROR("Not enough size in buffer for CSE layout!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
WRITE_MEMBER(buff, l->rom_bypass);
|
||||
WRITE_MEMBER(buff, l->data_offset);
|
||||
WRITE_MEMBER(buff, l->data_size);
|
||||
WRITE_MEMBER(buff, l->bp1_offset);
|
||||
WRITE_MEMBER(buff, l->bp1_size);
|
||||
WRITE_MEMBER(buff, l->bp2_offset);
|
||||
WRITE_MEMBER(buff, l->bp2_size);
|
||||
WRITE_MEMBER(buff, l->bp3_offset);
|
||||
WRITE_MEMBER(buff, l->bp3_size);
|
||||
WRITE_MEMBER(buff, l->reserved);
|
||||
WRITE_MEMBER(buff, l->checksum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void update_checksum(bpdt_hdr_ptr ptr, struct bpdt_entry *e)
|
||||
{
|
||||
(void)ptr;
|
||||
(void)e;
|
||||
|
||||
/* TODO(b/202549343) */
|
||||
ERROR("Update checksum is not supported for 1.6!\n");
|
||||
}
|
||||
|
||||
static bool validate_checksum(bpdt_hdr_ptr ptr, struct bpdt_entry *e)
|
||||
{
|
||||
(void)e;
|
||||
(void)ptr;
|
||||
|
||||
/* TODO(b/202549343) */
|
||||
ERROR("Validate checksum is not supported for 1.6!\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const struct bpdt_ops bpdt_1_6_ops = {
|
||||
.match_version = match_version,
|
||||
|
||||
.create_hdr = create_bpdt_hdr,
|
||||
.print_hdr = print_bpdt_hdr,
|
||||
.read_hdr = read_bpdt_hdr,
|
||||
.write_hdr = write_bpdt_hdr,
|
||||
|
||||
.get_entry_count = get_bpdt_entry_count,
|
||||
.inc_entry_count = inc_bpdt_entry_count,
|
||||
|
||||
.create_layout = create_cse_layout,
|
||||
.print_layout = print_cse_layout,
|
||||
.read_layout = read_cse_layout,
|
||||
.write_layout = write_cse_layout,
|
||||
|
||||
.update_checksum = update_checksum,
|
||||
.validate_checksum = validate_checksum,
|
||||
|
||||
.subpart_hdr_version = SUBPART_HDR_VERSION_1,
|
||||
.subpart_entry_version = SUBPART_ENTRY_VERSION_1,
|
||||
};
|
|
@ -0,0 +1,367 @@
|
|||
/* BPDT version 1.7 support */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "cse_serger.h"
|
||||
|
||||
enum bpdt_flags {
|
||||
BPDT_FLAGS_REDUNDANCY_SUPPORTED = 1 << 0,
|
||||
};
|
||||
|
||||
struct bpdt_header {
|
||||
uint32_t signature; /* BPDT_SIGNATURE */
|
||||
uint16_t descriptor_count;
|
||||
uint8_t version; /* Layout 1.7 = 2 */
|
||||
uint8_t flags; /* See enum bpdt_flags */
|
||||
uint32_t checksum;
|
||||
uint32_t ifwi_version;
|
||||
struct {
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint16_t build;
|
||||
uint16_t hotfix;
|
||||
} fit_tool_version;
|
||||
} __packed;
|
||||
|
||||
struct cse_layout {
|
||||
uint8_t rom_bypass[16];
|
||||
uint16_t size;
|
||||
uint16_t redundancy;
|
||||
uint32_t checksum;
|
||||
uint32_t data_offset;
|
||||
uint32_t data_size;
|
||||
uint32_t bp1_offset;
|
||||
uint32_t bp1_size;
|
||||
uint32_t bp2_offset;
|
||||
uint32_t bp2_size;
|
||||
uint32_t bp3_offset;
|
||||
uint32_t bp3_size;
|
||||
uint32_t bp4_offset;
|
||||
uint32_t bp4_size;
|
||||
uint32_t bp5_offset;
|
||||
uint32_t bp5_size;
|
||||
uint32_t temp_base_addr;
|
||||
uint32_t temp_base_size;
|
||||
uint32_t flog_offset;
|
||||
uint32_t flog_size;
|
||||
} __packed;
|
||||
|
||||
static bool match_version(const struct buffer *buff)
|
||||
{
|
||||
const uint8_t *data = buffer_get(buff);
|
||||
const uint32_t sig = read_le32(data);
|
||||
const uint8_t version = read_at_le8(data, offsetof(struct bpdt_header, version));
|
||||
|
||||
if (sig != BPDT_SIGNATURE) {
|
||||
ERROR("Invalid BPDT signature(0x%x)!\n", sig);
|
||||
return false;
|
||||
}
|
||||
|
||||
return version == BPDT_VERSION_1_7;
|
||||
}
|
||||
|
||||
static bpdt_hdr_ptr create_bpdt_hdr(void)
|
||||
{
|
||||
struct bpdt_header *h = calloc(1, sizeof(*h));
|
||||
|
||||
if (!h)
|
||||
return NULL;
|
||||
|
||||
h->signature = BPDT_SIGNATURE;
|
||||
h->descriptor_count = 0;
|
||||
h->version = BPDT_VERSION_1_7;
|
||||
h->flags = 0;
|
||||
h->checksum = 0;
|
||||
h->ifwi_version = 0;
|
||||
h->fit_tool_version.major = 0;
|
||||
h->fit_tool_version.minor = 0;
|
||||
h->fit_tool_version.build = 0;
|
||||
h->fit_tool_version.hotfix = 0;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static void print_bpdt_hdr(const bpdt_hdr_ptr ptr)
|
||||
{
|
||||
struct bpdt_header *h = ptr;
|
||||
|
||||
printf(" * BPDT header\n");
|
||||
printf("%-25s 0x%-23.8x\n", "Signature", h->signature);
|
||||
printf("%-25s %-25d\n", "Descriptor count", h->descriptor_count);
|
||||
printf("%-25s %d (Layout 1.7)\n", "BPDT Version", h->version);
|
||||
printf("%-25s 0x%-23x\n", "Flags", h->flags);
|
||||
printf("%-25s 0x%-23x\n", "Checksum", h->checksum);
|
||||
printf("%-25s 0x%-23x\n", "IFWI Version", h->ifwi_version);
|
||||
printf("%-25s %d.%d.%d.%d(%.2x.%.2x.%.2x.%.2x)\n", "FIT Tool Version",
|
||||
h->fit_tool_version.major, h->fit_tool_version.minor,
|
||||
h->fit_tool_version.build, h->fit_tool_version.hotfix,
|
||||
h->fit_tool_version.major, h->fit_tool_version.minor,
|
||||
h->fit_tool_version.build, h->fit_tool_version.hotfix);
|
||||
}
|
||||
|
||||
static bpdt_hdr_ptr read_bpdt_hdr(struct buffer *buff)
|
||||
{
|
||||
struct bpdt_header *h = calloc(1, sizeof(*h));
|
||||
|
||||
if (!h)
|
||||
return NULL;
|
||||
|
||||
READ_MEMBER(buff, h->signature);
|
||||
READ_MEMBER(buff, h->descriptor_count);
|
||||
READ_MEMBER(buff, h->version);
|
||||
READ_MEMBER(buff, h->flags);
|
||||
READ_MEMBER(buff, h->checksum);
|
||||
READ_MEMBER(buff, h->ifwi_version);
|
||||
READ_MEMBER(buff, h->fit_tool_version);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static int write_bpdt_hdr(struct buffer *buff, const bpdt_hdr_ptr ptr)
|
||||
{
|
||||
struct bpdt_header *h = ptr;
|
||||
|
||||
if (buffer_size(buff) < sizeof(struct bpdt_header)) {
|
||||
ERROR("Not enough size in buffer for BPDT header!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
WRITE_MEMBER(buff, h->signature);
|
||||
WRITE_MEMBER(buff, h->descriptor_count);
|
||||
WRITE_MEMBER(buff, h->version);
|
||||
WRITE_MEMBER(buff, h->flags);
|
||||
WRITE_MEMBER(buff, h->checksum);
|
||||
WRITE_MEMBER(buff, h->ifwi_version);
|
||||
WRITE_MEMBER(buff, h->fit_tool_version);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t get_bpdt_entry_count(const bpdt_hdr_ptr ptr)
|
||||
{
|
||||
return ((const struct bpdt_header *)ptr)->descriptor_count;
|
||||
}
|
||||
|
||||
static void inc_bpdt_entry_count(bpdt_hdr_ptr ptr)
|
||||
{
|
||||
struct bpdt_header *h = ptr;
|
||||
h->descriptor_count++;
|
||||
}
|
||||
|
||||
static uint32_t crc32(uint32_t seed, const uint8_t *data, size_t len)
|
||||
{
|
||||
uint32_t crc = seed;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
crc ^= *data++;
|
||||
|
||||
for (size_t b = 0; b < 8; b++) {
|
||||
if (crc & 1)
|
||||
crc = (crc >> 1) ^ 0xedb88320;
|
||||
else
|
||||
crc = crc >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate checksum by:
|
||||
* a. stashing l->checksum in curr_checksum and setting l->checksum to 0
|
||||
* b. calculating checksum
|
||||
* c. restoring l->checksum and return calculated checksum value.
|
||||
*/
|
||||
static uint32_t calculate_layout_checksum(struct cse_layout *l)
|
||||
{
|
||||
uint32_t curr_checksum = l->checksum;
|
||||
uint32_t calc_checksum;
|
||||
|
||||
l->checksum = 0;
|
||||
calc_checksum = ~crc32(0xffffffff, (void *)&l->size, l->size);
|
||||
l->checksum = curr_checksum;
|
||||
|
||||
return calc_checksum;
|
||||
}
|
||||
|
||||
static cse_layout_ptr create_cse_layout(const struct cse_layout_regions *r)
|
||||
{
|
||||
struct cse_layout *l = calloc(1, sizeof(*l));
|
||||
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
memset(l->rom_bypass, 0xff, sizeof(l->rom_bypass));
|
||||
l->size = sizeof(struct cse_layout) - sizeof(l->rom_bypass);
|
||||
l->redundancy = 0;
|
||||
l->checksum = 0;
|
||||
l->data_offset = r->data_partition.offset;
|
||||
l->data_size = r->data_partition.size;
|
||||
l->bp1_offset = r->bp1.offset;
|
||||
l->bp1_size = r->bp1.size;
|
||||
l->bp2_offset = r->bp2.offset;
|
||||
l->bp2_size = r->bp2.size;
|
||||
l->bp3_offset = r->bp3.offset;
|
||||
l->bp3_size = r->bp3.size;
|
||||
l->bp4_offset = r->bp4.offset;
|
||||
l->bp4_size = r->bp4.size;
|
||||
l->bp5_offset = 0;
|
||||
l->bp5_size = 0;
|
||||
l->temp_base_addr = 0;
|
||||
l->temp_base_size = 0;
|
||||
l->flog_offset = 0;
|
||||
l->flog_size = 0;
|
||||
|
||||
l->checksum = calculate_layout_checksum(l);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
static void print_cse_layout(const cse_layout_ptr ptr)
|
||||
{
|
||||
struct cse_layout *l = ptr;
|
||||
|
||||
printf(" * CSE Layout\n\n");
|
||||
printf("ROM Bypass: ");
|
||||
for (size_t i = 0; i < sizeof(l->rom_bypass); i++)
|
||||
printf("0x%x ", l->rom_bypass[i]);
|
||||
printf("\n");
|
||||
printf("Size: 0x%x\n", l->size);
|
||||
printf("Redundancy: 0x%x\n", l->redundancy);
|
||||
printf("Checksum: 0x%x\n", l->checksum);
|
||||
printf("Data partition offset: 0x%x\n", l->data_offset);
|
||||
printf("Data partition size: 0x%x\n", l->data_size);
|
||||
printf("BP1 offset: 0x%x\n", l->bp1_offset);
|
||||
printf("BP1 size: 0x%x\n", l->bp1_size);
|
||||
printf("BP2 offset: 0x%x\n", l->bp2_offset);
|
||||
printf("BP2 size: 0x%x\n", l->bp2_size);
|
||||
printf("BP3 offset: 0x%x\n", l->bp3_offset);
|
||||
printf("BP3 size: 0x%x\n", l->bp3_size);
|
||||
printf("BP4 offset: 0x%x\n", l->bp4_offset);
|
||||
printf("BP4 size: 0x%x\n", l->bp4_size);
|
||||
printf("BP5 offset: 0x%x\n", l->bp5_offset);
|
||||
printf("BP5 size: 0x%x\n", l->bp5_size);
|
||||
printf("Temp base addr: 0x%x\n", l->temp_base_addr);
|
||||
printf("Temp base size: 0x%x\n", l->temp_base_size);
|
||||
printf("FLOG offset: 0x%x\n", l->flog_offset);
|
||||
printf("FLOG size: 0x%x\n", l->flog_size);
|
||||
}
|
||||
|
||||
static cse_layout_ptr read_cse_layout(struct buffer *buff)
|
||||
{
|
||||
struct cse_layout *l = calloc(1, sizeof(*l));
|
||||
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
READ_MEMBER(buff, l->rom_bypass);
|
||||
READ_MEMBER(buff, l->size);
|
||||
READ_MEMBER(buff, l->redundancy);
|
||||
READ_MEMBER(buff, l->checksum);
|
||||
READ_MEMBER(buff, l->data_offset);
|
||||
READ_MEMBER(buff, l->data_size);
|
||||
READ_MEMBER(buff, l->bp1_offset);
|
||||
READ_MEMBER(buff, l->bp1_size);
|
||||
READ_MEMBER(buff, l->bp2_offset);
|
||||
READ_MEMBER(buff, l->bp2_size);
|
||||
READ_MEMBER(buff, l->bp3_offset);
|
||||
READ_MEMBER(buff, l->bp3_size);
|
||||
READ_MEMBER(buff, l->bp4_offset);
|
||||
READ_MEMBER(buff, l->bp4_size);
|
||||
READ_MEMBER(buff, l->bp5_offset);
|
||||
READ_MEMBER(buff, l->bp5_size);
|
||||
READ_MEMBER(buff, l->temp_base_addr);
|
||||
READ_MEMBER(buff, l->temp_base_size);
|
||||
READ_MEMBER(buff, l->flog_offset);
|
||||
READ_MEMBER(buff, l->flog_size);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
static int write_cse_layout(struct buffer *buff, const cse_layout_ptr ptr)
|
||||
{
|
||||
struct cse_layout *l = ptr;
|
||||
|
||||
if (buffer_size(buff) < sizeof(struct cse_layout)) {
|
||||
ERROR("Not enough size in buffer for CSE layout!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
WRITE_MEMBER(buff, l->rom_bypass);
|
||||
WRITE_MEMBER(buff, l->size);
|
||||
WRITE_MEMBER(buff, l->redundancy);
|
||||
WRITE_MEMBER(buff, l->checksum);
|
||||
WRITE_MEMBER(buff, l->data_offset);
|
||||
WRITE_MEMBER(buff, l->data_size);
|
||||
WRITE_MEMBER(buff, l->bp1_offset);
|
||||
WRITE_MEMBER(buff, l->bp1_size);
|
||||
WRITE_MEMBER(buff, l->bp2_offset);
|
||||
WRITE_MEMBER(buff, l->bp2_size);
|
||||
WRITE_MEMBER(buff, l->bp3_offset);
|
||||
WRITE_MEMBER(buff, l->bp3_size);
|
||||
WRITE_MEMBER(buff, l->bp4_offset);
|
||||
WRITE_MEMBER(buff, l->bp4_size);
|
||||
WRITE_MEMBER(buff, l->bp5_offset);
|
||||
WRITE_MEMBER(buff, l->bp5_size);
|
||||
WRITE_MEMBER(buff, l->temp_base_addr);
|
||||
WRITE_MEMBER(buff, l->temp_base_size);
|
||||
WRITE_MEMBER(buff, l->flog_offset);
|
||||
WRITE_MEMBER(buff, l->flog_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t calculate_bpdt_checksum(struct bpdt_header *h, struct bpdt_entry *e)
|
||||
{
|
||||
uint32_t calc_checksum;
|
||||
uint32_t curr_checksum = h->checksum;
|
||||
|
||||
h->checksum = 0;
|
||||
|
||||
calc_checksum = crc32(0xffffffff, (void *)&h->descriptor_count,
|
||||
sizeof(*h) - sizeof(h->signature));
|
||||
|
||||
if (e && h->descriptor_count)
|
||||
calc_checksum = crc32(calc_checksum, (void *)e,
|
||||
h->descriptor_count * sizeof(struct bpdt_entry));
|
||||
|
||||
h->checksum = curr_checksum;
|
||||
|
||||
return ~calc_checksum;
|
||||
}
|
||||
|
||||
static void update_checksum(bpdt_hdr_ptr ptr, struct bpdt_entry *e)
|
||||
{
|
||||
struct bpdt_header *h = ptr;
|
||||
h->checksum = calculate_bpdt_checksum(h, e);
|
||||
}
|
||||
|
||||
static bool validate_checksum(bpdt_hdr_ptr ptr, struct bpdt_entry *e)
|
||||
{
|
||||
struct bpdt_header *h = ptr;
|
||||
return calculate_bpdt_checksum(h, e) == h->checksum;
|
||||
}
|
||||
|
||||
const struct bpdt_ops bpdt_1_7_ops = {
|
||||
.match_version = match_version,
|
||||
|
||||
.create_hdr = create_bpdt_hdr,
|
||||
.print_hdr = print_bpdt_hdr,
|
||||
.read_hdr = read_bpdt_hdr,
|
||||
.write_hdr = write_bpdt_hdr,
|
||||
|
||||
.get_entry_count = get_bpdt_entry_count,
|
||||
.inc_entry_count = inc_bpdt_entry_count,
|
||||
|
||||
.create_layout = create_cse_layout,
|
||||
.print_layout = print_cse_layout,
|
||||
.read_layout = read_cse_layout,
|
||||
.write_layout = write_cse_layout,
|
||||
|
||||
.update_checksum = update_checksum,
|
||||
.validate_checksum = validate_checksum,
|
||||
|
||||
.subpart_hdr_version = SUBPART_HDR_VERSION_2,
|
||||
.subpart_entry_version = SUBPART_ENTRY_VERSION_1,
|
||||
};
|
|
@ -0,0 +1,66 @@
|
|||
/* Subpart directory entry version 1 support */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "cse_serger.h"
|
||||
|
||||
#define SUBPART_OFFSET_SHIFT 0
|
||||
#define SUBPART_OFFSET_MASK 0x1ffffff
|
||||
#define SUBPART_OFFSET(x) (((x) >> SUBPART_OFFSET_SHIFT) & SUBPART_OFFSET_MASK)
|
||||
#define SUBPART_COMPRESSED_SHIFT 25
|
||||
#define SUBPART_COMPRESSED_MASK 1
|
||||
#define SUBPART_COMPRESSED(x) \
|
||||
(((x) >> SUBPART_COMPRESSED_SHIFT) & SUBPART_COMPRESSED_MASK)
|
||||
|
||||
struct subpart_entry {
|
||||
uint8_t name[12];
|
||||
uint32_t offset_bytes;
|
||||
uint32_t length;
|
||||
uint32_t rsvd2;
|
||||
} __packed;
|
||||
|
||||
static void subpart_read_entry(struct buffer *buff, struct subpart_entry *e)
|
||||
{
|
||||
READ_MEMBER(buff, e->name);
|
||||
READ_MEMBER(buff, e->offset_bytes);
|
||||
READ_MEMBER(buff, e->length);
|
||||
READ_MEMBER(buff, e->rsvd2);
|
||||
}
|
||||
|
||||
static void subpart_print_entry(const struct subpart_entry *e, size_t index)
|
||||
{
|
||||
printf("%-25zd%-25.12s0x%-23x%-25c0x%-23x0x%-23x\n", index,
|
||||
e->name, SUBPART_OFFSET(e->offset_bytes),
|
||||
SUBPART_COMPRESSED(e->offset_bytes) ? 'Y' : 'N',
|
||||
e->length, e->rsvd2);
|
||||
}
|
||||
|
||||
static void subpart_print_entries(struct buffer *buff, size_t count)
|
||||
{
|
||||
struct subpart_entry *e = malloc(count * sizeof(*e));
|
||||
|
||||
if (!e)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
subpart_read_entry(buff, &e[i]);
|
||||
|
||||
printf("%-25s%-25s%-25s%-25s%-25s%-25s\n", "Entry #", "Name", "Offset",
|
||||
"Huffman Compressed?", "Length", "Rsvd");
|
||||
|
||||
printf("====================================================================="
|
||||
"=====================================================================\n");
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
subpart_print_entry(&e[i], i + 1);
|
||||
|
||||
printf("====================================================================="
|
||||
"=====================================================================\n");
|
||||
|
||||
free(e);
|
||||
}
|
||||
|
||||
const struct subpart_entry_ops subpart_entry_1_ops = {
|
||||
.print = subpart_print_entries,
|
||||
};
|
|
@ -0,0 +1,69 @@
|
|||
/* Subpart directory header version 1 support */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "cse_serger.h"
|
||||
|
||||
struct subpart_hdr {
|
||||
uint32_t signature; /* SUBPART_SIGNATURE */
|
||||
uint32_t count;
|
||||
uint8_t hdr_version; /* Header version = 1 */
|
||||
uint8_t entry_version; /* Entry version = 1 */
|
||||
uint8_t length;
|
||||
uint8_t checksum;
|
||||
uint8_t name[4];
|
||||
} __packed;
|
||||
|
||||
static void subpart_hdr_print(const subpart_hdr_ptr ptr)
|
||||
{
|
||||
const struct subpart_hdr *hdr = ptr;
|
||||
|
||||
printf("%-25s %.4s\n", "Signature", (const char *)&hdr->signature);
|
||||
printf("%-25s %-25d\n", "Count", hdr->count);
|
||||
printf("%-25s %-25d\n", "Header Version", hdr->hdr_version);
|
||||
printf("%-25s %-25d\n", "Entry Version", hdr->entry_version);
|
||||
printf("%-25s 0x%-23x\n", "Header Length", hdr->length);
|
||||
printf("%-25s 0x%-23x\n", "Checksum", hdr->checksum);
|
||||
printf("%-25s ", "Name");
|
||||
for (size_t i = 0; i < sizeof(hdr->name); i++)
|
||||
printf("%c", hdr->name[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static subpart_hdr_ptr subpart_hdr_read(struct buffer *buff)
|
||||
{
|
||||
struct subpart_hdr *hdr = malloc(sizeof(*hdr));
|
||||
|
||||
if (!hdr)
|
||||
return NULL;
|
||||
|
||||
READ_MEMBER(buff, hdr->signature);
|
||||
READ_MEMBER(buff, hdr->count);
|
||||
READ_MEMBER(buff, hdr->hdr_version);
|
||||
READ_MEMBER(buff, hdr->entry_version);
|
||||
READ_MEMBER(buff, hdr->length);
|
||||
READ_MEMBER(buff, hdr->checksum);
|
||||
READ_MEMBER(buff, hdr->name);
|
||||
|
||||
return hdr;
|
||||
}
|
||||
|
||||
static size_t subpart_get_count(const subpart_hdr_ptr ptr)
|
||||
{
|
||||
const struct subpart_hdr *hdr = ptr;
|
||||
|
||||
return hdr->count;
|
||||
}
|
||||
|
||||
static void subpart_hdr_free(subpart_hdr_ptr ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
const struct subpart_hdr_ops subpart_hdr_1_ops = {
|
||||
.read = subpart_hdr_read,
|
||||
.print = subpart_hdr_print,
|
||||
.get_entry_count = subpart_get_count,
|
||||
.free = subpart_hdr_free,
|
||||
};
|
|
@ -0,0 +1,72 @@
|
|||
/* Subpart directory header version 2 support */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "cse_serger.h"
|
||||
|
||||
struct subpart_hdr {
|
||||
uint32_t signature; /* SUBPART_SIGNATURE */
|
||||
uint32_t count;
|
||||
uint8_t hdr_version; /* Header version = 2 */
|
||||
uint8_t entry_version; /* Entry version = 1 */
|
||||
uint8_t length;
|
||||
uint8_t reserved;
|
||||
uint8_t name[4];
|
||||
uint32_t checksum;
|
||||
} __packed;
|
||||
|
||||
static subpart_hdr_ptr subpart_hdr_read(struct buffer *buff)
|
||||
{
|
||||
struct subpart_hdr *hdr = malloc(sizeof(*hdr));
|
||||
|
||||
if (!hdr)
|
||||
return NULL;
|
||||
|
||||
READ_MEMBER(buff, hdr->signature);
|
||||
READ_MEMBER(buff, hdr->count);
|
||||
READ_MEMBER(buff, hdr->hdr_version);
|
||||
READ_MEMBER(buff, hdr->entry_version);
|
||||
READ_MEMBER(buff, hdr->length);
|
||||
READ_MEMBER(buff, hdr->reserved);
|
||||
READ_MEMBER(buff, hdr->name);
|
||||
READ_MEMBER(buff, hdr->checksum);
|
||||
|
||||
return hdr;
|
||||
}
|
||||
|
||||
static void subpart_hdr_print(const subpart_hdr_ptr ptr)
|
||||
{
|
||||
const struct subpart_hdr *hdr = ptr;
|
||||
|
||||
printf("%-25s %.4s\n", "Signature", (const char *)&hdr->signature);
|
||||
printf("%-25s %-25d\n", "Count", hdr->count);
|
||||
printf("%-25s %-25d\n", "Header Version", hdr->hdr_version);
|
||||
printf("%-25s %-25d\n", "Entry Version", hdr->entry_version);
|
||||
printf("%-25s 0x%-23x\n", "Header Length", hdr->length);
|
||||
printf("%-25s 0x%-23x\n", "Reserved", hdr->reserved);
|
||||
printf("%-25s ", "Name");
|
||||
for (size_t i = 0; i < sizeof(hdr->name); i++)
|
||||
printf("%c", hdr->name[i]);
|
||||
printf("\n");
|
||||
printf("%-25s 0x%-23x\n", "Checksum", hdr->checksum);
|
||||
}
|
||||
|
||||
static size_t subpart_get_count(const subpart_hdr_ptr ptr)
|
||||
{
|
||||
const struct subpart_hdr *hdr = ptr;
|
||||
|
||||
return hdr->count;
|
||||
}
|
||||
|
||||
static void subpart_hdr_free(subpart_hdr_ptr ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
const struct subpart_hdr_ops subpart_hdr_2_ops = {
|
||||
.read = subpart_hdr_read,
|
||||
.print = subpart_hdr_print,
|
||||
.get_entry_count = subpart_get_count,
|
||||
.free = subpart_hdr_free,
|
||||
};
|
|
@ -0,0 +1,987 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* CSE Serger - Tool for stitching Intel CSE components */
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cse_serger.h"
|
||||
|
||||
#define NO_PARTITION_TYPE (-1)
|
||||
|
||||
static struct params {
|
||||
bool print_sub_parts;
|
||||
const char *partition_name;
|
||||
int partition_type;
|
||||
const char *output_dir;
|
||||
const char *image_name;
|
||||
const char *version_str;
|
||||
const char *input_file;
|
||||
struct cse_layout_regions layout_regions;
|
||||
} params;
|
||||
|
||||
static const struct {
|
||||
const char *version_str;
|
||||
const struct bpdt_ops *ops;
|
||||
} bpdt_ops_table[] = {
|
||||
{ "1.6", &bpdt_1_6_ops },
|
||||
{ "1.7", &bpdt_1_7_ops },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
enum subpart_hdr_version version;
|
||||
const struct subpart_hdr_ops *ops;
|
||||
} subpart_hdr_ops_table[] = {
|
||||
{ SUBPART_HDR_VERSION_1, &subpart_hdr_1_ops },
|
||||
{ SUBPART_HDR_VERSION_2, &subpart_hdr_2_ops },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
enum subpart_entry_version version;
|
||||
const struct subpart_entry_ops *ops;
|
||||
} subpart_entry_ops_table[] = {
|
||||
{ SUBPART_ENTRY_VERSION_1, &subpart_entry_1_ops },
|
||||
};
|
||||
|
||||
enum bpdt_entry_type {
|
||||
SMIP = 0,
|
||||
CSE_RBE = 1,
|
||||
CSE_BUP = 2,
|
||||
UCODE = 3,
|
||||
IBB = 4,
|
||||
S_BPDT = 5,
|
||||
OBB = 6,
|
||||
CSE_MAIN = 7,
|
||||
ISH = 8,
|
||||
CSE_IDLM = 9,
|
||||
IFP_OVERRIDE = 10,
|
||||
UTOK = 11,
|
||||
UFS_PHY = 12,
|
||||
UFS_GPP = 13,
|
||||
PMC = 14,
|
||||
IUNIT = 15,
|
||||
NVM_CFG = 16,
|
||||
UEP = 17,
|
||||
OEM_KM = 20,
|
||||
PAVP = 22,
|
||||
IOM_FW = 23,
|
||||
NPHY_FW = 24,
|
||||
TBT_FW = 25,
|
||||
ICC = 32,
|
||||
|
||||
MAX_SUBPARTS,
|
||||
};
|
||||
|
||||
static struct {
|
||||
struct buffer input_buff;
|
||||
|
||||
const struct bpdt_ops *bpdt_ops;
|
||||
const struct subpart_hdr_ops *subpart_hdr_ops;
|
||||
const struct subpart_entry_ops *subpart_entry_ops;
|
||||
|
||||
bpdt_hdr_ptr bpdt_hdr;
|
||||
cse_layout_ptr cse_layout;
|
||||
struct bpdt_entry bpdt_entries[MAX_SUBPARTS];
|
||||
struct buffer subpart_buff[MAX_SUBPARTS];
|
||||
bool repack;
|
||||
size_t file_end_offset;
|
||||
} ifwi;
|
||||
|
||||
#define SUBPART_WITH_ALT(_index, _rname, _name, _aname) \
|
||||
[_index] = { _rname, _name, _aname }
|
||||
#define SUBPART(_index, _rname, _name) \
|
||||
SUBPART_WITH_ALT(_index, _rname, _name, "")
|
||||
|
||||
static const struct {
|
||||
const char *readable_name;
|
||||
const char *name;
|
||||
const char *alt_name;
|
||||
} subparts[] = {
|
||||
SUBPART(SMIP, "OEM SMIP", "SMIP"),
|
||||
SUBPART(CSE_RBE, "CSE RBE", "RBEP"),
|
||||
SUBPART_WITH_ALT(CSE_BUP, "CSE BUP", "FTPR", "MFTP"),
|
||||
SUBPART(UCODE, "Microcode", "UCOD"),
|
||||
SUBPART(IBB, "Initial Boot Block", "IBBP"),
|
||||
SUBPART(S_BPDT, "Secondary BPDT", "SBDT"),
|
||||
SUBPART(OBB, "OEM Boot Block", "OBBP"),
|
||||
SUBPART(CSE_MAIN, "CSE Main", "NFTP"),
|
||||
SUBPART(ISH, "ISH Firmware", "ISHP"),
|
||||
SUBPART(CSE_IDLM, "CSE IDLM", "DLMP"),
|
||||
SUBPART(IFP_OVERRIDE, "IFP override", "IFPP"),
|
||||
SUBPART(UTOK, "Debug tokens", "UTOK"),
|
||||
SUBPART(UFS_PHY, "UFS Phy", "UFSP"),
|
||||
SUBPART(UFS_GPP, "UFS GPP", "UFSG"),
|
||||
SUBPART(PMC, "PMC Firmware", "PMCP"),
|
||||
SUBPART(IUNIT, "IUNIT Firmware", "IUNP"),
|
||||
SUBPART(NVM_CFG, "NVM CFG", "NVMC"),
|
||||
SUBPART(UEP, "UEP", "UEPP"),
|
||||
SUBPART(OEM_KM, "OEM Key Manifest", "OEMP"),
|
||||
SUBPART(PAVP, "PAVP", "PAVP"),
|
||||
SUBPART(IOM_FW, "IOM Firmware", "IOMP"),
|
||||
SUBPART(NPHY_FW, "NPHY Firmware", "NPHY"),
|
||||
SUBPART(TBT_FW, "TBT Firmware", "TBTP"),
|
||||
SUBPART(ICC, "ICC Firmware", "PCHC"),
|
||||
};
|
||||
|
||||
static const char *subpart_readable_name(enum bpdt_entry_type type)
|
||||
{
|
||||
return subparts[type].readable_name;
|
||||
}
|
||||
|
||||
static const char *subpart_name(enum bpdt_entry_type type)
|
||||
{
|
||||
return subparts[type].name;
|
||||
}
|
||||
|
||||
static const char *subpart_alt_name(enum bpdt_entry_type type)
|
||||
{
|
||||
return subparts[type].alt_name;
|
||||
}
|
||||
|
||||
static struct buffer *subpart_buff(int type)
|
||||
{
|
||||
return &ifwi.subpart_buff[type];
|
||||
}
|
||||
|
||||
static int subpart_get_type_from_name(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_SUBPARTS; i++) {
|
||||
if (subpart_name(i) == NULL)
|
||||
continue;
|
||||
|
||||
if (!strcmp(subpart_name(i), name))
|
||||
return i;
|
||||
|
||||
if (!strcmp(subpart_alt_name(i), name))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void write_member(struct buffer *buff, void *src, size_t size)
|
||||
{
|
||||
void *dst = buffer_get(buff);
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
write_le8(dst, *(uint8_t *)src);
|
||||
break;
|
||||
case 2:
|
||||
write_le16(dst, *(uint16_t *)src);
|
||||
break;
|
||||
case 4:
|
||||
write_le32(dst, *(uint32_t *)src);
|
||||
break;
|
||||
case 8:
|
||||
write_le64(dst, *(uint64_t *)src);
|
||||
break;
|
||||
default:
|
||||
memcpy(dst, src, size);
|
||||
break;
|
||||
}
|
||||
|
||||
buffer_seek(buff, size);
|
||||
}
|
||||
|
||||
void read_member(struct buffer *buff, void *dst, size_t size)
|
||||
{
|
||||
const void *src = buffer_get(buff);
|
||||
|
||||
switch (size) {
|
||||
case 1:
|
||||
*(uint8_t *)dst = read_le8(src);
|
||||
break;
|
||||
case 2:
|
||||
*(uint16_t *)dst = read_le16(src);
|
||||
break;
|
||||
case 4:
|
||||
*(uint32_t *)dst = read_le32(src);
|
||||
break;
|
||||
case 8:
|
||||
*(uint64_t *)dst = read_le64(src);
|
||||
break;
|
||||
default:
|
||||
memcpy(dst, src, size);
|
||||
break;
|
||||
}
|
||||
|
||||
buffer_seek(buff, size);
|
||||
}
|
||||
|
||||
static const struct bpdt_ops *get_bpdt_ops(const struct buffer *buff)
|
||||
{
|
||||
assert(buff || params.version_str);
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(bpdt_ops_table); i++) {
|
||||
if (params.version_str) {
|
||||
if (!strcmp(params.version_str, bpdt_ops_table[i].version_str))
|
||||
return bpdt_ops_table[i].ops;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
if (bpdt_ops_table[i].ops->match_version(buff))
|
||||
return bpdt_ops_table[i].ops;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct subpart_hdr_ops *get_subpart_hdr_ops(void)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_SIZE(subpart_hdr_ops_table); i++) {
|
||||
if (subpart_hdr_ops_table[i].version == ifwi.bpdt_ops->subpart_hdr_version)
|
||||
return subpart_hdr_ops_table[i].ops;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct subpart_entry_ops *get_subpart_entry_ops(void)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_SIZE(subpart_entry_ops_table); i++) {
|
||||
if (subpart_entry_ops_table[i].version == ifwi.bpdt_ops->subpart_entry_version)
|
||||
return subpart_entry_ops_table[i].ops;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int subpart_read(struct buffer *input_buff)
|
||||
{
|
||||
size_t input_size = buffer_size(input_buff);
|
||||
struct bpdt_entry *e = &ifwi.bpdt_entries[0];
|
||||
struct buffer *buff;
|
||||
|
||||
for (size_t i = 0; i < ifwi.bpdt_ops->get_entry_count(ifwi.bpdt_hdr); i++, e++) {
|
||||
if (e->size == 0)
|
||||
continue;
|
||||
|
||||
if (e->type >= MAX_SUBPARTS) {
|
||||
ERROR("Invalid part type(%d)\n", e->type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (e->offset + e->size > input_size) {
|
||||
ERROR("Part(%d) exceeds file size. Part offset=0x%x, Part size = 0x%x, File size = 0x%zx\n",
|
||||
e->type, e->offset, e->size, input_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buff = subpart_buff(e->type);
|
||||
if (buffer_size(buff) != 0) {
|
||||
ERROR("Multiple subparts of same type(%d %s)!\n",
|
||||
e->type, subpart_name(e->type));
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffer_splice(buff, input_buff, e->offset, e->size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bpdt_entry *find_bpdt_entry(uint32_t type)
|
||||
{
|
||||
struct bpdt_entry *e = &ifwi.bpdt_entries[0];
|
||||
|
||||
for (size_t i = 0; i < ifwi.bpdt_ops->get_entry_count(ifwi.bpdt_hdr); i++, e++) {
|
||||
if (e->type == type)
|
||||
return e;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct bpdt_entry *new_bpdt_entry(void)
|
||||
{
|
||||
size_t count = ifwi.bpdt_ops->get_entry_count(ifwi.bpdt_hdr);
|
||||
if (count == MAX_SUBPARTS) {
|
||||
ERROR("No space for new BPDT entry!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ifwi.bpdt_ops->inc_entry_count(ifwi.bpdt_hdr);
|
||||
|
||||
return &ifwi.bpdt_entries[count];
|
||||
}
|
||||
|
||||
static void set_file_end_offset(struct buffer *buff)
|
||||
{
|
||||
struct bpdt_entry *e = &ifwi.bpdt_entries[0];
|
||||
size_t end_offset;
|
||||
size_t count = ifwi.bpdt_ops->get_entry_count(ifwi.bpdt_hdr);
|
||||
|
||||
ifwi.file_end_offset = ALIGN_UP(buffer_offset(buff), BUFF_SIZE_ALIGN);
|
||||
|
||||
for (size_t i = 0; i < count; i++, e++) {
|
||||
end_offset = e->offset + e->size;
|
||||
if (end_offset > ifwi.file_end_offset)
|
||||
ifwi.file_end_offset = end_offset;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void read_bpdt_entries(struct buffer *buff)
|
||||
{
|
||||
struct bpdt_entry *e = &ifwi.bpdt_entries[0];
|
||||
size_t count = ifwi.bpdt_ops->get_entry_count(ifwi.bpdt_hdr);
|
||||
|
||||
for (size_t i = 0; i < count; i++, e++) {
|
||||
READ_MEMBER(buff, e->type);
|
||||
READ_MEMBER(buff, e->offset);
|
||||
READ_MEMBER(buff, e->size);
|
||||
}
|
||||
}
|
||||
|
||||
static int write_bpdt_entries(struct buffer *buff)
|
||||
{
|
||||
struct bpdt_entry *e = &ifwi.bpdt_entries[0];
|
||||
size_t count = ifwi.bpdt_ops->get_entry_count(ifwi.bpdt_hdr);
|
||||
|
||||
if (buffer_size(buff) < count * sizeof(*e)) {
|
||||
ERROR("Not enough buffer space for bpdt entries!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; i++, e++) {
|
||||
WRITE_MEMBER(buff, e->type);
|
||||
WRITE_MEMBER(buff, e->offset);
|
||||
WRITE_MEMBER(buff, e->size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_bpdt_entries(void)
|
||||
{
|
||||
const size_t count = ifwi.bpdt_ops->get_entry_count(ifwi.bpdt_hdr);
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
const struct bpdt_entry *e = &ifwi.bpdt_entries[0];
|
||||
|
||||
printf("\n * BPDT entries\n");
|
||||
|
||||
printf("%-25s%-25s%-25s%-25s%-25s%-25s\n", "Entry #",
|
||||
"Partition Name", "Human readable name", "Type", "Offset", "Size");
|
||||
|
||||
printf("===================================================================="
|
||||
"====================================================================\n");
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
printf("%-25zd%-25s%-25s%-25d0x%-23x0x%-23x"
|
||||
"\n", i+1, subpart_name(e[i].type), subpart_readable_name(e[i].type),
|
||||
e[i].type, e[i].offset, e[i].size);
|
||||
}
|
||||
|
||||
printf("===================================================================="
|
||||
"====================================================================\n");
|
||||
}
|
||||
|
||||
static int ifwi_parse(const char *image_name)
|
||||
{
|
||||
struct buffer *input_buff = &ifwi.input_buff;
|
||||
struct buffer bpdt_buff;
|
||||
|
||||
if (buffer_from_file(input_buff, image_name)) {
|
||||
ERROR("Failed to read input file %s\n", image_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffer_clone(&bpdt_buff, input_buff);
|
||||
|
||||
ifwi.bpdt_ops = get_bpdt_ops(&bpdt_buff);
|
||||
if (!ifwi.bpdt_ops) {
|
||||
ERROR("No matching bpdt_ops!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ifwi.bpdt_hdr = ifwi.bpdt_ops->read_hdr(&bpdt_buff);
|
||||
if (ifwi.bpdt_hdr == NULL)
|
||||
return -1;
|
||||
|
||||
read_bpdt_entries(&bpdt_buff);
|
||||
set_file_end_offset(&bpdt_buff);
|
||||
|
||||
if (!ifwi.bpdt_ops->validate_checksum(ifwi.bpdt_hdr, &ifwi.bpdt_entries[0])) {
|
||||
ERROR("Checksum failed!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ifwi.subpart_hdr_ops = get_subpart_hdr_ops();
|
||||
if (ifwi.subpart_hdr_ops == NULL) {
|
||||
ERROR("No matching subpart_hdr_ops for given BPDT!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ifwi.subpart_entry_ops = get_subpart_entry_ops();
|
||||
if (ifwi.subpart_entry_ops == NULL) {
|
||||
ERROR("No matching subpart_entry_ops for given BPDT!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return subpart_read(&ifwi.input_buff);
|
||||
}
|
||||
|
||||
static int subpart_write(struct buffer *buff)
|
||||
{
|
||||
struct bpdt_entry *e;
|
||||
struct buffer *s_buff;
|
||||
|
||||
for (size_t i = 0; i < ifwi.bpdt_ops->get_entry_count(ifwi.bpdt_hdr); i++) {
|
||||
e = &ifwi.bpdt_entries[i];
|
||||
|
||||
if (e->size == 0)
|
||||
continue;
|
||||
|
||||
if (e->offset + e->size > buffer_size(buff)) {
|
||||
ERROR("Subpart end(0x%x) overflows buffer size(0x%zx)\n",
|
||||
e->offset + e->size, buffer_size(buff));
|
||||
return -1;
|
||||
}
|
||||
|
||||
s_buff = subpart_buff(e->type);
|
||||
|
||||
if (buffer_size(s_buff) != e->size) {
|
||||
ERROR("Subpart buffer size does not match BPDT entry size!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(buffer_get(buff) + e->offset, buffer_get(s_buff), e->size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ifwi_repack(void)
|
||||
{
|
||||
if (!ifwi.repack)
|
||||
return 0;
|
||||
|
||||
struct buffer output_buff;
|
||||
const size_t size = ifwi.file_end_offset;
|
||||
struct buffer bpdt_buff;
|
||||
|
||||
if (buffer_create(&output_buff, size, "Output IFWI")) {
|
||||
ERROR("Unable to allocate output buff!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffer_clone(&bpdt_buff, &output_buff);
|
||||
|
||||
ifwi.bpdt_ops->update_checksum(ifwi.bpdt_hdr, &ifwi.bpdt_entries[0]);
|
||||
|
||||
if (ifwi.bpdt_ops->write_hdr(&bpdt_buff, ifwi.bpdt_hdr))
|
||||
return -1;
|
||||
|
||||
if (write_bpdt_entries(&bpdt_buff))
|
||||
return -1;
|
||||
|
||||
subpart_write(&output_buff);
|
||||
|
||||
if (buffer_write_file(&output_buff, params.image_name)) {
|
||||
ERROR("File write error!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Image written successfully to %s.\n", params.image_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool should_process_partition(int type)
|
||||
{
|
||||
if (params.partition_name) {
|
||||
const char *name = subpart_name(type);
|
||||
|
||||
if (!name)
|
||||
return false;
|
||||
|
||||
if (strcmp(params.partition_name, name))
|
||||
return false;
|
||||
} else if (params.partition_type != NO_PARTITION_TYPE) {
|
||||
if (params.partition_type != type)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int process_entries(int (*fn)(const struct bpdt_entry *e))
|
||||
{
|
||||
struct bpdt_entry *e = &ifwi.bpdt_entries[0];
|
||||
bool found = false;
|
||||
|
||||
for (size_t i = 0; i < ifwi.bpdt_ops->get_entry_count(ifwi.bpdt_hdr); i++, e++) {
|
||||
if (e->size == 0)
|
||||
continue;
|
||||
|
||||
if (!should_process_partition(e->type))
|
||||
continue;
|
||||
|
||||
if (fn(e))
|
||||
return -1;
|
||||
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found && params.partition_name) {
|
||||
ERROR("Partition %s not found!\n", params.partition_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!found && params.partition_type != NO_PARTITION_TYPE) {
|
||||
ERROR("Partition type %d not found!\n", params.partition_type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_subpart(const struct bpdt_entry *e)
|
||||
{
|
||||
struct buffer buff;
|
||||
subpart_hdr_ptr hdr;
|
||||
|
||||
printf("\n\n * Subpart entry #%d(%s)\n", e->type, subpart_readable_name(e->type));
|
||||
|
||||
buffer_clone(&buff, subpart_buff(e->type));
|
||||
hdr = ifwi.subpart_hdr_ops->read(&buff);
|
||||
if (!hdr) {
|
||||
ERROR("Failed to read subpart header!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ifwi.subpart_hdr_ops->print(hdr);
|
||||
ifwi.subpart_entry_ops->print(&buff, ifwi.subpart_hdr_ops->get_entry_count(hdr));
|
||||
ifwi.subpart_hdr_ops->free(hdr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_print(void)
|
||||
{
|
||||
ifwi.bpdt_ops->print_hdr(ifwi.bpdt_hdr);
|
||||
print_bpdt_entries();
|
||||
|
||||
if (!params.print_sub_parts && !params.partition_name &&
|
||||
params.partition_type == NO_PARTITION_TYPE)
|
||||
return 0;
|
||||
|
||||
return process_entries(print_subpart);
|
||||
}
|
||||
|
||||
static char *get_file_path(const char *name)
|
||||
{
|
||||
size_t filename_len = strlen(name) + 1;
|
||||
|
||||
/* output_dir name followed by '/' */
|
||||
if (params.output_dir)
|
||||
filename_len += strlen(params.output_dir) + 1;
|
||||
|
||||
char *filepath = malloc(filename_len);
|
||||
if (!filepath)
|
||||
return NULL;
|
||||
|
||||
snprintf(filepath, filename_len, "%s%s%s",
|
||||
params.output_dir ? : "",
|
||||
params.output_dir ? "/" : "",
|
||||
name);
|
||||
|
||||
return filepath;
|
||||
}
|
||||
|
||||
static int write_partition_to_file(const struct bpdt_entry *e)
|
||||
{
|
||||
size_t end_offset = e->offset + e->size - 1;
|
||||
|
||||
if (end_offset > buffer_size(&ifwi.input_buff)) {
|
||||
ERROR("Offset out of bounds!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *name = subpart_name(e->type);
|
||||
char *filepath = get_file_path(name);
|
||||
if (!filepath) {
|
||||
ERROR("Failed to allocate filepath!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Dumping %.4s in %s\n", name, filepath);
|
||||
|
||||
struct buffer buff;
|
||||
buffer_splice(&buff, &ifwi.input_buff, e->offset, e->size);
|
||||
buffer_write_file(&buff, filepath);
|
||||
|
||||
free(filepath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_dump(void)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (params.output_dir && (stat(params.output_dir, &sb) == -1)) {
|
||||
ERROR("Failed to stat %s: %s\n", params.output_dir, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return process_entries(write_partition_to_file);
|
||||
}
|
||||
|
||||
static int cmd_print_layout(void)
|
||||
{
|
||||
if (params.version_str == NULL) {
|
||||
ERROR("No version provided!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const struct bpdt_ops *ops = get_bpdt_ops(NULL);
|
||||
if (!ops) {
|
||||
ERROR("No matching bpdt_ops!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct buffer buff;
|
||||
if (buffer_from_file(&buff, params.image_name)) {
|
||||
ERROR("Failed to read input file %s\n", params.image_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ifwi.cse_layout = ops->read_layout(&buff);
|
||||
if (!ifwi.cse_layout) {
|
||||
ERROR("Failed to read CSE layout!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ops->print_layout(ifwi.cse_layout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int allocate_buffer(struct buffer *buff, struct buffer *wbuff, const char *str)
|
||||
{
|
||||
if (params.version_str == NULL) {
|
||||
ERROR("No version provided!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ifwi.bpdt_ops = get_bpdt_ops(NULL);
|
||||
if (!ifwi.bpdt_ops)
|
||||
return -1;
|
||||
|
||||
if (buffer_create(buff, BUFF_SIZE_ALIGN, str)) {
|
||||
ERROR("Buffer creation error!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *data = buffer_get(buff);
|
||||
memset(data, 0xff, buffer_size(buff));
|
||||
|
||||
buffer_clone(wbuff, buff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_create_layout(void)
|
||||
{
|
||||
struct buffer buff, wbuff;
|
||||
|
||||
if (allocate_buffer(&buff, &wbuff, "CSE layout"))
|
||||
return -1;
|
||||
|
||||
ifwi.cse_layout = ifwi.bpdt_ops->create_layout(¶ms.layout_regions);
|
||||
if (!ifwi.cse_layout) {
|
||||
ERROR("Failed to create layout!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ifwi.bpdt_ops->write_layout(&wbuff, ifwi.cse_layout)) {
|
||||
ERROR("Failed to write CSE layout!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffer_write_file(&buff, params.image_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_create_bpdt(void)
|
||||
{
|
||||
struct buffer buff;
|
||||
struct buffer wbuff;
|
||||
|
||||
if (allocate_buffer(&buff, &wbuff, "BPDT header"))
|
||||
return -1;
|
||||
|
||||
ifwi.bpdt_hdr = ifwi.bpdt_ops->create_hdr();
|
||||
if (!ifwi.bpdt_hdr) {
|
||||
ERROR("Failed to create BPDT header!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ifwi.bpdt_ops->update_checksum(ifwi.bpdt_hdr, NULL);
|
||||
|
||||
if (ifwi.bpdt_ops->write_hdr(&wbuff, ifwi.bpdt_hdr)) {
|
||||
ERROR("Failed to write BPDT header!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffer_write_file(&buff, params.image_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_add(void)
|
||||
{
|
||||
if (!params.partition_name && params.partition_type == NO_PARTITION_TYPE) {
|
||||
ERROR("Partition name/type is required for add!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int type;
|
||||
|
||||
if (params.partition_name) {
|
||||
type = subpart_get_type_from_name(params.partition_name);
|
||||
if (type == NO_PARTITION_TYPE) {
|
||||
ERROR("Invalid partition %s\n", params.partition_name);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
type = params.partition_type;
|
||||
if (type > MAX_SUBPARTS) {
|
||||
ERROR("Invalid type %d\n", type);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
struct bpdt_entry *e = find_bpdt_entry(type);
|
||||
if (e) {
|
||||
ERROR("Partition %s(%d) already exists!\n", params.partition_name ? : "", type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
e = new_bpdt_entry();
|
||||
if (e == NULL)
|
||||
return -1;
|
||||
|
||||
e->type = type;
|
||||
e->offset = 0;
|
||||
e->size = 0;
|
||||
|
||||
ifwi.repack = true;
|
||||
|
||||
if (params.input_file == NULL)
|
||||
return 0;
|
||||
|
||||
struct buffer *buff = subpart_buff(type);
|
||||
if (buffer_from_file_aligned_size(buff, params.input_file, BUFF_SIZE_ALIGN)) {
|
||||
ERROR("Failed to read input file %s\n", params.input_file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
e->offset = ALIGN_UP(ifwi.file_end_offset, BUFF_SIZE_ALIGN);
|
||||
e->size = buffer_size(buff);
|
||||
|
||||
ifwi.file_end_offset = e->offset + e->size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void parse_region(struct region *r, char *arg)
|
||||
{
|
||||
char *tok;
|
||||
|
||||
tok = strtok(arg, ":");
|
||||
r->offset = strtol(tok, NULL, 0);
|
||||
|
||||
tok = strtok(NULL, ":");
|
||||
r->size = strtol(tok, NULL, 0);
|
||||
}
|
||||
|
||||
static struct command {
|
||||
const char *name;
|
||||
const char *optstring;
|
||||
int (*cb)(void);
|
||||
bool parse_ifwi;
|
||||
} commands[] = {
|
||||
{ "print", "n:st:?", cmd_print, true },
|
||||
{ "dump", "n:o:t:?", cmd_dump, true },
|
||||
{ "create-layout", "v:?", cmd_create_layout, false },
|
||||
{ "print-layout", "v:?", cmd_print_layout, false },
|
||||
{ "create-bpdt", "v:?", cmd_create_bpdt, false },
|
||||
{ "add", "f:n:t:v:?", cmd_add, true },
|
||||
};
|
||||
|
||||
enum {
|
||||
LONGOPT_START = 256,
|
||||
LONGOPT_BP1 = LONGOPT_START,
|
||||
LONGOPT_BP2,
|
||||
LONGOPT_BP3,
|
||||
LONGOPT_BP4,
|
||||
LONGOPT_DATA,
|
||||
|
||||
LONGOPT_END,
|
||||
};
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"help", required_argument, 0, 'h'},
|
||||
{"parition_name", required_argument, 0, 'n'},
|
||||
{"output_dir", required_argument, 0, 'o'},
|
||||
{"sub_partition", no_argument, 0, 's'},
|
||||
{"version", required_argument, 0, 'v'},
|
||||
{"bp1", required_argument, 0, LONGOPT_BP1},
|
||||
{"bp2", required_argument, 0, LONGOPT_BP2},
|
||||
{"bp3", required_argument, 0, LONGOPT_BP3},
|
||||
{"bp4", required_argument, 0, LONGOPT_BP4},
|
||||
{"dp", required_argument, 0, LONGOPT_DATA},
|
||||
{NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
static bool valid_opt(size_t i, int c)
|
||||
{
|
||||
/* Check if it is one of the optstrings supported by the command. */
|
||||
if (strchr(commands[i].optstring, c))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Check if it is one of the non-ASCII characters. Currently, the
|
||||
* non-ASCII characters are only checked against the valid list
|
||||
* irrespective of the command.
|
||||
*/
|
||||
return c >= LONGOPT_START && c < LONGOPT_END;
|
||||
}
|
||||
|
||||
static void usage(const char *name)
|
||||
{
|
||||
printf("%s: Utility for stitching CSE components\n"
|
||||
"USAGE:\n"
|
||||
" %s FILE COMMAND\n\n"
|
||||
"COMMANDs:\n"
|
||||
" print [-s][-n NAME][-t TYPE]\n"
|
||||
" dump [-o DIR][-n NAME]\n"
|
||||
" create-layout --dp <offset:size> --bp1 <offset:size> --bp2 <offset:size> -v VERSION\n"
|
||||
" print-layout -v VERSION\n"
|
||||
" create-bpdt -v VERSION\n"
|
||||
" add [-n NAME][-t TYPE][-f INPUT_FILE]\n"
|
||||
"\nOPTIONS:\n"
|
||||
" -f INPUT_FILE : Input file\n"
|
||||
" -n NAME : Sub-partition name\n"
|
||||
" -o DIR : Output directory\n"
|
||||
" -s : Print sub-partition info\n"
|
||||
" -t TYPE : Sub-partition type\n"
|
||||
" -v VERSION : BPDT version\n"
|
||||
" --dp <offset:size> : Offset and size of data partition\n"
|
||||
" --bp1 <offset:size> : Offset and size of BP1\n"
|
||||
" --bp2 <offset:size> : Offset and size of BP2\n"
|
||||
" --bp3 <offset:size> : Offset and size of BP3\n"
|
||||
" --bp4 <offset:size> : Offset and size of BP4\n"
|
||||
"\n",
|
||||
name, name);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 3) {
|
||||
printf("Incorrect number of args(%d)!\n", argc);
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *prog_name = argv[0];
|
||||
const char *image_name = argv[1];
|
||||
const char *cmd = argv[2];
|
||||
|
||||
size_t i;
|
||||
|
||||
params.partition_type = NO_PARTITION_TYPE;
|
||||
params.image_name = image_name;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(commands); i++) {
|
||||
if (strcmp(cmd, commands[i].name))
|
||||
continue;
|
||||
|
||||
int c;
|
||||
int option_index;
|
||||
|
||||
while (1) {
|
||||
c = getopt_long(argc, argv, commands[i].optstring,
|
||||
long_options, &option_index);
|
||||
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
if (!valid_opt(i, c)) {
|
||||
if (c < LONGOPT_START) {
|
||||
ERROR("Invalid option -- '%c'\n", c);
|
||||
} else {
|
||||
ERROR("Invalid option -- '%d'\n", c);
|
||||
}
|
||||
usage(prog_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 'f':
|
||||
params.input_file = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
params.partition_name = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
params.output_dir = optarg;
|
||||
break;
|
||||
case 's':
|
||||
params.print_sub_parts = true;
|
||||
break;
|
||||
case 'v':
|
||||
params.version_str = optarg;
|
||||
break;
|
||||
case 't':
|
||||
params.partition_type = atoi(optarg);
|
||||
break;
|
||||
case LONGOPT_BP1:
|
||||
parse_region(¶ms.layout_regions.bp1, optarg);
|
||||
break;
|
||||
case LONGOPT_BP2:
|
||||
parse_region(¶ms.layout_regions.bp2, optarg);
|
||||
break;
|
||||
case LONGOPT_BP3:
|
||||
parse_region(¶ms.layout_regions.bp3, optarg);
|
||||
break;
|
||||
case LONGOPT_BP4:
|
||||
parse_region(¶ms.layout_regions.bp4, optarg);
|
||||
break;
|
||||
case LONGOPT_DATA:
|
||||
parse_region(¶ms.layout_regions.data_partition, optarg);
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
default:
|
||||
usage(prog_name);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(commands)) {
|
||||
printf("No command match %s!\n", cmd);
|
||||
usage(prog_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (commands[i].parse_ifwi && ifwi_parse(image_name))
|
||||
return 1;
|
||||
|
||||
if (commands[i].cb())
|
||||
return 1;
|
||||
|
||||
return ifwi_repack();
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __CBFSTOOL_CSE_SERGER_H__
|
||||
#define __CBFSTOOL_CSE_SERGER_H__
|
||||
|
||||
#include <commonlib/endian.h>
|
||||
#include <commonlib/region.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define BPDT_SIGNATURE (0x000055AA)
|
||||
|
||||
#define BUFF_SIZE_ALIGN (4 * KiB)
|
||||
|
||||
#define READ_MEMBER(_buff, _x) read_member(_buff, &(_x), sizeof(_x))
|
||||
#define WRITE_MEMBER(_buff, _x) write_member(_buff, &(_x), sizeof(_x))
|
||||
|
||||
enum bpdt_version {
|
||||
BPDT_VERSION_1_6 = 1,
|
||||
BPDT_VERSION_1_7 = 2,
|
||||
};
|
||||
|
||||
enum subpart_hdr_version {
|
||||
SUBPART_HDR_VERSION_1 = 1,
|
||||
SUBPART_HDR_VERSION_2 = 2,
|
||||
};
|
||||
|
||||
enum subpart_entry_version {
|
||||
SUBPART_ENTRY_VERSION_1 = 1,
|
||||
};
|
||||
|
||||
struct cse_layout_regions {
|
||||
struct region data_partition;
|
||||
struct region bp1;
|
||||
struct region bp2;
|
||||
struct region bp3;
|
||||
struct region bp4;
|
||||
};
|
||||
|
||||
typedef void *cse_layout_ptr;
|
||||
typedef void *bpdt_hdr_ptr;
|
||||
typedef void *subpart_hdr_ptr;
|
||||
|
||||
struct bpdt_entry {
|
||||
uint32_t type;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
} __packed;
|
||||
|
||||
struct bpdt_ops {
|
||||
bool (*match_version)(const struct buffer *buff);
|
||||
|
||||
bpdt_hdr_ptr (*create_hdr)(void);
|
||||
void (*print_hdr)(const bpdt_hdr_ptr ptr);
|
||||
bpdt_hdr_ptr (*read_hdr)(struct buffer *buff);
|
||||
int (*write_hdr)(struct buffer *buff, const bpdt_hdr_ptr ptr);
|
||||
|
||||
size_t (*get_entry_count)(const bpdt_hdr_ptr ptr);
|
||||
void (*inc_entry_count)(bpdt_hdr_ptr ptr);
|
||||
|
||||
cse_layout_ptr (*create_layout)(const struct cse_layout_regions *regions);
|
||||
void (*print_layout)(const cse_layout_ptr ptr);
|
||||
cse_layout_ptr (*read_layout)(struct buffer *buff);
|
||||
int (*write_layout)(struct buffer *buff, const cse_layout_ptr ptr);
|
||||
|
||||
void (*update_checksum)(bpdt_hdr_ptr ptr, struct bpdt_entry *entry);
|
||||
bool (*validate_checksum)(bpdt_hdr_ptr ptr, struct bpdt_entry *entry);
|
||||
|
||||
enum subpart_hdr_version subpart_hdr_version;
|
||||
enum subpart_entry_version subpart_entry_version;
|
||||
};
|
||||
|
||||
struct subpart_hdr_ops {
|
||||
subpart_hdr_ptr (*read)(struct buffer *buffer);
|
||||
void (*print)(const subpart_hdr_ptr ptr);
|
||||
size_t (*get_entry_count)(const subpart_hdr_ptr ptr);
|
||||
void (*free)(subpart_hdr_ptr ptr);
|
||||
};
|
||||
|
||||
struct subpart_entry_ops {
|
||||
void (*print)(struct buffer *buff, size_t size);
|
||||
};
|
||||
|
||||
extern const struct bpdt_ops bpdt_1_7_ops;
|
||||
extern const struct bpdt_ops bpdt_1_6_ops;
|
||||
|
||||
extern const struct subpart_hdr_ops subpart_hdr_1_ops;
|
||||
extern const struct subpart_hdr_ops subpart_hdr_2_ops;
|
||||
|
||||
extern const struct subpart_entry_ops subpart_entry_1_ops;
|
||||
|
||||
void read_member(struct buffer *buff, void *dst, size_t size);
|
||||
void write_member(struct buffer *buff, void *src, size_t size);
|
||||
|
||||
#endif /* __CBFSTOOL_CSE_SERGER_H__ */
|
Loading…
Reference in New Issue