diff --git a/Makefile.inc b/Makefile.inc index 7be0505971..b784f3eee8 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -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 diff --git a/util/cbfstool/Makefile b/util/cbfstool/Makefile index dc5177b2f1..e853ae509c 100644 --- a/util/cbfstool/Makefile +++ b/util/cbfstool/Makefile @@ -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: diff --git a/util/cbfstool/Makefile.inc b/util/cbfstool/Makefile.inc index 050bc0d81e..0a445b5942 100644 --- a/util/cbfstool/Makefile.inc +++ b/util/cbfstool/Makefile.inc @@ -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 diff --git a/util/cbfstool/bpdt_formats/Makefile.inc b/util/cbfstool/bpdt_formats/Makefile.inc new file mode 100644 index 0000000000..c676489e9f --- /dev/null +++ b/util/cbfstool/bpdt_formats/Makefile.inc @@ -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 diff --git a/util/cbfstool/bpdt_formats/bpdt_1_6.c b/util/cbfstool/bpdt_formats/bpdt_1_6.c new file mode 100644 index 0000000000..eaa2902670 --- /dev/null +++ b/util/cbfstool/bpdt_formats/bpdt_1_6.c @@ -0,0 +1,272 @@ +/* BPDT version 1.6 support */ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include + +#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, +}; diff --git a/util/cbfstool/bpdt_formats/bpdt_1_7.c b/util/cbfstool/bpdt_formats/bpdt_1_7.c new file mode 100644 index 0000000000..53294242ca --- /dev/null +++ b/util/cbfstool/bpdt_formats/bpdt_1_7.c @@ -0,0 +1,367 @@ +/* BPDT version 1.7 support */ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include + +#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, +}; diff --git a/util/cbfstool/bpdt_formats/subpart_entry_1.c b/util/cbfstool/bpdt_formats/subpart_entry_1.c new file mode 100644 index 0000000000..a5982ff132 --- /dev/null +++ b/util/cbfstool/bpdt_formats/subpart_entry_1.c @@ -0,0 +1,66 @@ +/* Subpart directory entry version 1 support */ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include + +#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, +}; diff --git a/util/cbfstool/bpdt_formats/subpart_hdr_1.c b/util/cbfstool/bpdt_formats/subpart_hdr_1.c new file mode 100644 index 0000000000..5335c7a844 --- /dev/null +++ b/util/cbfstool/bpdt_formats/subpart_hdr_1.c @@ -0,0 +1,69 @@ +/* Subpart directory header version 1 support */ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include + +#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, +}; diff --git a/util/cbfstool/bpdt_formats/subpart_hdr_2.c b/util/cbfstool/bpdt_formats/subpart_hdr_2.c new file mode 100644 index 0000000000..17304dc447 --- /dev/null +++ b/util/cbfstool/bpdt_formats/subpart_hdr_2.c @@ -0,0 +1,72 @@ +/* Subpart directory header version 2 support */ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include + +#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, +}; diff --git a/util/cbfstool/cse_serger.c b/util/cbfstool/cse_serger.c new file mode 100644 index 0000000000..cc49671d19 --- /dev/null +++ b/util/cbfstool/cse_serger.c @@ -0,0 +1,987 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* CSE Serger - Tool for stitching Intel CSE components */ + +#include +#include +#include +#include +#include +#include +#include + +#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 --bp1 --bp2 -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 and size of data partition\n" + " --bp1 : Offset and size of BP1\n" + " --bp2 : Offset and size of BP2\n" + " --bp3 : Offset and size of BP3\n" + " --bp4 : 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(); +} diff --git a/util/cbfstool/cse_serger.h b/util/cbfstool/cse_serger.h new file mode 100644 index 0000000000..b60a9c7a0a --- /dev/null +++ b/util/cbfstool/cse_serger.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __CBFSTOOL_CSE_SERGER_H__ +#define __CBFSTOOL_CSE_SERGER_H__ + +#include +#include + +#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__ */