util/amdtools: Add ec_usb_pd_fw
ec_usb_pd_fw is a board specific utility to generate pointers to firmware images found in the SPI flash. On some AMD boards the x86 SPI flash is shared with the EC. The EC can also update the USB Power Delivery controllers firmware, but it needs to know where to load the firmware from. It uses pointers stored in the first 128 bytes of the x86 SPI flash. Add a small utility to generate pointers to the USB PD firmware, located somewhere in the ROM identified by the FMAP region. There can be up to 12 USB PD firmwares, depending on the used vendor or model. Change-Id: I98717e849592f83eb7bacbfed33a8d4b811a5e18 Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/87430 Reviewed-by: David Hendricks <david.hendricks@gmail.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
parent
e63620012c
commit
5a0953614b
8 changed files with 400 additions and 0 deletions
55
util/amdtools/ec_usb_pd_fw/Makefile
Normal file
55
util/amdtools/ec_usb_pd_fw/Makefile
Normal file
|
|
@ -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)
|
||||
24
util/amdtools/ec_usb_pd_fw/Makefile.mk
Normal file
24
util/amdtools/ec_usb_pd_fw/Makefile.mk
Normal file
|
|
@ -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 $@
|
||||
8
util/amdtools/ec_usb_pd_fw/description.md
Normal file
8
util/amdtools/ec_usb_pd_fw/description.md
Normal file
|
|
@ -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.
|
||||
114
util/amdtools/ec_usb_pd_fw/main.c
Normal file
114
util/amdtools/ec_usb_pd_fw/main.c
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/* 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> [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;
|
||||
}
|
||||
108
util/amdtools/ec_usb_pd_fw/rom.c
Normal file
108
util/amdtools/ec_usb_pd_fw/rom.c
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/* 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"
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
10
util/amdtools/ec_usb_pd_fw/rom.h
Normal file
10
util/amdtools/ec_usb_pd_fw/rom.h
Normal file
|
|
@ -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 <stdbool.h>
|
||||
|
||||
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__
|
||||
61
util/amdtools/ec_usb_pd_fw/utils.c
Normal file
61
util/amdtools/ec_usb_pd_fw/utils.c
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* 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>
|
||||
|
||||
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/amdtools/ec_usb_pd_fw/utils.h
Normal file
20
util/amdtools/ec_usb_pd_fw/utils.h
Normal file
|
|
@ -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 <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 // EC_USB_PD_FW__UTILS_H__
|
||||
Loading…
Add table
Add a link
Reference in a new issue