From e63620012cb64baa0de3c45e906ffcc74fe5d656 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Wed, 23 Apr 2025 11:54:00 +0200 Subject: [PATCH] util: Add Microchip EC FW tool The Microchip EC can share the SPI flash with the x86 host. Since it boots first and does power sequencing, there's no problem with concurrent access happening. Due to various vendor specific flash layouts used on x86, the EC needs a pointer to it's own firmware. The pointer resides at flash offset 0 and is read by MEC152x and MEC1701 and MEC172x ECs, probably others as well. The introduced tool generates the EC FW PTR at flash offset 0. Allows to get rid of hand-crafted binary files (EC_SIG) being used on AMD mainboards that hardcode the offset and must manually being checked if those match the FMAP. When there'll be additional firmware regions added it becomes unconvienient to maintain those by hand. Usage output: Usage: ./util/mec152x/mec152xtool -h|--help -f|--fmap_region_name Command: GEN_ECFW_PTR - Writes the ECFW PTR Based on https://chromium.googlesource.com/chromiumos/platform/ec/+/08f5a1e6fc2c9467230444ac9b582dcf4d9f0068/chip/mchp/util/pack_ec_mec172x.py Change-Id: I3b74c9f65643ad4437de29d4aed307b1a2b33286 Signed-off-by: Patrick Rudolph Reviewed-on: https://review.coreboot.org/c/coreboot/+/87428 Tested-by: build bot (Jenkins) Reviewed-by: Matt DeVillier --- util/mec152x/Makefile | 54 ++++++++++++++++++++ util/mec152x/Makefile.mk | 23 +++++++++ util/mec152x/description.md | 8 +++ util/mec152x/main.c | 99 +++++++++++++++++++++++++++++++++++++ util/mec152x/rom.c | 77 +++++++++++++++++++++++++++++ util/mec152x/rom.h | 10 ++++ util/mec152x/utils.c | 79 +++++++++++++++++++++++++++++ util/mec152x/utils.h | 20 ++++++++ 8 files changed, 370 insertions(+) create mode 100644 util/mec152x/Makefile create mode 100644 util/mec152x/Makefile.mk create mode 100644 util/mec152x/description.md create mode 100644 util/mec152x/main.c create mode 100644 util/mec152x/rom.c create mode 100644 util/mec152x/rom.h create mode 100644 util/mec152x/utils.c create mode 100644 util/mec152x/utils.h diff --git a/util/mec152x/Makefile b/util/mec152x/Makefile new file mode 100644 index 0000000000..43b350145c --- /dev/null +++ b/util/mec152x/Makefile @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +PRG := mec152xtool +TOP ?= $(abspath ../..) +ROOT := $(TOP)/src + +CC ?= $(CROSS_COMPILE)gcc +HOSTCC ?= $(CC) +INSTALL ?= /usr/bin/env install +PREFIX ?= /usr/local + +HOSTCFLAGS ?= $(CFLAGS) +HOSTCFLAGS += -Wall -Wextra -MMD -MP -O3 +HOSTCFLAGS += -I $(TOP)/util/cbfstool/flashmap/ +HOSTCFLAGS += -I $(ROOT)/commonlib/bsd/include + +HOSTLDFLAGS ?= $(LDFLAGS) + +# there files are in this directory +SRC := main.c utils.c rom.c +# and these are in $(TOP)/util/cbfstool/flashmap/ +SRC += fmap.c kv_pair.c valstr.c + +OBJ := $(SRC:.c=.o) +DEP := $(SRC:.c=.o.d) + +.PHONY: all debug clean install + +all: $(PRG) + +debug: HOSTCFLAGS += -O0 -g +debug: HOSTLDFLAGS += -g +debug: all + +install: $(PRG) + $(INSTALL) -d $(DESTDIR)$(PREFIX)/bin/ + $(INSTALL) $^ $(DESTDIR)$(PREFIX)/bin/ + +uninstall: + $(RM) $(DESTDIR)$(PREFIX)/bin/$(PRG) + +clean: + $(RM) -f $(PRG) $(OBJ) $(DEP) + +$(PRG): $(OBJ) + $(HOSTCC) -o $@ $^ $(HOSTLDFLAGS) + +%.o: %.c + $(HOSTCC) $(HOSTCFLAGS) -c -o $@ -MF $@.d $< + +%.o: $(TOP)/util/cbfstool/flashmap/%.c + $(HOSTCC) $(HOSTCFLAGS) -c -o $@ -MF $@.d $< + +-include $(DEP) diff --git a/util/mec152x/Makefile.mk b/util/mec152x/Makefile.mk new file mode 100644 index 0000000000..067fdb097f --- /dev/null +++ b/util/mec152x/Makefile.mk @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: BSD-3-Clause + +mec152xtoolobj = main.o utils.o rom.o fmap.o kv_pair.o valstr.o +mec152xheader = $(addprefix $(dir)/,utils.h rom.h) + +WERROR ?= -Wno-error +MEC152XCFLAGS := -Wno-array-bounds -Wextra -O3 -Wshadow $(WERROR) +MEC152XCFLAGS += -I $(top)/util/cbfstool/flashmap/ +MEC152XCFLAGS += -I $(top)/util/mec152x +MEC152XCFLAGS += -I $(top)/src/commonlib/bsd/include + +additional-dirs += $(objutil)/mec152x + +$(objutil)/mec152x/%.o: $(top)/util/mec152x/%.c $(mec152xheader) | $(objutil) + printf " MEC152X $@\n" + $(HOSTCC) $(MEC152XCFLAGS) $(HOSTCFLAGS) -c -o $@ $< + +$(objutil)/mec152x/%.o: $(top)/util/cbfstool/flashmap/%.c $(mec152xheader) | $(objutil) + $(HOSTCC) $(MEC152XCFLAGS) $(HOSTCFLAGS) -c -o $@ $< + +$(objutil)/mec152x/mec152xtool: $(addprefix $(objutil)/mec152x/,$(mec152xtoolobj)) $(mec152xheader) | $(objutil) + printf " MEC152X $@\n" + $(HOSTCC) $(addprefix $(objutil)/mec152x/,$(mec152xtoolobj)) $(LDFLAGS) -o $@ diff --git a/util/mec152x/description.md b/util/mec152x/description.md new file mode 100644 index 0000000000..c2ef805441 --- /dev/null +++ b/util/mec152x/description.md @@ -0,0 +1,8 @@ +Offline Microchip EC FW modification tool `C` + +Generates the EC firmware pointer at flash offset 0 based on +the FMAP and the specified FMAP region name. The EC will read +this pointer to find the EC firmware in the host SPI flash. + +This will overwrite the first 4 bytes of flash. The user must +be aware of potential conflicts such as with the Intel IFD. diff --git a/util/mec152x/main.c b/util/mec152x/main.c new file mode 100644 index 0000000000..569cc43300 --- /dev/null +++ b/util/mec152x/main.c @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rom.h" + +static const char *USAGE_FMT = "Usage: %s \n" + " -h|--help\n" + " -f|--fmap_region_name\n\n" + "Command:\n" + " GEN_ECFW_PTR - Writes the ECFW PTR\n"; + +static const char *program_name; + +static void print_program_usage(void) +{ + fprintf(stderr, USAGE_FMT, program_name); + exit(EXIT_FAILURE); +} + +#define ERROR(...) do { fprintf(stderr, "ERROR: " __VA_ARGS__); \ + print_program_usage(); } \ + while (0) + +static void print_help(void) +{ + printf(USAGE_FMT, program_name); +} + +static const struct option longopts[] = { + {"help", 0, 0, 'h'}, + {"fmap_region_name", 1, 0, 'f'}, + {NULL, 0, 0, 0} +}; + +int main(int argc, char *argv[]) +{ + char *fmap_region_name = NULL; + const char *rom_file = NULL; + const char *command = NULL; + + int opt, idx, ret = 0; + program_name = argv[0]; + + if (argc < 3) + ERROR("Not enough argument given\n\n"); + + while (!ret && + (opt = getopt_long(argc, argv, "?hf:", + longopts, &idx)) != -1) { + switch (opt) { + case 'h': + case '?': + print_help(); + exit(EXIT_SUCCESS); + break; + case 'f': + fmap_region_name = strdup(optarg); + break; + } + } + if (optind < argc) { + rom_file = argv[optind]; + optind++; + } + if (optind < argc) { + command = argv[optind]; + optind++; + } + + if (optind < argc) + ERROR("Unknown argument: %s\n\n", argv[optind]); + + if (!rom_file) + ERROR("No ROM specified\n\n"); + + if (!command) { + ERROR("No command specified\n\n"); + } else if (!strcmp(command, "GEN_ECFW_PTR")) { + if (!fmap_region_name) + ERROR("No fmap region specified\n\n"); + if (!rom_set_ec_fw_ptr(rom_file, fmap_region_name)) + ERROR("Failed to write EC FW PTR\n\n"); + } else { + ERROR("Unknown command specified: '%s'\n\n", command); + } + + if (fmap_region_name) + free(fmap_region_name); + + return ret; +} diff --git a/util/mec152x/rom.c b/util/mec152x/rom.c new file mode 100644 index 0000000000..2c61c6ce2b --- /dev/null +++ b/util/mec152x/rom.c @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include +#include +#include +#include "rom.h" +#include "utils.h" + +/* Generate EC FW PTR. EC FW must be 256 byte aligned and has a CRC-8 checksum */ +static uint32_t gen_signature(uint32_t rom_offset) +{ + uint32_t signature = rom_offset >> 8; + uint8_t crc = crc8_itu((uint8_t *)&signature, 3); + + signature |= ((uint32_t)crc) << 24; + + return signature; +} + +/** + * @brief Write the EC FW pointer to the ROM + * @param rom_file The filename of the ROM to update + * The ROM must contain a valid FMAP + * @param fmap_region_name FMAP region name containing the EC firmware + * @return True on success + * + * On MEC15x1 the firmware resides in the shared x86 SPI flash. As the EC + * boots first and does the power sequencing there's no concurrent access. + * The EC expects to find a pointer to the FW at offset 0. + */ +bool rom_set_ec_fw_ptr(const char rom_file[], const char *fmap_region_name) +{ + struct mem_range file = {0}; + bool ret = false; + + file = map_file(rom_file, true); + if (file.start == NULL) { + fprintf(stderr, "ERROR: Failed to load \"%s\"\n", rom_file); + return false; + } + + long fmap_offset = fmap_find(file.start, file.length); + if (fmap_offset >= 0) { + struct fmap *fmap = (void *)(file.start + fmap_offset); + const struct fmap_area *area = fmap_find_area(fmap, fmap_region_name); + if (area == NULL) { + fprintf(stderr, + "ERROR: Found FMAP without '%s' in \"%s\"\n\n", + fmap_region_name, rom_file); + printf("FMAP is:\n"); + fmap_print(fmap); + goto error; + } + + /* EC FW must be aligned */ + if (area->offset % 256) { + fprintf(stderr, + "ERROR: Region '%s' not aligned to 256 in \"%s\"\n", + fmap_region_name, rom_file); + goto error; + } + + /* EC FW pointer resides at offset 0 */ + uint32_t *ec_fw_ptr = (uint32_t *)file.start; + *ec_fw_ptr = htole32(gen_signature(area->offset)); + } else { + fprintf(stderr, "ERROR: FMAP not found \"%s\"\n", rom_file); + goto error; + } + + ret = true; +error: + unmap_file(file); + return ret; +} diff --git a/util/mec152x/rom.h b/util/mec152x/rom.h new file mode 100644 index 0000000000..0d7a3cb4a5 --- /dev/null +++ b/util/mec152x/rom.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef MEC15X1__ROM_H__ +#define MEC15X1__ROM_H__ + +#include + +bool rom_set_ec_fw_ptr(const char rom_file[], const char *fmap_region_name); + +#endif // MEC15X1__ROM_H__ diff --git a/util/mec152x/utils.c b/util/mec152x/utils.c new file mode 100644 index 0000000000..b4b0afd02d --- /dev/null +++ b/util/mec152x/utils.c @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "utils.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Return CRC-8 of the data, using x^8 x^2 x 1 polynomial. */ +uint8_t crc8_itu(const uint8_t *data, int len) +{ + unsigned int crc = 0; + int i, j; + + for (j = 0; j < len; j++, data++) { + crc ^= (*data << 8); + for (i = 8; i; i--) { + if (crc & 0x8000) + crc ^= (0x1070 << 3); + crc <<= 1; + } + } + + return (uint8_t)(crc >> 8) ^ 0x55; +} + +struct mem_range map_file(const char path[], bool rw) +{ + struct mem_range range = {0}; + + int open_flags = rw ? O_RDWR : O_RDONLY; + int mmap_flags = rw ? PROT_READ | PROT_WRITE : PROT_READ; + + int fd = open(path, open_flags); + if (fd == -1) { + fprintf(stderr, "Failed to open(): %s\n", strerror(errno)); + return range; + } + + struct stat stat_buf; + if (fstat(fd, &stat_buf) != 0) { + (void)close(fd); + fprintf(stderr, "Failed to fstat(): %s\n", strerror(errno)); + return range; + } + + if (stat_buf.st_size == 0) { + (void)close(fd); + fprintf(stderr, "Can't map an empty \"%s\" file\n", path); + return range; + } + + uint8_t *mem = mmap(/*addr=*/NULL, stat_buf.st_size, mmap_flags, + MAP_SHARED | MAP_POPULATE, fd, /*offset=*/0); + (void)close(fd); + if (mem == MAP_FAILED) { + fprintf(stderr, "Failed to mmap(): %s\n", strerror(errno)); + return range; + } + + range.start = mem; + range.length = stat_buf.st_size; + return range; +} + +void unmap_file(struct mem_range store) +{ + if (munmap(store.start, store.length) != 0) + fprintf(stderr, "Failed to munmap(): %s\n", strerror(errno)); +} diff --git a/util/mec152x/utils.h b/util/mec152x/utils.h new file mode 100644 index 0000000000..f586565e3b --- /dev/null +++ b/util/mec152x/utils.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef MEC152X__UTILS_H__ +#define MEC152X__UTILS_H__ + +#include +#include +#include + +struct mem_range { + uint8_t *start; + size_t length; +}; +uint8_t crc8_itu(const uint8_t *data, int len); + +struct mem_range map_file(const char path[], bool rw); + +void unmap_file(struct mem_range store); + +#endif // MEC152X__UTILS_H__