diff --git a/util/amdtools/ec_usb_pd_fw/Makefile b/util/amdtools/ec_usb_pd_fw/Makefile new file mode 100644 index 0000000000..6049884a2f --- /dev/null +++ b/util/amdtools/ec_usb_pd_fw/Makefile @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +PRG := ec_usb_pd_fw +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) $(PRG) $(DESTDIR)$(PREFIX)/bin/ + $(RMDIR) $(DESTDIR)$(PREFIX)/bin/ + +clean: + -$(RM) $(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/amdtools/ec_usb_pd_fw/Makefile.mk b/util/amdtools/ec_usb_pd_fw/Makefile.mk new file mode 100644 index 0000000000..0905f8524d --- /dev/null +++ b/util/amdtools/ec_usb_pd_fw/Makefile.mk @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: BSD-3-Clause + +ec_usb_pd_fwtoolobj = main.o utils.o rom.o fmap.o kv_pair.o valstr.o +ec_usb_pd_fwheader = $(addprefix $(dir)/,utils.h rom.h) + +WERROR ?= -Wno-error +EC_USB_PD_FWCFLAGS ?= $(CFLAGS) +EC_USB_PD_FWCFLAGS += -Wno-array-bounds -Wextra -MMD -MP -O3 -Wshadow $(WERROR) +EC_USB_PD_FWCFLAGS += -I $(top)/util/cbfstool/flashmap/ +EC_USB_PD_FWCFLAGS += -I $(top)/util/amdtools/ec_usb_pd_fw +EC_USB_PD_FWCFLAGS += -I $(top)/src/commonlib/bsd/include + +additional-dirs += $(objutil)/ec_usb_pd_fw + +$(objutil)/ec_usb_pd_fw/%.o: $(top)/util/amdtools/ec_usb_pd_fw/%.c $(ec_usb_pd_fwheader) | $(objutil)/ec_usb_pd_fw + printf " PD_FWTOOL $@\n" + $(HOSTCC) $(EC_USB_PD_FWCFLAGS) $(HOSTCFLAGS) -c -o $@ $< + +$(objutil)/ec_usb_pd_fw/%.o: $(top)/util/cbfstool/flashmap/%.c $(ec_usb_pd_fwheader) | $(objutil)/ec_usb_pd_fw + $(HOSTCC) $(EC_USB_PD_FWCFLAGS) $(HOSTCFLAGS) -c -o $@ $< + +$(objutil)/ec_usb_pd_fw/ec_usb_pd_fw: $(addprefix $(objutil)/ec_usb_pd_fw/,$(ec_usb_pd_fwtoolobj)) $(ec_usb_pd_fwheader) | $(objutil)/ec_usb_pd_fw + printf " PD_FWTOOL $@\n" + $(HOSTCC) $(addprefix $(objutil)/ec_usb_pd_fw/,$(ec_usb_pd_fwtoolobj)) $(LDFLAGS) -o $@ diff --git a/util/amdtools/ec_usb_pd_fw/description.md b/util/amdtools/ec_usb_pd_fw/description.md new file mode 100644 index 0000000000..15e60e595e --- /dev/null +++ b/util/amdtools/ec_usb_pd_fw/description.md @@ -0,0 +1,8 @@ +Offline AMD Board specific USB Type-C PD FW modification tool `C` + +Generates pointers at flash offset 16 for the EC firmware to consume +by reading the FMAP and the provided FMAP region name. The EC will +load the USB PD firmware into the corresponding chip where necessary. + +This will overwrite parts of the flash without warning. The user must +be aware of potential conflicts such as with the Intel IFD. diff --git a/util/amdtools/ec_usb_pd_fw/main.c b/util/amdtools/ec_usb_pd_fw/main.c new file mode 100644 index 0000000000..9f239f46e2 --- /dev/null +++ b/util/amdtools/ec_usb_pd_fw/main.c @@ -0,0 +1,114 @@ +/* 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 [arguments]\n" + " -h|--help\n" + " -f|--fmap_region_name FMAP region\n" + " -s|--slot [0-11] Specify slot to use\n" + " -n|--next_free Use next free slot\n\n" + "Command:\n" + " GEN_PDFW_PTR - Writes the USB Type-C PD firmware 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'}, + {"next_free", 0, 0, 'n'}, + {"slot", 1, 0, 's'}, + {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 slot = -1; + bool next_free = false; + + 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:s:n", + longopts, &idx)) != -1) { + switch (opt) { + case 'h': + case '?': + print_help(); + exit(EXIT_SUCCESS); + break; + case 'n': + next_free = true; + break; + case 'f': + fmap_region_name = strdup(optarg); + break; + case 's': + slot = strtol(optarg, NULL, 0); + 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_PDFW_PTR")) { + if (!fmap_region_name) + ERROR("No fmap region specified\n\n"); + if (next_free && slot >= 0) + ERROR("Specified slot and auto allocation\n\n"); + if (!next_free && slot < 0) + ERROR("Didn't specify a slot and not auto allocating\n\n"); + if (slot > 11) + ERROR("Invalid slot specified\n\n"); + if (!rom_set_pd_fw_ptr(rom_file, fmap_region_name, slot, next_free)) + ERROR("Failed to write EC PD 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/amdtools/ec_usb_pd_fw/rom.c b/util/amdtools/ec_usb_pd_fw/rom.c new file mode 100644 index 0000000000..183eb6ac1d --- /dev/null +++ b/util/amdtools/ec_usb_pd_fw/rom.c @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include +#include +#include +#include "rom.h" +#include "utils.h" + +/** + * @brief Generate pointer to PD firmware. + * @param rom_offset Offset of the PD firmware + * @param size Size of the PD firmware + * + * Each pointer is 8 byte wide consists of: + * - 4 byte offset + * - 3 byte size + * - 1 byte checksum (all 8 bytes sum up to 0xff) + */ +static uint64_t gen_signature(uint32_t rom_offset, uint32_t size) +{ + uint64_t signature = rom_offset; + signature |= ((uint64_t)size & 0xffffffULL) << 32; + uint8_t crc = 0; + + for (int i = 0; i < 7; i++) + crc += (signature >> (i * 8)) & 0xff; + + signature |= (0xffULL - (uint64_t)crc) << 56; + + return signature; +} + +static bool check_signature(uint64_t signature) +{ + uint8_t crc = 0; + + for (int i = 0; i < 8; i++) + crc += (signature >> (i * 8)) & 0xff; + + return crc == 0xff; +} + +/** + * @brief Write the EC PD 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 PD firmware + * @return True on success + * + * The EC firmware copies the USB Type-C PD firmware from the x86 SPI flash + * to the corresponding controller when necessary. It expects to find a list + * of pointers at offset 10h. The ROM can contain multiple PD firmwares depending + * on the used IC vendor/familiy. + * + */ +bool rom_set_pd_fw_ptr(const char rom_file[], const char *fmap_region_name, int slot, bool automatic) +{ + uint64_t *ec_pd_fw_ptr; + 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; + } + + if (automatic) + slot = 0; + + 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 PD pointer resides at offset 0x10 */ + ec_pd_fw_ptr = (uint64_t *)(file.start + 0x10 + slot * 8); + + /* Find free slot if in auto mode */ + for (; automatic && check_signature(le64toh(*ec_pd_fw_ptr)) && slot < 12; slot++) + ec_pd_fw_ptr++; + + if (slot == 12) { + fprintf(stderr, "ERROR: No free slots left\n\n"); + goto error; + } + + *ec_pd_fw_ptr = htole64(gen_signature(area->offset, area->size)); + } 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/amdtools/ec_usb_pd_fw/rom.h b/util/amdtools/ec_usb_pd_fw/rom.h new file mode 100644 index 0000000000..452f2ecb1a --- /dev/null +++ b/util/amdtools/ec_usb_pd_fw/rom.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef EC_USB_PD_FW__ROM_H__ +#define EC_USB_PD_FW__ROM_H__ + +#include + +bool rom_set_pd_fw_ptr(const char rom_file[], const char *fmap_region_name, int slot, bool automatic); + +#endif // EC_USB_PD_FW__ROM_H__ diff --git a/util/amdtools/ec_usb_pd_fw/utils.c b/util/amdtools/ec_usb_pd_fw/utils.c new file mode 100644 index 0000000000..e196ff35a7 --- /dev/null +++ b/util/amdtools/ec_usb_pd_fw/utils.c @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "utils.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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/amdtools/ec_usb_pd_fw/utils.h b/util/amdtools/ec_usb_pd_fw/utils.h new file mode 100644 index 0000000000..f914e2ceb4 --- /dev/null +++ b/util/amdtools/ec_usb_pd_fw/utils.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef EC_USB_PD_FW__UTILS_H__ +#define EC_USB_PD_FW__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 // EC_USB_PD_FW__UTILS_H__