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__