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 <furquan@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/55259 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
This commit is contained in:
parent
afd268a0cb
commit
796aeeba96
@ -60,7 +60,7 @@ COREBOOT_EXPORTS += CCACHE_EXTRAFILES
|
|||||||
#######################################################################
|
#######################################################################
|
||||||
# root rule to resolve if in build mode (ie. configuration exists)
|
# root rule to resolve if in build mode (ie. configuration exists)
|
||||||
real-target: $(obj)/config.h coreboot files_added
|
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
|
# 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
|
# targets after the build completes by creating a Makefile.inc in the
|
||||||
@ -544,6 +544,7 @@ RMODTOOL:=$(objutil)/cbfstool/rmodtool
|
|||||||
IFWITOOL:=$(objutil)/cbfstool/ifwitool
|
IFWITOOL:=$(objutil)/cbfstool/ifwitool
|
||||||
IFITTOOL:=$(objutil)/cbfstool/ifittool
|
IFITTOOL:=$(objutil)/cbfstool/ifittool
|
||||||
AMDCOMPRESS:=$(objutil)/cbfstool/amdcompress
|
AMDCOMPRESS:=$(objutil)/cbfstool/amdcompress
|
||||||
|
CSE_FPT:=$(objutil)/cbfstool/cse_fpt
|
||||||
|
|
||||||
$(obj)/cbfstool: $(CBFSTOOL)
|
$(obj)/cbfstool: $(CBFSTOOL)
|
||||||
cp $< $@
|
cp $< $@
|
||||||
@ -563,6 +564,9 @@ $(obj)/ifittool: $(IFITTOOL)
|
|||||||
$(obj)/amdcompress: $(AMDCOMPRESS)
|
$(obj)/amdcompress: $(AMDCOMPRESS)
|
||||||
cp $< $@
|
cp $< $@
|
||||||
|
|
||||||
|
$(obj)/cse_fpt: $(CSE_FPT)
|
||||||
|
cp $< $@
|
||||||
|
|
||||||
_WINCHECK=$(shell uname -o 2> /dev/null)
|
_WINCHECK=$(shell uname -o 2> /dev/null)
|
||||||
STACK=
|
STACK=
|
||||||
ifeq ($(_WINCHECK),Msys)
|
ifeq ($(_WINCHECK),Msys)
|
||||||
@ -689,7 +693,7 @@ install-git-commit-clangfmt:
|
|||||||
include util/crossgcc/Makefile.inc
|
include util/crossgcc/Makefile.inc
|
||||||
|
|
||||||
.PHONY: tools
|
.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
|
# Common recipes for all stages
|
||||||
|
@ -13,7 +13,7 @@ VBOOT_SOURCE ?= $(top)/3rdparty/vboot
|
|||||||
VBOOT_HOST_BUILD ?= $(abspath $(objutil)/vboot_lib)
|
VBOOT_HOST_BUILD ?= $(abspath $(objutil)/vboot_lib)
|
||||||
|
|
||||||
.PHONY: all
|
.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
|
cbfstool: $(objutil)/cbfstool/cbfstool
|
||||||
|
|
||||||
@ -29,7 +29,9 @@ cbfs-compression-tool: $(objutil)/cbfstool/cbfs-compression-tool
|
|||||||
|
|
||||||
elogtool: $(objutil)/cbfstool/elogtool
|
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:
|
clean:
|
||||||
$(RM) fmd_parser.c fmd_parser.h fmd_scanner.c fmd_scanner.h
|
$(RM) fmd_parser.c fmd_parser.h fmd_scanner.c fmd_scanner.h
|
||||||
$(RM) $(objutil)/cbfstool/cbfstool $(cbfsobj)
|
$(RM) $(objutil)/cbfstool/cbfstool $(cbfsobj)
|
||||||
@ -39,6 +41,7 @@ clean:
|
|||||||
$(RM) $(objutil)/cbfstool/ifittool $(ifitobj)
|
$(RM) $(objutil)/cbfstool/ifittool $(ifitobj)
|
||||||
$(RM) $(objutil)/cbfstool/cbfs-compression-tool $(cbfscompobj)
|
$(RM) $(objutil)/cbfstool/cbfs-compression-tool $(cbfscompobj)
|
||||||
$(RM) $(objutil)/cbfstool/elogtool $(elogobj)
|
$(RM) $(objutil)/cbfstool/elogtool $(elogobj)
|
||||||
|
$(RM) $(objutil)/cbfstool/cse_fpt $(cse_fpt_obj)
|
||||||
$(RM) -r $(VBOOT_HOST_BUILD)
|
$(RM) -r $(VBOOT_HOST_BUILD)
|
||||||
|
|
||||||
linux_trampoline.c: linux_trampoline.S
|
linux_trampoline.c: linux_trampoline.S
|
||||||
@ -61,6 +64,7 @@ install: all
|
|||||||
$(INSTALL) ifittool $(DESTDIR)$(BINDIR)
|
$(INSTALL) ifittool $(DESTDIR)$(BINDIR)
|
||||||
$(INSTALL) cbfs-compression-tool $(DESTDIR)$(BINDIR)
|
$(INSTALL) cbfs-compression-tool $(DESTDIR)$(BINDIR)
|
||||||
$(INSTALL) elogtool $(DESTDIR)$(BINDIR)
|
$(INSTALL) elogtool $(DESTDIR)$(BINDIR)
|
||||||
|
$(INSTALL) cse_fpt $(DESTDIR)$(BINDIR)
|
||||||
|
|
||||||
distclean: clean
|
distclean: clean
|
||||||
|
|
||||||
@ -74,6 +78,7 @@ help:
|
|||||||
@echo " ifittool - Manipulate Intel FW Interface Table (FIT)"
|
@echo " ifittool - Manipulate Intel FW Interface Table (FIT)"
|
||||||
@echo " cbfs-compression-tool - benchmark compression algorithms"
|
@echo " cbfs-compression-tool - benchmark compression algorithms"
|
||||||
@echo " elogtool - Display ELOG events"
|
@echo " elogtool - Display ELOG events"
|
||||||
|
@echo " cse_fpt - Manage Intel CSE Flash Partition Table (FPT)"
|
||||||
|
|
||||||
ifneq ($(V),1)
|
ifneq ($(V),1)
|
||||||
.SILENT:
|
.SILENT:
|
||||||
|
@ -96,6 +96,12 @@ elogobj += valstr.o
|
|||||||
elogobj += elog.o
|
elogobj += elog.o
|
||||||
elogobj += common.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 ?= -Werror -Wall -Wextra -Wshadow
|
||||||
TOOLCFLAGS += -Wcast-qual -Wmissing-prototypes -Wredundant-decls -Wshadow
|
TOOLCFLAGS += -Wcast-qual -Wmissing-prototypes -Wredundant-decls -Wshadow
|
||||||
TOOLCFLAGS += -Wstrict-prototypes -Wwrite-strings
|
TOOLCFLAGS += -Wstrict-prototypes -Wwrite-strings
|
||||||
@ -178,6 +184,10 @@ $(objutil)/cbfstool/%.o: $(top)/util/cbfstool/lz4/lib/%.c
|
|||||||
printf " HOSTCC $(subst $(objutil)/,,$(@))\n"
|
printf " HOSTCC $(subst $(objutil)/,,$(@))\n"
|
||||||
$(HOSTCC) $(TOOLCPPFLAGS) $(TOOLCFLAGS) $(HOSTCFLAGS) -c -o $@ $<
|
$(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)
|
$(objutil)/cbfstool/cbfstool: $(addprefix $(objutil)/cbfstool/,$(cbfsobj)) $(VBOOT_HOSTLIB)
|
||||||
printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n"
|
printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n"
|
||||||
$(HOSTCC) -v $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(cbfsobj)) $(VBOOT_HOSTLIB)
|
$(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"
|
printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n"
|
||||||
$(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(elogobj)) $(VBOOT_HOSTLIB)
|
$(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
|
# Yacc source is superset of header
|
||||||
$(objutil)/cbfstool/fmd.o: TOOLCFLAGS += -Wno-redundant-decls
|
$(objutil)/cbfstool/fmd.o: TOOLCFLAGS += -Wno-redundant-decls
|
||||||
$(objutil)/cbfstool/fmd_parser.o: TOOLCFLAGS += -Wno-redundant-decls
|
$(objutil)/cbfstool/fmd_parser.o: TOOLCFLAGS += -Wno-redundant-decls
|
||||||
|
434
util/cbfstool/cse_fpt.c
Normal file
434
util/cbfstool/cse_fpt.c
Normal file
@ -0,0 +1,434 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
/* CSE FPT tool */
|
||||||
|
|
||||||
|
#include <commonlib/endian.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#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();
|
||||||
|
}
|
37
util/cbfstool/cse_fpt.h
Normal file
37
util/cbfstool/cse_fpt.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
|
||||||
|
#ifndef __CBFSTOOL_CSE_FPT_H__
|
||||||
|
#define __CBFSTOOL_CSE_FPT_H__
|
||||||
|
|
||||||
|
#include <commonlib/endian.h>
|
||||||
|
#include <commonlib/region.h>
|
||||||
|
|
||||||
|
#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__ */
|
3
util/cbfstool/fpt_formats/Makefile.inc
Normal file
3
util/cbfstool/fpt_formats/Makefile.inc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
fpt_formats_obj += fpt_hdr_20.o
|
||||||
|
fpt_formats_obj += fpt_hdr_21.o
|
106
util/cbfstool/fpt_formats/fpt_hdr_20.c
Normal file
106
util/cbfstool/fpt_formats/fpt_hdr_20.c
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/* CSE FPT header version 0x20 */
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#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,
|
||||||
|
};
|
114
util/cbfstool/fpt_formats/fpt_hdr_21.c
Normal file
114
util/cbfstool/fpt_formats/fpt_hdr_21.c
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/* CSE FPT header version 0x21 */
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#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,
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user