diff options
-rw-r--r-- | util/amdfwtool/.gitignore | 1 | ||||
-rw-r--r-- | util/amdfwtool/Makefile | 22 | ||||
-rw-r--r-- | util/amdfwtool/amdfwread.c | 237 | ||||
-rw-r--r-- | util/amdfwtool/amdfwtool.c | 55 | ||||
-rw-r--r-- | util/amdfwtool/amdfwtool.h | 8 | ||||
-rw-r--r-- | util/testing/Makefile.inc | 1 |
6 files changed, 286 insertions, 38 deletions
diff --git a/util/amdfwtool/.gitignore b/util/amdfwtool/.gitignore index cac1859b43..e688fcfa68 100644 --- a/util/amdfwtool/.gitignore +++ b/util/amdfwtool/.gitignore @@ -1 +1,2 @@ amdfwtool +amdfwread diff --git a/util/amdfwtool/Makefile b/util/amdfwtool/Makefile index 7b7793535a..bdc3dc3432 100644 --- a/util/amdfwtool/Makefile +++ b/util/amdfwtool/Makefile @@ -2,27 +2,33 @@ HOSTCC ?= cc -SRC = amdfwtool.c data_parse.c -OBJ = $(SRC:%.c=%.o) -TARGET = amdfwtool +READ_SRC = amdfwread.c +READ_OBJ = $(READ_SRC:%.c=%.o) +TOOL_SRC = amdfwtool.c data_parse.c +TOOL_OBJ = $(TOOL_SRC:%.c=%.o) +HEADER=amdfwtool.h +TARGETS = amdfwread amdfwtool WERROR=-Werror CFLAGS=-O2 -Wall -Wextra -Wshadow ${WERROR} -all: $(TARGET) +all: $(TARGETS) -$(TARGET): $(OBJ) - $(CC) $(OBJ) $(LDFLAGS) -o $@ +amdfwread: $(READ_OBJ) + $(CC) $(READ_OBJ) $(LDFLAGS) -o $@ + +amdfwtool: $(TOOL_OBJ) + $(CC) $(TOOL_OBJ) $(LDFLAGS) -o $@ %.o: %.c $(HEADER) $(CC) $(CFLAGS) -c -o $@ $< clean: - @rm -f $(TARGET) $(OBJ) + @rm -f $(TARGETS) $(READ_OBJ) $(TOOL_OBJ) distclean: clean help: - @echo "${TARGET}: Create AMD Firmware combination" + @echo "${TARGETS}: Tools to create and read from AMD firmware combinations" @echo "Targets: all, clean, distclean, help" @echo "To disable warnings as errors, run make as:" @echo " make all WERROR=\"\"" diff --git a/util/amdfwtool/amdfwread.c b/util/amdfwtool/amdfwread.c new file mode 100644 index 0000000000..1a95c5c267 --- /dev/null +++ b/util/amdfwtool/amdfwread.c @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#include <getopt.h> +#include <stddef.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include "amdfwtool.h" + +/* An address can be relative to the image/file start but it can also be the address when + * the image is mapped at 0xff000000. Used to ensure that we only attempt to read within + * the limits of the file. */ +#define FILE_REL_MASK 0xffffff + +#define ERR(...) fprintf(stderr, __VA_ARGS__) + +/* Possible locations for the header */ +const uint32_t fw_header_offsets[] = { + 0xfa0000, + 0xe20000, + 0xc20000, + 0x820000, + 0x020000, +}; + +/* Converts addresses to be relative to the start of the file */ +static uint64_t relative_offset(const psp_directory_header *header, uint32_t header_offset, + const psp_directory_entry *entry, size_t entry_index) +{ + amd_addr_mode mode = header->additional_info_fields.address_mode; + if (mode == AMD_ADDR_REL_BIOS) { + /* Entry address mode override directory mode with this value */ + mode = entry->address_mode; + } + + switch (mode) { + case AMD_ADDR_REL_BIOS: + return entry->addr + header_offset; + + case AMD_ADDR_REL_SLOT: + return entry->addr + header_offset + sizeof(psp_directory_header) + + entry_index * sizeof(psp_directory_entry); + + default: + break; + } + + return entry->addr & FILE_REL_MASK; +} + +static int read_fw_header(FILE *fw, uint32_t offset, embedded_firmware *fw_header) +{ + if (fseek(fw, offset, SEEK_SET) != 0) { + ERR("Failed to seek to fw header offset 0x%x\n", offset); + return 1; + } + + if (fread(fw_header, sizeof(embedded_firmware), 1, fw) != 1) { + ERR("Failed to read fw header at 0x%x\n", offset); + return 1; + } + + return fw_header->signature != EMBEDDED_FW_SIGNATURE; +} + +static int read_psp_directory(FILE *fw, uint32_t offset, uint32_t expected_cookie, + psp_directory_header *header, psp_directory_entry **entries, + size_t *num_entries) +{ + offset &= FILE_REL_MASK; + + if (fseek(fw, offset, SEEK_SET) != 0) { + ERR("Failed to seek to PSP header at 0x%x\n", offset); + return 1; + } + + if (fread(header, sizeof(psp_directory_header), 1, fw) != 1) { + ERR("Failed to read PSP header cookie\n"); + return 1; + } + + /* Ensure that we have a PSP directory */ + if (header->cookie != expected_cookie) { + ERR("Invalid PSP header cookie value found: 0x%x, expected: 0x%x\n", + expected_cookie, header->cookie); + return 1; + } + + /* Read the entries */ + *num_entries = header->num_entries; + *entries = malloc(sizeof(psp_directory_entry) * header->num_entries); + if (fread(*entries, sizeof(psp_directory_entry), header->num_entries, fw) + != header->num_entries) { + ERR("Failed to read %d PSP entries\n", header->num_entries); + return 1; + } + + return 0; +} + +static int read_soft_fuse(FILE *fw, const embedded_firmware *fw_header) +{ + psp_directory_entry *current_entries = NULL; + size_t num_current_entries = 0; + + uint32_t psp_offset = 0; + /* 0xffffffff indicates that the offset is in new_psp_directory */ + if (fw_header->psp_directory != 0xffffffff) + psp_offset = fw_header->psp_directory; + else + psp_offset = fw_header->new_psp_directory; + + psp_directory_header header; + if (read_psp_directory(fw, psp_offset, PSP_COOKIE, &header, + ¤t_entries, &num_current_entries) != 0) + return 1; + + while (1) { + uint32_t l2_dir_offset = 0; + + for (size_t i = 0; i < num_current_entries; i++) { + uint32_t type = current_entries[i].type; + if (type == AMD_PSP_FUSE_CHAIN) { + uint64_t mode = current_entries[i].address_mode; + uint64_t addr = current_entries[i].addr; + uint64_t fuse = mode << 62 | addr; + + printf("Soft-fuse:0x%lx\n", fuse); + return 0; + } else if (type == AMD_FW_L2_PTR) { + /* There's a second level PSP directory to read */ + if (l2_dir_offset != 0) + return 1; + + l2_dir_offset = relative_offset(&header, psp_offset, + ¤t_entries[i], i); + } + } + + free(current_entries); + + /* Didn't find an L2 PSP directory so we can stop */ + if (l2_dir_offset == 0) + break; + + /* Read the L2 PSP directory */ + if (read_psp_directory(fw, l2_dir_offset, PSPL2_COOKIE, &header, + ¤t_entries, &num_current_entries) != 0) + break; + } + + return 1; +} + +enum { + AMDFW_OPT_HELP = 'h', + + /* When bit 31 is set, options are a bitfield of info to print from the + firmware image. */ + AMDFW_OPT_SOFT_FUSE = 0xF0000001, +}; + +static char const optstring[] = {AMDFW_OPT_HELP}; + +static struct option long_options[] = { + {"help", no_argument, 0, AMDFW_OPT_HELP}, + {"soft-fuse", no_argument, 0, AMDFW_OPT_SOFT_FUSE}, +}; + +static void print_usage(void) +{ + printf("amdfwread: Examine AMD firmware images\n"); + printf("Usage: amdfwread [options] <file>\n"); + printf("--soft-fuse Print soft fuse value\n"); +} + +int main(int argc, char **argv) +{ + char *fw_file = NULL; + + int selected_functions = 0; + int index = 0; + while (1) { + int opt = getopt_long(argc, argv, optstring, long_options, &index); + + if (opt == -1) { + index++; + if (index >= argc) { + /* Print usage if we didn't get any arguments */ + print_usage(); + return 0; + } + + fw_file = argv[index]; + break; + } + + if (opt == AMDFW_OPT_HELP) { + print_usage(); + return 0; + } + + selected_functions |= opt; + } + + FILE *fw = fopen(fw_file, "rb"); + if (!fw) { + ERR("Failed to open FW file %s\n", fw_file); + return 1; + } + + /* Find the FW header by checking each possible location */ + embedded_firmware fw_header; + int found_header = 0; + for (size_t i = 0; i < ARRAY_SIZE(fw_header_offsets); i++) { + if (read_fw_header(fw, fw_header_offsets[i], &fw_header) == 0) { + found_header = 1; + break; + } + } + + if (!found_header) { + ERR("Failed to find FW header\n"); + fclose(fw); + return 1; + } + + if (selected_functions & AMDFW_OPT_SOFT_FUSE) { + if (read_soft_fuse(fw, &fw_header) != 0) { + fclose(fw); + return 1; + } + } + + fclose(fw); + return 0; +} diff --git a/util/amdfwtool/amdfwtool.c b/util/amdfwtool/amdfwtool.c index c58d1e9fc0..abc0a9eb9d 100644 --- a/util/amdfwtool/amdfwtool.c +++ b/util/amdfwtool/amdfwtool.c @@ -358,18 +358,13 @@ typedef struct _context { uint32_t current_table; } context; -#define ADDRESS_MODE_0_PHY 0 -#define ADDRESS_MODE_1_REL_BIOS 1 -#define ADDRESS_MODE_2_REL_TAB 2 -#define ADDRESS_MODE_3_REL_SLOT 3 - #define RUN_BASE(ctx) (0xFFFFFFFF - (ctx).rom_size + 1) #define RUN_OFFSET_MODE(ctx, offset, mode) \ - ((mode) == ADDRESS_MODE_0_PHY ? RUN_BASE(ctx) + (offset) : \ - ((mode) == ADDRESS_MODE_1_REL_BIOS ? (offset) : \ - ((mode) == ADDRESS_MODE_2_REL_TAB ? (offset) - ctx.current_table : (offset)))) + ((mode) == AMD_ADDR_PHYSICAL ? RUN_BASE(ctx) + (offset) : \ + ((mode) == AMD_ADDR_REL_BIOS ? (offset) : \ + ((mode) == AMD_ADDR_REL_TAB ? (offset) - ctx.current_table : (offset)))) #define RUN_OFFSET(ctx, offset) RUN_OFFSET_MODE((ctx), (offset), (ctx).address_mode) -#define RUN_TO_OFFSET(ctx, run) ((ctx).address_mode == ADDRESS_MODE_0_PHY ? \ +#define RUN_TO_OFFSET(ctx, run) ((ctx).address_mode == AMD_ADDR_PHYSICAL ? \ (run) - RUN_BASE(ctx) : (run)) /* TODO: */ #define RUN_CURRENT(ctx) RUN_OFFSET((ctx), (ctx).current) /* The mode in entry can not be higher than the header's. @@ -385,7 +380,7 @@ typedef struct _context { /* Only set the address mode in entry if the table is mode 2. */ #define SET_ADDR_MODE(table, mode) \ ((table)->header.additional_info_fields.address_mode == \ - ADDRESS_MODE_2_REL_TAB ? (mode) : 0) + AMD_ADDR_REL_TAB ? (mode) : 0) #define SET_ADDR_MODE_BY_TABLE(table) \ SET_ADDR_MODE((table), (table)->header.additional_info_fields.address_mode) @@ -696,7 +691,7 @@ static void integrate_psp_ab(context *ctx, psp_directory_table *pspdir, pspdir->entries[count].subprog = 0; pspdir->entries[count].rsvd = 0; if (ish != NULL) { - ish->pl2_location = BUFF_TO_RUN_MODE(*ctx, pspdir2, ADDRESS_MODE_1_REL_BIOS); + ish->pl2_location = BUFF_TO_RUN_MODE(*ctx, pspdir2, AMD_ADDR_REL_BIOS); ish->boot_priority = ab == AMD_FW_RECOVERYAB_A ? 0xFFFFFFFF : 1; ish->update_retry_count = 2; ish->glitch_retry_count = 0; @@ -704,15 +699,15 @@ static void integrate_psp_ab(context *ctx, psp_directory_table *pspdir, ish->checksum = fletcher32(&ish->boot_priority, sizeof(ish_directory_table) - sizeof(uint32_t)); pspdir->entries[count].addr = - BUFF_TO_RUN_MODE(*ctx, ish, ADDRESS_MODE_1_REL_BIOS); + BUFF_TO_RUN_MODE(*ctx, ish, AMD_ADDR_REL_BIOS); pspdir->entries[count].address_mode = - SET_ADDR_MODE(pspdir, ADDRESS_MODE_1_REL_BIOS); + SET_ADDR_MODE(pspdir, AMD_ADDR_REL_BIOS); pspdir->entries[count].size = TABLE_ALIGNMENT; } else { pspdir->entries[count].addr = - BUFF_TO_RUN_MODE(*ctx, pspdir2, ADDRESS_MODE_1_REL_BIOS); + BUFF_TO_RUN_MODE(*ctx, pspdir2, AMD_ADDR_REL_BIOS); pspdir->entries[count].address_mode = - SET_ADDR_MODE(pspdir, ADDRESS_MODE_1_REL_BIOS); + SET_ADDR_MODE(pspdir, AMD_ADDR_REL_BIOS); pspdir->entries[count].size = pspdir2->header.num_entries * sizeof(psp_directory_entry) + sizeof(psp_directory_header); @@ -814,9 +809,9 @@ static void integrate_psp_firmwares(context *ctx, pspdir->entries[count].size = ALIGN(bytes, ERASE_ALIGNMENT); pspdir->entries[count].addr = - RUN_CURRENT_MODE(*ctx, ADDRESS_MODE_1_REL_BIOS); + RUN_CURRENT_MODE(*ctx, AMD_ADDR_REL_BIOS); pspdir->entries[count].address_mode = - SET_ADDR_MODE(pspdir, ADDRESS_MODE_1_REL_BIOS); + SET_ADDR_MODE(pspdir, AMD_ADDR_REL_BIOS); ctx->current = ALIGN(ctx->current + bytes, BLOB_ERASE_ALIGNMENT); @@ -871,9 +866,9 @@ static void integrate_psp_firmwares(context *ctx, * sizeof(psp_directory_entry); pspdir->entries[count].addr = - BUFF_TO_RUN_MODE(*ctx, pspdir2, ADDRESS_MODE_1_REL_BIOS); + BUFF_TO_RUN_MODE(*ctx, pspdir2, AMD_ADDR_REL_BIOS); pspdir->entries[count].address_mode = - SET_ADDR_MODE(pspdir, ADDRESS_MODE_1_REL_BIOS); + SET_ADDR_MODE(pspdir, AMD_ADDR_REL_BIOS); count++; } @@ -1092,14 +1087,14 @@ static void integrate_bios_firmwares(context *ctx, /* If source is given, use that and its size */ biosdir->entries[count].source = fw_table[i].src; biosdir->entries[count].address_mode = - SET_ADDR_MODE(biosdir, ADDRESS_MODE_1_REL_BIOS); + SET_ADDR_MODE(biosdir, AMD_ADDR_REL_BIOS); biosdir->entries[count].size = fw_table[i].size; } else { /* Else reserve size bytes within amdfw.rom */ ctx->current = ALIGN(ctx->current, ERASE_ALIGNMENT); biosdir->entries[count].source = RUN_CURRENT(*ctx); biosdir->entries[count].address_mode = - SET_ADDR_MODE(biosdir, ADDRESS_MODE_1_REL_BIOS); + SET_ADDR_MODE(biosdir, AMD_ADDR_REL_BIOS); biosdir->entries[count].size = ALIGN( fw_table[i].size, ERASE_ALIGNMENT); memset(BUFF_CURRENT(*ctx), 0xff, @@ -1113,7 +1108,7 @@ static void integrate_bios_firmwares(context *ctx, if (level == BDT_LVL1 && locate_bdt2_bios(biosdir2, &source, &size)) { biosdir->entries[count].source = source; biosdir->entries[count].address_mode = - SET_ADDR_MODE(biosdir, ADDRESS_MODE_1_REL_BIOS); + SET_ADDR_MODE(biosdir, AMD_ADDR_REL_BIOS); biosdir->entries[count].size = size; break; } @@ -1121,7 +1116,7 @@ static void integrate_bios_firmwares(context *ctx, /* level 2, or level 1 and no copy found in level 2 */ biosdir->entries[count].source = fw_table[i].src; biosdir->entries[count].address_mode = - SET_ADDR_MODE(biosdir, ADDRESS_MODE_1_REL_BIOS); + SET_ADDR_MODE(biosdir, AMD_ADDR_REL_BIOS); biosdir->entries[count].dest = fw_table[i].dest; biosdir->entries[count].size = fw_table[i].size; @@ -1136,9 +1131,9 @@ static void integrate_bios_firmwares(context *ctx, } biosdir->entries[count].source = - RUN_CURRENT_MODE(*ctx, ADDRESS_MODE_1_REL_BIOS); + RUN_CURRENT_MODE(*ctx, AMD_ADDR_REL_BIOS); biosdir->entries[count].address_mode = - SET_ADDR_MODE(biosdir, ADDRESS_MODE_1_REL_BIOS); + SET_ADDR_MODE(biosdir, AMD_ADDR_REL_BIOS); ctx->current = ALIGN(ctx->current + bytes, 0x100U); break; @@ -1180,7 +1175,7 @@ static void integrate_bios_firmwares(context *ctx, biosdir->entries[count].source = BUFF_TO_RUN(*ctx, biosdir2); biosdir->entries[count].address_mode = - SET_ADDR_MODE(biosdir, ADDRESS_MODE_1_REL_BIOS); + SET_ADDR_MODE(biosdir, AMD_ADDR_REL_BIOS); biosdir->entries[count].subprog = 0; biosdir->entries[count].inst = 0; biosdir->entries[count].copy = 0; @@ -1861,13 +1856,13 @@ int main(int argc, char **argv) } if (cb_config.need_ish) - ctx.address_mode = ADDRESS_MODE_2_REL_TAB; + ctx.address_mode = AMD_ADDR_REL_TAB; else if (cb_config.second_gen) - ctx.address_mode = ADDRESS_MODE_1_REL_BIOS; + ctx.address_mode = AMD_ADDR_REL_BIOS; else - ctx.address_mode = ADDRESS_MODE_0_PHY; + ctx.address_mode = AMD_ADDR_PHYSICAL; printf(" AMDFWTOOL Using firmware directory location of %s address: 0x%08x\n", - ctx.address_mode == ADDRESS_MODE_0_PHY ? "absolute" : "relative", + ctx.address_mode == AMD_ADDR_PHYSICAL ? "absolute" : "relative", RUN_CURRENT(ctx)); integrate_firmwares(&ctx, amd_romsig, amd_fw_table); diff --git a/util/amdfwtool/amdfwtool.h b/util/amdfwtool/amdfwtool.h index 0bef43a6cd..d40e60d237 100644 --- a/util/amdfwtool/amdfwtool.h +++ b/util/amdfwtool/amdfwtool.h @@ -83,6 +83,13 @@ typedef enum _amd_bios_type { AMD_BIOS_SKIP } amd_bios_type; +typedef enum _amd_addr_mode { + AMD_ADDR_PHYSICAL = 0, /* Physical address */ + AMD_ADDR_REL_BIOS, /* Relative to beginning of image */ + AMD_ADDR_REL_TAB, /* Relative to table */ + AMD_ADDR_REL_SLOT, /* Relative to slot */ +} amd_addr_mode; + struct second_gen_efs { /* todo: expand for Server products */ int gen:1; /* Client products only use bit 0 */ int reserved:31; @@ -293,5 +300,6 @@ uint8_t process_config(FILE *config, amd_cb_config *cb_config, uint8_t print_dep #define LINE_EOF (1) #define LINE_TOO_LONG (2) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #endif /* _AMD_FW_TOOL_H_ */ diff --git a/util/testing/Makefile.inc b/util/testing/Makefile.inc index 5c9e4d85a9..5a81e3bf28 100644 --- a/util/testing/Makefile.inc +++ b/util/testing/Makefile.inc @@ -50,6 +50,7 @@ junit.xml: echo TOOLLIST= \ +amdfwread \ amdfwtool \ cbfstool \ cbmem \ |