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 <rom-file> <command>
-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 <patrick.rudolph@9elements.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/87428
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Matt DeVillier <matt.devillier@gmail.com>
This commit is contained in:
parent
0b5ce9d9f0
commit
e63620012c
8 changed files with 370 additions and 0 deletions
54
util/mec152x/Makefile
Normal file
54
util/mec152x/Makefile
Normal file
|
|
@ -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)
|
||||
23
util/mec152x/Makefile.mk
Normal file
23
util/mec152x/Makefile.mk
Normal file
|
|
@ -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 $@
|
||||
8
util/mec152x/description.md
Normal file
8
util/mec152x/description.md
Normal file
|
|
@ -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.
|
||||
99
util/mec152x/main.c
Normal file
99
util/mec152x/main.c
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "rom.h"
|
||||
|
||||
static const char *USAGE_FMT = "Usage: %s <rom-file> <command>\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;
|
||||
}
|
||||
77
util/mec152x/rom.c
Normal file
77
util/mec152x/rom.c
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <assert.h>
|
||||
#include <commonlib/bsd/compiler.h>
|
||||
#include <endian.h>
|
||||
#include <fmap.h>
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
||||
10
util/mec152x/rom.h
Normal file
10
util/mec152x/rom.h
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef MEC15X1__ROM_H__
|
||||
#define MEC15X1__ROM_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool rom_set_ec_fw_ptr(const char rom_file[], const char *fmap_region_name);
|
||||
|
||||
#endif // MEC15X1__ROM_H__
|
||||
79
util/mec152x/utils.c
Normal file
79
util/mec152x/utils.c
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* 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));
|
||||
}
|
||||
20
util/mec152x/utils.h
Normal file
20
util/mec152x/utils.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef MEC152X__UTILS_H__
|
||||
#define MEC152X__UTILS_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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__
|
||||
Loading…
Add table
Add a link
Reference in a new issue