summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--util/amdfwtool/.gitignore1
-rw-r--r--util/amdfwtool/Makefile22
-rw-r--r--util/amdfwtool/amdfwread.c237
-rw-r--r--util/amdfwtool/amdfwtool.c55
-rw-r--r--util/amdfwtool/amdfwtool.h8
-rw-r--r--util/testing/Makefile.inc1
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,
+ &current_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,
+ &current_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,
+ &current_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 \