From 796aeeba96fce7d6560c4d1994962a7107137666 Mon Sep 17 00:00:00 2001 From: Furquan Shaikh Date: Thu, 16 Sep 2021 22:02:01 -0700 Subject: util/cse_fpt: Add a new tool for managing Intel CSE FPT binaries This change adds a new tool `cse_fpt` which can be used to print and dump CSE partitions in Flash Partition Table (FPT) format. BUG=b:189167923 Change-Id: I93c8d33e9baa327cbdab918a14f2f7a039953be6 Signed-off-by: Furquan Shaikh Reviewed-on: https://review.coreboot.org/c/coreboot/+/55259 Tested-by: build bot (Jenkins) Reviewed-by: Tim Wawrzynczak --- Makefile.inc | 8 +- util/cbfstool/Makefile | 9 +- util/cbfstool/Makefile.inc | 14 ++ util/cbfstool/cse_fpt.c | 434 +++++++++++++++++++++++++++++++++ util/cbfstool/cse_fpt.h | 37 +++ util/cbfstool/fpt_formats/Makefile.inc | 3 + util/cbfstool/fpt_formats/fpt_hdr_20.c | 106 ++++++++ util/cbfstool/fpt_formats/fpt_hdr_21.c | 114 +++++++++ 8 files changed, 721 insertions(+), 4 deletions(-) create mode 100644 util/cbfstool/cse_fpt.c create mode 100644 util/cbfstool/cse_fpt.h create mode 100644 util/cbfstool/fpt_formats/Makefile.inc create mode 100644 util/cbfstool/fpt_formats/fpt_hdr_20.c create mode 100644 util/cbfstool/fpt_formats/fpt_hdr_21.c diff --git a/Makefile.inc b/Makefile.inc index 501fc59807..7be0505971 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 +coreboot: $(obj)/coreboot.rom $(obj)/cbfstool $(obj)/rmodtool $(obj)/ifwitool $(obj)/cse_fpt # 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 @@ -544,6 +544,7 @@ RMODTOOL:=$(objutil)/cbfstool/rmodtool IFWITOOL:=$(objutil)/cbfstool/ifwitool IFITTOOL:=$(objutil)/cbfstool/ifittool AMDCOMPRESS:=$(objutil)/cbfstool/amdcompress +CSE_FPT:=$(objutil)/cbfstool/cse_fpt $(obj)/cbfstool: $(CBFSTOOL) cp $< $@ @@ -563,6 +564,9 @@ $(obj)/ifittool: $(IFITTOOL) $(obj)/amdcompress: $(AMDCOMPRESS) cp $< $@ +$(obj)/cse_fpt: $(CSE_FPT) + cp $< $@ + _WINCHECK=$(shell uname -o 2> /dev/null) STACK= ifeq ($(_WINCHECK),Msys) @@ -689,7 +693,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 +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) ########################################################################### # Common recipes for all stages diff --git a/util/cbfstool/Makefile b/util/cbfstool/Makefile index f5e0c6917e..dc5177b2f1 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 +all: cbfstool ifittool fmaptool rmodtool ifwitool cbfs-compression-tool elogtool cse_fpt cbfstool: $(objutil)/cbfstool/cbfstool @@ -29,7 +29,9 @@ cbfs-compression-tool: $(objutil)/cbfstool/cbfs-compression-tool elogtool: $(objutil)/cbfstool/elogtool -.PHONY: clean cbfstool ifittool fmaptool rmodtool ifwitool cbfs-compression-tool elogtool +cse_fpt: $(objutil)/cbfstool/cse_fpt + +.PHONY: clean cbfstool ifittool fmaptool rmodtool ifwitool cbfs-compression-tool elogtool cse_fpt clean: $(RM) fmd_parser.c fmd_parser.h fmd_scanner.c fmd_scanner.h $(RM) $(objutil)/cbfstool/cbfstool $(cbfsobj) @@ -39,6 +41,7 @@ clean: $(RM) $(objutil)/cbfstool/ifittool $(ifitobj) $(RM) $(objutil)/cbfstool/cbfs-compression-tool $(cbfscompobj) $(RM) $(objutil)/cbfstool/elogtool $(elogobj) + $(RM) $(objutil)/cbfstool/cse_fpt $(cse_fpt_obj) $(RM) -r $(VBOOT_HOST_BUILD) linux_trampoline.c: linux_trampoline.S @@ -61,6 +64,7 @@ install: all $(INSTALL) ifittool $(DESTDIR)$(BINDIR) $(INSTALL) cbfs-compression-tool $(DESTDIR)$(BINDIR) $(INSTALL) elogtool $(DESTDIR)$(BINDIR) + $(INSTALL) cse_fpt $(DESTDIR)$(BINDIR) distclean: clean @@ -74,6 +78,7 @@ help: @echo " ifittool - Manipulate Intel FW Interface Table (FIT)" @echo " cbfs-compression-tool - benchmark compression algorithms" @echo " elogtool - Display ELOG events" + @echo " cse_fpt - Manage Intel CSE Flash Partition Table (FPT)" ifneq ($(V),1) .SILENT: diff --git a/util/cbfstool/Makefile.inc b/util/cbfstool/Makefile.inc index 1113c86c51..050bc0d81e 100644 --- a/util/cbfstool/Makefile.inc +++ b/util/cbfstool/Makefile.inc @@ -96,6 +96,12 @@ elogobj += valstr.o elogobj += elog.o elogobj += common.o +include $(top)/util/cbfstool/fpt_formats/Makefile.inc +cse_fpt_obj := +cse_fpt_obj += cse_fpt.o +cse_fpt_obj += common.o +cse_fpt_obj += $(foreach var, $(fpt_formats_obj), $(var)) + TOOLCFLAGS ?= -Werror -Wall -Wextra -Wshadow TOOLCFLAGS += -Wcast-qual -Wmissing-prototypes -Wredundant-decls -Wshadow TOOLCFLAGS += -Wstrict-prototypes -Wwrite-strings @@ -178,6 +184,10 @@ $(objutil)/cbfstool/%.o: $(top)/util/cbfstool/lz4/lib/%.c printf " HOSTCC $(subst $(objutil)/,,$(@))\n" $(HOSTCC) $(TOOLCPPFLAGS) $(TOOLCFLAGS) $(HOSTCFLAGS) -c -o $@ $< +$(objutil)/cbfstool/%.o: $(top)/util/cbfstool/fpt_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) @@ -210,6 +220,10 @@ $(objutil)/cbfstool/elogtool: $(addprefix $(objutil)/cbfstool/,$(elogobj)) $(VBO printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n" $(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(elogobj)) $(VBOOT_HOSTLIB) +$(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)) + # 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/cse_fpt.c b/util/cbfstool/cse_fpt.c new file mode 100644 index 0000000000..bde3a8c165 --- /dev/null +++ b/util/cbfstool/cse_fpt.c @@ -0,0 +1,434 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* CSE FPT tool */ + +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "cse_fpt.h" + +static struct params { + const char *output_dir; + const char *partition_name; + + struct fpt_hdr_ops *hdr_ops; +} params; + +#define FPT_ENTRY_TYPE_MASK 0x7f +#define FPT_ENTRY_TYPE_SHIFT 0 +#define GET_FPT_ENTRY_TYPE(x) (((x) >> FPT_ENTRY_TYPE_SHIFT) & FPT_ENTRY_TYPE_MASK) +#define FPT_ENTRY_TYPE_CODE 0x0 +#define FPT_ENTRY_TYPE_DATA 0x1 + +#define FPT_ENTRY_VALID_MASK 0xff +#define FPT_ENTRY_VALID_SHIFT 24 +#define GET_FPT_ENTRY_VALID(x) (((x) >> FPT_ENTRY_VALID_SHIFT) & FPT_ENTRY_VALID_MASK) +#define FPT_ENTRY_INVALID 0xff +#define FPT_ENTRY_VALID 0x0 + +struct fpt_entry { + uint8_t name[4]; /* ASCII short name */ + uint8_t rsvd1[4]; + uint32_t offset; /* Offset in bytes from start of FPT binary */ + uint32_t length; /* Size in bytes */ + uint8_t rsvd2[12]; + uint32_t flags; +} __packed; + +static struct fpt { + struct buffer input_buff; + + const struct fpt_hdr_ops *hdr_ops; + + fpt_hdr_ptr hdr; + struct fpt_entry *entries; +} fpt; + +static void usage(const char *name) +{ + printf("%s: Utility for CSE FPT\n\n" + "USAGE:\n" + " %s FILE COMMAND\n\n" + "COMMANDs:\n" + " print\n" + " dump [-o OUTPUT_DIR] [-n NAME]\n" + "\nOPTIONS:\n" + " -o OUTPUT_DIR : Directory to dump the partition files in\n" + " -n NAME : Name of partition to dump\n" + "\n", + name, name); +} + +void read_member(struct buffer *buff, void *dst, size_t size) +{ + uint8_t *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); + } + + buffer_seek(buff, size); +} + +static int get_fpt_buff(struct buffer *input_buff, struct buffer *fpt_buff) +{ + /* + * FPT marker is typically at offset 0x10 in the released CSE binary. Check at offset + * 0x10 first and if that fails fall back to checking offset 0. + */ + const size_t fpt_offsets[] = { 0x10, 0 }; + size_t i; + + for (i = 0; i < ARRAY_SIZE(fpt_offsets); i++) { + if (buffer_size(input_buff) < (strlen(FPT_MARKER) + fpt_offsets[i])) + continue; + + const uint8_t *data = buffer_get(input_buff); + + if (!memcmp(data + fpt_offsets[i], FPT_MARKER, strlen(FPT_MARKER))) + break; + } + + if (i == ARRAY_SIZE(fpt_offsets)) { + ERROR("Could not locate FPT at known offsets.\n"); + return -1; + } + + buffer_clone(fpt_buff, input_buff); + buffer_seek(fpt_buff, fpt_offsets[i]); + + return 0; +} + +static int read_fpt_entries(struct buffer *buff) +{ + size_t i; + struct fpt_entry *e; + const size_t entries = fpt.hdr_ops->get_entry_count(fpt.hdr); + const size_t fpt_entries_size = sizeof(struct fpt_entry) * entries; + + if (buffer_size(buff) < fpt_entries_size) { + ERROR("Not enough bytes(actual=0x%zx, expected=0x%zx) for FPT entries!\n", + buffer_size(buff), fpt_entries_size); + return -1; + } + + e = fpt.entries = malloc(fpt_entries_size); + + for (i = 0; i < entries; i++, e++) { + READ_MEMBER(buff, e->name); + READ_MEMBER(buff, e->rsvd1); + READ_MEMBER(buff, e->offset); + READ_MEMBER(buff, e->length); + READ_MEMBER(buff, e->rsvd2); + READ_MEMBER(buff, e->flags); + } + + return 0; +} + +static const struct fpt_hdr_ops *get_fpt_hdr_ops(struct buffer *buff) +{ + static const struct fpt_hdr_ops *hdr_ops[] = { + &fpt_hdr_20_ops, + &fpt_hdr_21_ops, + }; + + for (size_t i = 0; i < ARRAY_SIZE(hdr_ops); i++) { + if (hdr_ops[i]->match_version(buff)) + return hdr_ops[i]; + } + + return NULL; +} + +static int fpt_parse(const char *image_name) +{ + struct buffer *input_buff = &fpt.input_buff; + struct buffer fpt_buff; + + if (buffer_from_file(input_buff, image_name)) { + ERROR("Failed to read input file %s\n", image_name); + return -1; + } + + if (get_fpt_buff(input_buff, &fpt_buff)) + return -1; + + fpt.hdr_ops = get_fpt_hdr_ops(&fpt_buff); + if (fpt.hdr_ops == NULL) { + ERROR("FPT header format not supported!\n"); + return -1; + } + + fpt.hdr = fpt.hdr_ops->read(&fpt_buff); + if (!fpt.hdr) { + ERROR("Unable to read FPT header!\n"); + return -1; + } + + return read_fpt_entries(&fpt_buff); +} + +static bool is_partition_valid(const struct fpt_entry *e) +{ + return e->offset != 0 && e->length != 0 && + GET_FPT_ENTRY_VALID(e->flags) != FPT_ENTRY_INVALID; +} + +static bool is_partition_code(const struct fpt_entry *e) +{ + return GET_FPT_ENTRY_TYPE(e->flags) == FPT_ENTRY_TYPE_CODE; +} + +static void print_fpt_entry(const struct fpt_entry *e) +{ + printf("%-25s0x%-23x0x%-23x%c,%c (0x%.8x)\n", + e->name, e->offset, e->length, + is_partition_code(e) ? 'C' : 'D', + is_partition_valid(e) ? 'V' : 'I', + e->flags); +} + +static void print_fpt_entries(const struct fpt_entry *e, size_t count) +{ + printf("\n * FPT entries\n"); + + printf("%-25s%-25s%-25s%-25s\n", "Name", "Offset", "Size", "Flags"); + + printf("==============================================================" + "===============================\n"); + + for (size_t i = 0; i < count; i++) + print_fpt_entry(&e[i]); + + printf("==============================================================" + "================================\n"); + printf("Flags: I=invalid, V=valid, C=code, D=data\n"); +} + +static bool partition_name_match(const struct fpt_entry *e, const char *name) +{ + if (!name) + return false; + + return !memcmp(e->name, name, sizeof(e->name)); +} + +static const struct fpt_entry *get_partition_entry(const char *name) +{ + for (size_t i = 0; i < fpt.hdr_ops->get_entry_count(fpt.hdr); i++) { + if (partition_name_match(&fpt.entries[i], name)) + return &fpt.entries[i]; + } + + return NULL; +} + +static int cmd_print(void) +{ + if (params.partition_name == NULL) { + fpt.hdr_ops->print(fpt.hdr); + print_fpt_entries(fpt.entries, fpt.hdr_ops->get_entry_count(fpt.hdr)); + } else { + const struct fpt_entry *e = get_partition_entry(params.partition_name); + if (e) + print_fpt_entry(e); + else { + ERROR("Partition %s not found!\n", params.partition_name); + return -1; + } + } + return 0; +} + +static bool should_dump_partition(const struct fpt_entry *e) +{ + if (!is_partition_valid(e)) { + if (partition_name_match(e, params.partition_name)) { + ERROR("Invalid partition requested to be dumped!\n"); + exit(-1); + } + return false; + } + + /* Dump all partitions if no name provided. */ + if (params.partition_name == NULL) + return true; + + return partition_name_match(e, params.partition_name); +} + +static char *get_file_path(const struct fpt_entry *e) +{ + size_t filename_len = sizeof(e->name) + 1; + char *filepath; + + /* output_dir name followed by '/' */ + if (params.output_dir) + filename_len += strlen(params.output_dir) + 1; + + filepath = malloc(filename_len); + if (!filepath) + return NULL; + + snprintf(filepath, filename_len, "%s%s%s", + params.output_dir ? : "", + params.output_dir ? "/" : "", + e->name); + + return filepath; +} + +static int write_partition_to_file(const struct fpt_entry *e) +{ + size_t end_offset = e->offset + e->length - 1; + struct buffer part_buffer; + char *filepath; + + if (end_offset > buffer_size(&fpt.input_buff)) { + ERROR("Offset out of bounds for the partition!\n"); + return -1; + } + + filepath = get_file_path(e); + if (!filepath) { + ERROR("Failed to allocate space for filepath!\n"); + return -1; + } + + printf("Dumping %.4s in %s\n", e->name, filepath); + + buffer_splice(&part_buffer, &fpt.input_buff, e->offset, e->length); + buffer_write_file(&part_buffer, filepath); + + free(filepath); + + return 0; +} + +static int cmd_dump(void) +{ + size_t i; + bool found = false; + 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; + } + + for (i = 0; i < fpt.hdr_ops->get_entry_count(fpt.hdr); i++) { + if (!should_dump_partition(&fpt.entries[i])) + continue; + found = true; + if (write_partition_to_file(&fpt.entries[i])) + return -1; + } + + if (found == false) { + if (params.partition_name) + ERROR("%s not found!\n", params.partition_name); + ERROR("No partitions dumped!\n"); + return -1; + } + + return 0; +} + +static struct command { + const char *name; + const char *optstring; + int (*function)(void); +} commands[] = { + { "print", "n:?", cmd_print }, + { "dump", "n:o:?", cmd_dump }, +}; + +static struct option long_options[] = { + {"help", required_argument, 0, 'h'}, + {"partition_name", required_argument, 0, 'n'}, + {"output_dir", required_argument, 0, 'o'}, + {NULL, 0, 0, 0 } +}; + +int main(int argc, char **argv) +{ + if (argc < 3) { + ERROR("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; + + 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 (strchr(commands[i].optstring, c) == NULL) { + ERROR("Invalid option '%c'\n", c); + usage(prog_name); + return 1; + } + + switch (c) { + case 'o': + params.output_dir = optarg; + break; + case 'n': + params.partition_name = optarg; + break; + case 'h': + case '?': + default: + usage(prog_name); + return 1; + } + } + + break; + } + + if (i == ARRAY_SIZE(commands)) { + ERROR("No command match %s\n", cmd); + usage(prog_name); + return 1; + } + + if (fpt_parse(image_name)) + return 1; + + return commands[i].function(); +} diff --git a/util/cbfstool/cse_fpt.h b/util/cbfstool/cse_fpt.h new file mode 100644 index 0000000000..eda8a07930 --- /dev/null +++ b/util/cbfstool/cse_fpt.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __CBFSTOOL_CSE_FPT_H__ +#define __CBFSTOOL_CSE_FPT_H__ + +#include +#include + +#include "common.h" + +#define FPT_MARKER "$FPT" +#define FPT_ENTRY_VERSION 0x10 + +#define READ_MEMBER(_buff, _x) read_member(_buff, &(_x), sizeof(_x)) + +enum fpt_hdr_version { + FPT_HDR_VERSION_20 = 0x20, + FPT_HDR_VERSION_21 = 0x21, +}; + +typedef void *fpt_hdr_ptr; + +struct fpt_hdr_ops { + bool (*match_version)(struct buffer *buff); + + fpt_hdr_ptr (*read)(struct buffer *buff); + void (*print)(const fpt_hdr_ptr ptr); + + size_t (*get_entry_count)(const fpt_hdr_ptr ptr); +}; + +extern const struct fpt_hdr_ops fpt_hdr_20_ops; +extern const struct fpt_hdr_ops fpt_hdr_21_ops; + +void read_member(struct buffer *buff, void *dst, size_t size); + +#endif /* __CBFSTOOL_CSE_FPT_H__ */ diff --git a/util/cbfstool/fpt_formats/Makefile.inc b/util/cbfstool/fpt_formats/Makefile.inc new file mode 100644 index 0000000000..865eaed5cd --- /dev/null +++ b/util/cbfstool/fpt_formats/Makefile.inc @@ -0,0 +1,3 @@ + +fpt_formats_obj += fpt_hdr_20.o +fpt_formats_obj += fpt_hdr_21.o diff --git a/util/cbfstool/fpt_formats/fpt_hdr_20.c b/util/cbfstool/fpt_formats/fpt_hdr_20.c new file mode 100644 index 0000000000..166d24cdf0 --- /dev/null +++ b/util/cbfstool/fpt_formats/fpt_hdr_20.c @@ -0,0 +1,106 @@ +/* CSE FPT header version 0x20 */ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include + +#include "cse_fpt.h" + +struct fpt_hdr { + uint8_t marker[4]; /* FPT_MARKER */ + uint32_t num_entries; + uint8_t hdr_version; /* FPT_HDR_VERSION_20 */ + uint8_t entry_version; /* FPT_ENTRY_VERSION */ + uint8_t hdr_length; + uint8_t hdr_checksum; + uint8_t rsvd[20]; +} __packed; + +static bool match_version(struct buffer *buff) +{ + const uint8_t *data = buffer_get(buff); + uint8_t version = read_at_le8(data, offsetof(struct fpt_hdr, hdr_version)); + + return version == FPT_HDR_VERSION_20; +} + +static bool validate_fpt_hdr(const struct fpt_hdr *h) +{ + if (memcmp(h->marker, FPT_MARKER, sizeof(h->marker))) { + ERROR("Invalid FPT header marker!\n"); + return false; + } + + if (h->hdr_version != FPT_HDR_VERSION_20) { + ERROR("Invalid FPT header version(0x%x)!\n", h->hdr_version); + return false; + } + + if (h->entry_version != FPT_ENTRY_VERSION) { + ERROR("Invalid FPT entry version(0x%x)!\n", h->entry_version); + return false; + } + + const uint8_t *data = (const uint8_t *)h; + uint8_t checksum = 0; + + for (size_t i = 0; i < sizeof(*h); i++) + checksum += data[i]; + + if (checksum != 0) { + ERROR("Invalid checksum (0x%x)!\n", h->hdr_checksum); + return false; + } + + return true; +} + +static fpt_hdr_ptr read_fpt_hdr(struct buffer *buff) +{ + struct fpt_hdr *h = malloc(sizeof(*h)); + if (!h) + return NULL; + + READ_MEMBER(buff, h->marker); + READ_MEMBER(buff, h->num_entries); + READ_MEMBER(buff, h->hdr_version); + READ_MEMBER(buff, h->entry_version); + READ_MEMBER(buff, h->hdr_length); + READ_MEMBER(buff, h->hdr_checksum); + READ_MEMBER(buff, h->rsvd); + + if (!validate_fpt_hdr(h)) { + free(h); + return NULL; + } + + return h; +} + +static void print_fpt_hdr(const fpt_hdr_ptr ptr) +{ + struct fpt_hdr *h = ptr; + + printf(" * FPT header\n"); + printf("%-25s: %.4s\n", "Marker", h->marker); + printf("%-25s: %d\n", "Number of entries", h->num_entries); + printf("%-25s: 0x%x\n", "Header version", h->hdr_version); + printf("%-25s: 0x%x\n", "Entry version", h->entry_version); + printf("%-25s: %d\n", "Header length", h->hdr_length); + printf("%-25s: 0x%x\n", "Header checksum", h->hdr_checksum); +} + +static size_t get_entry_count(const fpt_hdr_ptr ptr) +{ + struct fpt_hdr *h = ptr; + + return h->num_entries; +} + +const struct fpt_hdr_ops fpt_hdr_20_ops = { + .match_version = match_version, + + .read = read_fpt_hdr, + .print = print_fpt_hdr, + + .get_entry_count = get_entry_count, +}; diff --git a/util/cbfstool/fpt_formats/fpt_hdr_21.c b/util/cbfstool/fpt_formats/fpt_hdr_21.c new file mode 100644 index 0000000000..57746ddd74 --- /dev/null +++ b/util/cbfstool/fpt_formats/fpt_hdr_21.c @@ -0,0 +1,114 @@ +/* CSE FPT header version 0x21 */ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include + +#include "cse_fpt.h" + +struct fpt_hdr { + uint8_t marker[4]; /* FPT_MARKER */ + uint32_t num_entries; + uint8_t hdr_version; /* FPT_HDR_VERSION_21 */ + uint8_t entry_version; /* FPT_ENTRY_VERSION */ + uint8_t hdr_length; + uint8_t redundancy; + uint8_t reserved[8]; + uint32_t checksum; + struct { + uint16_t major; + uint16_t minor; + uint16_t build; + uint16_t hotfix; + } fit_tool_version; +} __packed; + +static bool match_version(struct buffer *buff) +{ + const uint8_t *data = buffer_get(buff); + uint8_t version = read_at_le8(data, offsetof(struct fpt_hdr, hdr_version)); + + return version == FPT_HDR_VERSION_21; +} + +static bool validate_fpt_hdr(const struct fpt_hdr *h) +{ + if (memcmp(h->marker, FPT_MARKER, sizeof(h->marker))) { + ERROR("Invalid FPT header marker!\n"); + return false; + } + + if (h->hdr_version != FPT_HDR_VERSION_21) { + ERROR("Invalid FPT header version(0x%x)!\n", h->hdr_version); + return false; + } + + if (h->entry_version != FPT_ENTRY_VERSION) { + ERROR("Invalid FPT entry version(0x%x)!\n", h->entry_version); + return false; + } + + return true; +} + +static fpt_hdr_ptr read_fpt_hdr(struct buffer *buff) +{ + struct fpt_hdr *h = malloc(sizeof(*h)); + if (!h) + return NULL; + + READ_MEMBER(buff, h->marker); + READ_MEMBER(buff, h->num_entries); + READ_MEMBER(buff, h->hdr_version); + READ_MEMBER(buff, h->entry_version); + READ_MEMBER(buff, h->hdr_length); + READ_MEMBER(buff, h->redundancy); + READ_MEMBER(buff, h->reserved); + READ_MEMBER(buff, h->checksum); + READ_MEMBER(buff, h->fit_tool_version); + + if (!validate_fpt_hdr(h)) { + free(h); + return NULL; + } + + return h; +} + +static void print_fpt_hdr(const fpt_hdr_ptr ptr) +{ + struct fpt_hdr *h = ptr; + + printf(" * FPT header\n"); + printf("%-25s: %.4s\n", "Marker", h->marker); + printf("%-25s: %d\n", "Number of entries", h->num_entries); + printf("%-25s: 0x%x\n", "Header version", h->hdr_version); + printf("%-25s: 0x%x\n", "Entry version", h->entry_version); + printf("%-25s: %d\n", "Header length", h->hdr_length); + printf("%-25s: 0x%x\n", "Redundancy", h->redundancy); + printf("%-25s: ", "Reserved"); + for (size_t i = 0; i < ARRAY_SIZE(h->reserved); i++) + printf("0x%x ", h->reserved[i]); + printf("\n"); + printf("%-25s: 0x%x\n", "Checksum", h->checksum); + 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 size_t get_entry_count(const fpt_hdr_ptr ptr) +{ + struct fpt_hdr *h = ptr; + + return h->num_entries; +} + +const struct fpt_hdr_ops fpt_hdr_21_ops = { + .match_version = match_version, + + .read = read_fpt_hdr, + .print = print_fpt_hdr, + + .get_entry_count = get_entry_count, +}; -- cgit v1.2.3