Marty E. Plummer has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/34932 )
Change subject: util/cbfstool: new utility pnortool ......................................................................
util/cbfstool: new utility pnortool
Change-Id: I6566d86a1e025bfe30aa30da36210f49efde3184 Signed-off-by: Marty E. Plummer hanetzer@startmail.com --- M util/cbfstool/Makefile M util/cbfstool/Makefile.inc A util/cbfstool/pnor/pnor.c A util/cbfstool/pnor/pnor.h A util/cbfstool/pnor_from_fmd.c A util/cbfstool/pnor_from_fmd.h A util/cbfstool/pnortool.c 7 files changed, 568 insertions(+), 2 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/32/34932/1
diff --git a/util/cbfstool/Makefile b/util/cbfstool/Makefile index d5321f6..47470f2 100644 --- a/util/cbfstool/Makefile +++ b/util/cbfstool/Makefile @@ -12,10 +12,12 @@ VBOOT_SOURCE ?= $(top)/3rdparty/vboot
.PHONY: all -all: cbfstool ifittool fmaptool rmodtool ifwitool cbfs-compression-tool +all: cbfstool ifittool fmaptool rmodtool ifwitool cbfs-compression-tool pnortool
cbfstool: $(objutil)/cbfstool/cbfstool
+pnortool: $(objutil)/cbfstool/pnortool + fmaptool: $(objutil)/cbfstool/fmaptool
rmodtool: $(objutil)/cbfstool/rmodtool @@ -26,10 +28,11 @@
cbfs-compression-tool: $(objutil)/cbfstool/cbfs-compression-tool
-.PHONY: clean cbfstool ifittool fmaptool rmodtool ifwitool cbfs-compression-tool +.PHONY: clean cbfstool ifittool fmaptool rmodtool ifwitool cbfs-compression-tool pnortool clean: $(RM) fmd_parser.c fmd_parser.h fmd_scanner.c fmd_scanner.h $(RM) $(objutil)/cbfstool/cbfstool $(cbfsobj) + $(RM) $(objutil)/cbfstool/pnortool $(pnorobj) $(RM) $(objutil)/cbfstool/fmaptool $(fmapobj) $(RM) $(objutil)/cbfstool/rmodtool $(rmodobj) $(RM) $(objutil)/cbfstool/ifwitool $(ifwiobj) @@ -49,6 +52,7 @@ install: all mkdir -p $(DESTDIR)$(BINDIR) $(INSTALL) cbfstool $(DESTDIR)$(BINDIR) + $(INSTALL) pnortool $(DESTDIR)$(BINDIR) $(INSTALL) fmaptool $(DESTDIR)$(BINDIR) $(INSTALL) rmodtool $(DESTDIR)$(BINDIR) $(INSTALL) ifwitool $(DESTDIR)$(BINDIR) diff --git a/util/cbfstool/Makefile.inc b/util/cbfstool/Makefile.inc index 95372c2..f7cb366 100644 --- a/util/cbfstool/Makefile.inc +++ b/util/cbfstool/Makefile.inc @@ -42,6 +42,16 @@ # compression algorithms cbfsobj += $(compressionobj)
+pnorobj := +pnorobj += pnortool.o +pnorobj += cbfs_sections.o +pnorobj += pnor_from_fmd.o +pnorobj += fmd.o +pnorobj += fmd_parser.o +pnorobj += fmd_scanner.o +# FMAP +pnorobj += pnor.o + fmapobj := fmapobj += fmaptool.o fmapobj += cbfs_sections.o @@ -110,6 +120,7 @@ TOOLCFLAGS += -O2 TOOLCPPFLAGS ?= -D_DEFAULT_SOURCE # memccpy() from string.h TOOLCPPFLAGS += -D_XOPEN_SOURCE=700 # strdup() from string.h +TOOLCPPFLAGS += -I$(top)/util/cbfstool/pnor TOOLCPPFLAGS += -I$(top)/util/cbfstool/flashmap TOOLCPPFLAGS += -I$(top)/util/cbfstool TOOLCPPFLAGS += -I$(objutil)/cbfstool @@ -143,6 +154,10 @@ printf " HOSTCC $(subst $(objutil)/,,$(@))\n" $(HOSTCC) $(TOOLCPPFLAGS) $(TOOLCFLAGS) $(HOSTCFLAGS) -c -o $@ $<
+$(objutil)/cbfstool/%.o: $(top)/util/cbfstool/pnor/%.c + printf " HOSTCC $(subst $(objutil)/,,$(@))\n" + $(HOSTCC) $(TOOLCPPFLAGS) $(TOOLCFLAGS) $(HOSTCFLAGS) -c -o $@ $< + $(objutil)/cbfstool/%.o: $(top)/util/cbfstool/flashmap/%.c printf " HOSTCC $(subst $(objutil)/,,$(@))\n" $(HOSTCC) $(TOOLCPPFLAGS) $(TOOLCFLAGS) $(HOSTCFLAGS) -c -o $@ $< @@ -171,6 +186,10 @@ printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n" $(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(cbfsobj))
+$(objutil)/cbfstool/pnortool: $(addprefix $(objutil)/cbfstool/,$(pnorobj)) + printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n" + $(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(pnorobj)) + $(objutil)/cbfstool/fmaptool: $(addprefix $(objutil)/cbfstool/,$(fmapobj)) printf " HOSTCC $(subst $(objutil)/,,$(@)) (link)\n" $(HOSTCC) $(TOOLLDFLAGS) -o $@ $(addprefix $(objutil)/cbfstool/,$(fmapobj)) diff --git a/util/cbfstool/pnor/pnor.c b/util/cbfstool/pnor/pnor.c new file mode 100644 index 0000000..93218a1 --- /dev/null +++ b/util/cbfstool/pnor/pnor.c @@ -0,0 +1,186 @@ +#define _XOPEN_SOURCE 700 + +#include <endian.h> +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pnor.h" + +int pnor_size(const struct pnor *pnor) +{ + if (!pnor) + return -1; + + return sizeof(*pnor) + (pnor->entry_count) * sizeof(struct pnor_area); +} + +static int is_valid_pnor(const struct pnor *pnor) +{ + if (memcmp(pnor, PNOR_SIGNATURE, strlen(PNOR_SIGNATURE)) != 0) + return 0; + if (pnor->version != PNOR_VER) + return 0; + if (pnor->block_count * pnor->block_size + < sizeof(*pnor) + (pnor->entry_count * sizeof(struct pnor_area))) + return 0; + + return 1; +} + +static long int pnor_lsearch(const uint8_t *image, size_t len) +{ + unsigned long int offset; + int pnor_found = 0; + + for (offset = 0; offset < len - strlen(PNOR_SIGNATURE); offset++) { + if (is_valid_pnor((const struct pnor *)&image[offset])) { + pnor_found = 1; + break; + } + } + + if (!pnor_found) + return -1; + + if (offset + pnor_size((const struct pnor *)&image[offset]) > len) + return -1; + + return offset; +} + +static long int pnor_bsearch(const uint8_t *image, size_t len) +{ + unsigned long int offset = -1; + int pnor_found = 0, stride; + + for (stride = len / 2; stride >= 16; stride /= 2) { + if (pnor_found) + break; + + for (offset = 0; offset < len - strlen(PNOR_SIGNATURE); offset += stride) { + if ((offset % (stride * 2) == 0) && (offset != 0)) + continue; + if (is_valid_pnor((const struct pnor *)&image[offset])) { + pnor_found = 1; + break; + } + } + } + + if (!pnor_found) + return -1; + + if (offset + pnor_size((const struct pnor *)&image[offset]) > len) + return -1; + + return offset; +} + +static int popcnt(unsigned int u) +{ + int count; + + /* K&R method */ + for (count = 0; u; count++) + u &= (u - 1); + + return count; +} + +long int pnor_find(const uint8_t *image, unsigned int len) +{ + long int ret = -1; + if ((image == NULL) || (len == 0)) + return -1; + + if (popcnt(len) == 1) + ret = pnor_bsearch(image, len); + else + ret = pnor_lsearch(image, len); + + return ret; +} + +/* int pnor_print(const struct pnor *pnor) */ +/* { */ +/* return 0; */ +/* } */ + +/* char *pnor_flags_to_string(uint16_t flags) */ + +struct pnor *pnor_create(uint32_t size) +{ + struct pnor *pnor; + + pnor = malloc(sizeof(*pnor)); + if (!pnor) + return NULL; + + memset(pnor, 0, sizeof(*pnor)); + memcpy(&pnor->magic, PNOR_SIGNATURE, strlen(PNOR_SIGNATURE)); + pnor->version = PNOR_VER; + pnor->block_size = 0x1000; // hardcode for now + pnor->block_count = size / pnor->block_size; + + return pnor; +} + +void pnor_destroy(struct pnor *pnor) +{ + free(pnor); +} + +int pnor_append_area(struct pnor **pnor, uint32_t offset, uint32_t size, const uint8_t *name, + uint32_t flags) +{ + struct pnor_area *area; + int orig_size, new_size; + + if ((pnor == NULL || *pnor == NULL) || (name == NULL)) + return -1; + + if ((*pnor)->entry_count >= 0x10000) + return -1; + + orig_size = pnor_size(*pnor); + new_size = orig_size + sizeof(*area); + + *pnor = realloc(*pnor, new_size); + if (*pnor == NULL) + return -1; + + area = (struct pnor_area *)((uint8_t *)*pnor + orig_size); + memset(area, 0, sizeof(*area)); + memcpy(&area->base, &offset, sizeof(area->base)); + /* &area->base = &area->base / (*pnor)->block_size; */ + memcpy(&area->size, &size, sizeof(area->size)); + /* &area->size /= (*pnor)->block_size; */ + if (strcmp((const char *)name, "BOOTBLOCK") != 0) + memccpy(&area->name, name, '\0', PNOR_STRLEN); + else + memccpy(&area->name, "HBB", '\0', PNOR_STRLEN); + memcpy(&area->flags, &flags, sizeof(area->flags)); + + (*pnor)->entry_count++; + return new_size; +} + +const struct pnor_area *pnor_find_area(const struct pnor *pnor, const char *name) +{ + unsigned int i; + const struct pnor_area *area = NULL; + + if (!pnor || !name) + return NULL; + + for (i = 0; i < pnor->entry_count; i++) { + if (!strcmp((const char *)pnor->areas[i].name, name)) { + area = &pnor->areas[i]; + break; + } + } + + return area; +} diff --git a/util/cbfstool/pnor/pnor.h b/util/cbfstool/pnor/pnor.h new file mode 100644 index 0000000..569bf67 --- /dev/null +++ b/util/cbfstool/pnor/pnor.h @@ -0,0 +1,144 @@ +#ifndef PNOR_LIB_PNOR_H__ +#define PNOR_LIB_PNOR_H__ + +#include <endian.h> +#include <inttypes.h> + +#define PNOR_SIGNATURE "PART" +#define PNOR_VER 1 +#define PNOR_STRLEN 16 +#define PNOR_BOOTBLOCK "BOOTBLOCK" + +struct pnor_area_user { + uint8_t chip; + uint8_t compresstype; + uint16_t datainteg; + uint8_t vercheck; + uint8_t miscflags; + uint8_t freemisc[2]; + uint32_t reserved[14]; +} __packed; + +struct pnor_area { + uint8_t name[PNOR_STRLEN]; + uint32_t base; + uint32_t size; + uint32_t pid; + uint32_t id; + uint32_t type; + uint32_t flags; + uint32_t actual; + uint32_t resvd[4]; + struct pnor_area_user user; + uint32_t checksum; +} __packed; + +struct pnor { + uint32_t magic; + uint32_t version; + uint32_t size; + uint32_t entry_size; + uint32_t entry_count; + uint32_t block_size; + uint32_t block_count; + uint32_t resvd[4]; + uint32_t checksum; + struct pnor_area areas[]; +} __packed; + +/** + * pnor_find - find PNOR signature in a binary image + * + * @image: binary image + * @len: length of binary image + * + * This function does no error checking. The caller is responsible for + * verifying that the contents are sane. + * + * returns offset of PNOR signature to indicate success + * returns <0 to indicate failure + */ +extern long int pnor_find(const uint8_t *image, unsigned int len); + +/** + * pnor_print - Print contents of pnor data structure + * + * @map: raw map data + * + * returns 0 to indicate success + * returns <0 to indicate failure + */ +/* extern int pnor_print(const struct pnor *pnor); */ + +/** + * pnor_flags_to_string - convert raw flags field into user-friendly string + * + * @flags: raw flags + * + * This function returns a user-friendly comma-separated list of pnor area + * flags. If there are no flags (flags == 0), the string will contain only + * a terminating character ('\0') + * + * This function allocates memory which the caller must free. + * + * returns pointer to an allocated string if successful + * returns NULL to indicate failure + */ +char *pnor_flags_to_string(uint32_t flags); + +/** + * pnor_create - allocate and initialize a new pnor structure + * + * @size: size of the fimware (bytes) + * + * This function will allocate a pnor header. Members of the structure + * which are not passed in are automatically initialized. + * + * returns pointer to newly allocated flashmap header if successful + * returns NULL to indicate failure. + */ +extern struct pnor *pnor_create(uint32_t size); + +/* free memory used by a pnor structure */ +extern void pnor_destroy(struct pnor *pnor); + +/** + * pnor_size - returns size of the pnor data structure (including areas) + * + * @pnor: pnor + * + * returns size of pnor structure if successful + * returns <0 to indicate failure + */ +extern int pnor_size(const struct pnor *pnor); + +/** + * pnor_append_area - realloc an existing pnor and append an area + * + * @pnor: double pointer to existing pnor + * @offset: offset of area + * @size: size of area + * @name: name of area + * @flags: area flags + * + * returns total size of reallocated pnor structure if successful + * returns <0 to indicate failure + */ +extern int pnor_append_area(struct pnor **pnor, uint32_t offset, uint32_t size, + const uint8_t *name, uint32_t flags); + +/** + * pnor_find_area - find a pnor_area entry (by name) and return pointer to it + * + * @pnor: pnor structure to parse + * @name: name of area to find + * + * returns a pointer to the entry in the pnor structure if successfull + * returns NULL to indicate failure or if no matching area is found + */ +extern const struct pnor_area *pnor_find_area(const struct pnor *pnor, const char *name); + +/* unit testing stuff */ +extern int pnor_test(void); + +#endif /* PNOR_LIB_PNOR_H__*/ diff --git a/util/cbfstool/pnor_from_fmd.c b/util/cbfstool/pnor_from_fmd.c new file mode 100644 index 0000000..ba81875 --- /dev/null +++ b/util/cbfstool/pnor_from_fmd.c @@ -0,0 +1,63 @@ +#include "pnor_from_fmd.h" + +#include "common.h" + +#include <assert.h> +#include <string.h> + +static bool pnor_append_fmd_node(struct pnor **pnor, const struct flashmap_descriptor *section, + unsigned absolute_watermark) +{ + uint32_t flags = 0; + if (strlen(section->name) >= PNOR_STRLEN) { + ERROR("Section name ('%s') exceeds %d character PNOR format limit\n", + section->name, PNOR_STRLEN - 1); + return false; + } + + absolute_watermark += section->offset; + + if (pnor_append_area(pnor, absolute_watermark, section->size, (uint8_t *)section->name, + flags) + < 0) { + ERROR("Failed to insert section '%s' into PNOR\n", section->name); + return false; + } + + fmd_foreach_child(subsection, section) + { + if (!pnor_append_fmd_node(pnor, subsection, absolute_watermark)) + return false; + } + + return true; +} + +struct pnor *pnor_from_fmd(const struct flashmap_descriptor *desc) +{ + assert(desc); + assert(desc->size_known); + + if (strlen(desc->name) >= PNOR_STRLEN) { + ERROR("Image name ('%s') exceeds %d character PNOR header limite\n", desc->name, + PNOR_STRLEN - 1); + return NULL; + } + + struct pnor *pnor = pnor_create(desc->size); + + if (!pnor) { + ERROR("Failed to allocate PNOR header\n"); + return pnor; + } + + fmd_foreach_child(real_section, desc) + { + if (!pnor_append_fmd_node(&pnor, real_section, 0)) { + pnor_destroy(pnor); + return NULL; + } + } + + return pnor; +} diff --git a/util/cbfstool/pnor_from_fmd.h b/util/cbfstool/pnor_from_fmd.h new file mode 100644 index 0000000..5df87b4 --- /dev/null +++ b/util/cbfstool/pnor_from_fmd.h @@ -0,0 +1,14 @@ +#ifndef PNOR_FROM_FMD_H_ +#define PNOR_FROM_FMD_H_ + +#include "pnor/pnor.h" +#include "fmd.h" + +/** + * @param desc The descriptor tree serving as a data source + * @return The PNOR section, which is owned by the caller and must + * later be released with a call to pnor_destroy() + */ +struct pnor *pnor_from_fmd(const struct flashmap_descriptor *desc); + +#endif /* PNOR_FROM_FMD_H */ diff --git a/util/cbfstool/pnortool.c b/util/cbfstool/pnortool.c new file mode 100644 index 0000000..abee44d --- /dev/null +++ b/util/cbfstool/pnortool.c @@ -0,0 +1,136 @@ +#include "common.h" +#include "cbfs_sections.h" +#include "pnor_from_fmd.h" + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#define STDIN_FILENAME_SENTINEL "-" + +enum pnortool_return { + PNORTOOL_EXIT_SUCCESS = 0, + PNORTOOL_EXIT_BAD_ARGS, + PNORTOOL_EXIT_BAD_INPUT_PATH, + PNORTOOL_EXIT_BAD_OUTPUT_PATH, + PNORTOOL_EXIT_FAILED_DESCRIPTOR, + PNORTOOL_EXIT_MISSING_FMAP_SECTION, + PNORTOOL_EXIT_MISSING_PRIMARY_CBFS, + PNORTOOL_EXIT_FAILED_PNOR_CONVERSION, + PNORTOOL_EXIT_UNKNOWN_PNOR_SIZE, + PNORTOOL_EXIT_FAILED_WRITING_OUTPUT, +}; + +static void usage(const char *invoked_as) +{ + fprintf(stderr, "%s: Transpiler for fmd (flashmap descriptor) files\n", invoked_as); +} + +static void full_fmd_cleanup(struct flashmap_descriptor **victim) +{ + assert(victim); + + cbfs_sections_cleanup(); + fmd_cleanup(*victim); + *victim = NULL; +} + +int main(int argc, char **argv) +{ + struct { + // Manditory + const char *fmd_filename; + const char *pnor_filename; + } args = {NULL, NULL}; + + bool show_usage = false; + int each_arg; + while (!show_usage && (each_arg = getopt(argc, argv, ":h")) != -1) { + switch (each_arg) { + case 'h': + show_usage = true; + break; + default: + fprintf(stderr, "-%c: Unexpected command-line switch\n", optopt); + show_usage = true; + } + } + + if (show_usage || argc - optind != 2) { + usage(argv[0]); + return PNORTOOL_EXIT_BAD_ARGS; + } + args.fmd_filename = argv[optind]; + args.pnor_filename = argv[optind + 1]; + + FILE *fmd_file = stdin; + if (strcmp(args.fmd_filename, STDIN_FILENAME_SENTINEL) != 0) { + fmd_file = fopen(args.fmd_filename, "r"); + if (!fmd_file) { + fprintf(stderr, "FATAL: Unable to open file '%s'\n", args.fmd_filename); + return PNORTOOL_EXIT_BAD_INPUT_PATH; + } + } + + struct flashmap_descriptor *descriptor = fmd_create(fmd_file); + fclose(fmd_file); + if (!descriptor) { + fputs("FATAL: Failed while processing provided descriptor\n", stderr); + full_fmd_cleanup(&descriptor); + return PNORTOOL_EXIT_FAILED_DESCRIPTOR; + } + + if (!fmd_find_node(descriptor, SECTION_NAME_FMAP)) { + fprintf(stderr, "FATAL: Flashmap descriptor must have an '%s' section\n", + SECTION_NAME_FMAP); + full_fmd_cleanup(&descriptor); + return PNORTOOL_EXIT_MISSING_FMAP_SECTION; + } + + if (!cbfs_sections_primary_cbfs_accounted_for()) { + fprintf(stderr, + "FATAL: Flashmap descriptor must have a '%s' section that is annoted with '%s)'\n", + SECTION_NAME_PRIMARY_CBFS, SECTION_ANNOTATION_CBFS); + full_fmd_cleanup(&descriptor); + return PNORTOOL_EXIT_MISSING_PRIMARY_CBFS; + } + + struct pnor *pnor = pnor_from_fmd(descriptor); + if (!pnor) { + fputs("FATAL: Failed while constructing PNOR section\n", stderr); + full_fmd_cleanup(&descriptor); + return PNORTOOL_EXIT_FAILED_PNOR_CONVERSION; + } + + int size = pnor_size(pnor); + if (size < 0) { + fputs("FATAL: Failed to determine PNOR section size\n", stderr); + pnor_destroy(pnor); + full_fmd_cleanup(&descriptor); + return PNORTOOL_EXIT_UNKNOWN_PNOR_SIZE; + } + + FILE *pnor_file = fopen(args.pnor_filename, "wb"); + if (!pnor_file) { + fprintf(stderr, "FATAL: Unable to open file '%s' for writing\n", + args.pnor_filename); + pnor_destroy(pnor); + full_fmd_cleanup(&descriptor); + return PNORTOOL_EXIT_BAD_OUTPUT_PATH; + } + + if (!fwrite(pnor, size, 1, pnor_file)) { + fputs("FATAL: Failed to write final PNOR to file\n", stderr); + fclose(pnor_file); + pnor_destroy(pnor); + full_fmd_cleanup(&descriptor); + return PNORTOOL_EXIT_FAILED_WRITING_OUTPUT; + } + fclose(pnor_file); + pnor_destroy(pnor); + + fprintf(stderr, "SUCCESS: Wrote %d bytes to file '%s'\n", size, args.pnor_filename); + + full_fmd_cleanup(&descriptor); + return PNORTOOL_EXIT_SUCCESS; +}