summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--util/amdfwtool/amdfwread.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/util/amdfwtool/amdfwread.c b/util/amdfwtool/amdfwread.c
index a65fb26738..711b1582fd 100644
--- a/util/amdfwtool/amdfwread.c
+++ b/util/amdfwtool/amdfwread.c
@@ -4,6 +4,7 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#include "amdfwtool.h"
@@ -116,6 +117,36 @@ static int read_ish_directory(FILE *fw, uint32_t offset, ish_directory_table *ta
return read_header(fw, offset & FILE_REL_MASK, table, sizeof(*table));
}
+static int read_bios_directory(FILE *fw, uint32_t offset, uint32_t expected_cookie,
+ bios_directory_hdr *header, bios_directory_entry **entries,
+ size_t *num_entries)
+{
+ offset &= FILE_REL_MASK;
+
+ if (read_header(fw, offset, header, sizeof(bios_directory_hdr))) {
+ ERR("Failed to read BIOS header\n");
+ return 1;
+ }
+
+ /* Ensure that we have a BIOS directory */
+ if (header->cookie != expected_cookie) {
+ ERR("Invalid BIOS header cookie value found: 0x%x, expected: 0x%x\n",
+ header->cookie, expected_cookie);
+ return 1;
+ }
+
+ /* Read the entries */
+ *num_entries = header->num_entries;
+ *entries = malloc(sizeof(bios_directory_entry) * header->num_entries);
+ if (fread(*entries, sizeof(bios_directory_entry), header->num_entries, fw)
+ != header->num_entries) {
+ ERR("Failed to read %d BIOS 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;
@@ -203,9 +234,189 @@ static int read_soft_fuse(FILE *fw, const embedded_firmware *fw_header)
return 1;
}
+#define MAX_NUM_LEVELS 10
+#define MAX_INDENT_PER_LEVEL 4
+#define MAX_INDENTATION_LEN (MAX_NUM_LEVELS * MAX_INDENT_PER_LEVEL + 1)
+static void do_indentation_string(char *dest, uint8_t level)
+{
+ for (uint8_t i = 0; i < level && i < MAX_NUM_LEVELS; i++)
+ strcat(dest, " ");
+ strcat(dest, "+-->");
+}
+
+static int amdfw_bios_dir_walk(FILE *fw, uint32_t bios_offset, uint32_t cookie, uint8_t level)
+{
+ bios_directory_entry *current_entries = NULL;
+ size_t num_current_entries = 0;
+ bios_directory_hdr header;
+ uint32_t l2_dir_offset = 0;
+ char indent[MAX_INDENTATION_LEN] = {0};
+
+ if (read_bios_directory(fw, bios_offset, cookie, &header,
+ &current_entries, &num_current_entries) != 0)
+ return 1;
+
+ do_indentation_string(indent, level);
+ for (size_t i = 0; i < num_current_entries; i++) {
+ uint32_t type = current_entries[i].type;
+ uint64_t mode = current_entries[i].address_mode;
+ uint64_t addr = current_entries[i].source;
+
+ if (type == AMD_BIOS_APOB || type == AMD_BIOS_PSP_SHARED_MEM)
+ printf("%sBIOS%s: 0x%02x 0x%lx(DRAM-Address)\n",
+ indent, cookie == BHD_COOKIE ? "L1" : "L2",
+ type, current_entries[i].dest);
+ else if (type == AMD_BIOS_APOB_NV)
+ printf("%sBIOS%s: 0x%02x 0x%08lx 0x%08x\n",
+ indent, cookie == BHD_COOKIE ? "L1" : "L2",
+ type, relative_offset(bios_offset, addr, AMD_ADDR_PHYSICAL),
+ current_entries[i].size);
+ else
+ printf("%sBIOS%s: 0x%02x 0x%08lx 0x%08x\n",
+ indent, cookie == BHD_COOKIE ? "L1" : "L2",
+ type, relative_offset(bios_offset, addr, mode),
+ current_entries[i].size);
+
+ if (type == AMD_BIOS_L2_PTR) {
+ /* There's a second level BIOS directory to read */
+ if (l2_dir_offset != 0) {
+ ERR("Duplicate BIOS L2 Entry, prior offset: %08x\n",
+ l2_dir_offset);
+ free(current_entries);
+ return 1;
+ }
+
+ l2_dir_offset = relative_offset(bios_offset, addr, mode);
+ printf(" %sBIOSL2: Dir 0x%08x\n", indent, l2_dir_offset);
+ amdfw_bios_dir_walk(fw, l2_dir_offset, BHDL2_COOKIE, level + 2);
+ }
+ }
+
+ free(current_entries);
+ return 0;
+}
+
+static int amdfw_psp_dir_walk(FILE *fw, uint32_t psp_offset, uint32_t cookie, uint8_t level)
+{
+ psp_directory_entry *current_entries = NULL;
+ size_t num_current_entries = 0;
+ psp_directory_header header;
+ uint32_t l2_dir_offset = 0;
+ uint32_t bios_dir_offset = 0;
+ uint32_t ish_dir_offset = 0;
+ ish_directory_table ish_dir;
+ char indent[MAX_INDENTATION_LEN] = {0};
+
+ if (read_psp_directory(fw, psp_offset, cookie, &header,
+ &current_entries, &num_current_entries) != 0)
+ return 1;
+
+ do_indentation_string(indent, level);
+ for (size_t i = 0; i < num_current_entries; i++) {
+ uint32_t type = current_entries[i].type;
+ uint64_t mode = current_entries[i].address_mode;
+ uint64_t addr = current_entries[i].addr;
+
+ if (type == AMD_PSP_FUSE_CHAIN)
+ printf("%sPSP%s: 0x%02x 0x%lx(Soft-fuse)\n",
+ indent, cookie == PSP_COOKIE ? "L1" : "L2",
+ type, mode << 62 | addr);
+ else
+ printf("%sPSP%s: 0x%02x 0x%08lx 0x%08x\n",
+ indent, cookie == PSP_COOKIE ? "L1" : "L2",
+ type, relative_offset(psp_offset, addr, mode),
+ current_entries[i].size);
+
+ switch (type) {
+ case AMD_FW_L2_PTR:
+ /* There's a second level PSP directory to read */
+ if (l2_dir_offset != 0) {
+ ERR("Duplicate PSP L2 Entry, prior offset: %08x\n",
+ l2_dir_offset);
+ free(current_entries);
+ return 1;
+ }
+
+ l2_dir_offset = relative_offset(psp_offset, addr, mode);
+ printf(" %sPSPL2: Dir 0x%08x\n", indent, l2_dir_offset);
+ amdfw_psp_dir_walk(fw, l2_dir_offset, PSPL2_COOKIE, level + 2);
+ break;
+
+ case AMD_FW_RECOVERYAB_A:
+ if (l2_dir_offset != 0) {
+ ERR("Duplicate PSP L2 Entry, prior offset: %08x\n",
+ l2_dir_offset);
+ free(current_entries);
+ return 1;
+ }
+
+ ish_dir_offset = relative_offset(psp_offset, addr, mode);
+ if (read_ish_directory(fw, ish_dir_offset, &ish_dir) != 0) {
+ ERR("Error reading ISH directory\n");
+ free(current_entries);
+ return 1;
+ }
+
+ l2_dir_offset = ish_dir.pl2_location;
+ printf(" %sPSPL2: Dir 0x%08x\n", indent, l2_dir_offset);
+ amdfw_psp_dir_walk(fw, l2_dir_offset, PSPL2_COOKIE, level + 2);
+ break;
+
+ case AMD_FW_BIOS_TABLE:
+ bios_dir_offset = relative_offset(psp_offset, addr, mode);
+ printf(" %sBIOSL2: Dir 0x%08x\n", indent, bios_dir_offset);
+ amdfw_bios_dir_walk(fw, bios_dir_offset, BHDL2_COOKIE, level + 2);
+ break;
+
+ default:
+ /* No additional processing required, continue to the next entry. */
+ break;
+ }
+ }
+
+ free(current_entries);
+ return 0;
+}
+
+static int list_amdfw_psp_dir(FILE *fw, const embedded_firmware *fw_header)
+{
+ 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;
+
+ printf("PSPL1: Dir 0x%08x\n", psp_offset);
+ amdfw_psp_dir_walk(fw, psp_offset, PSP_COOKIE, 0);
+ return 0;
+}
+
+static int list_amdfw_bios_dir(FILE *fw, const embedded_firmware *fw_header)
+{
+ /* 0xffffffff implies that the SoC uses recovery A/B layout. Only BIOS L2 directory
+ is present and that too as part of PSP L2 directory. */
+ if (fw_header->bios3_entry != 0xffffffff) {
+ printf("BIOSL1: Dir 0x%08x\n", fw_header->bios3_entry);
+ amdfw_bios_dir_walk(fw, fw_header->bios3_entry, BHD_COOKIE, 0);
+ }
+ return 0;
+}
+
+
+static int list_amdfw_ro(FILE *fw, const embedded_firmware *fw_header)
+{
+ printf("Table: FW Offset Size\n");
+ list_amdfw_psp_dir(fw, fw_header);
+ list_amdfw_bios_dir(fw, fw_header);
+ return 0;
+}
+
enum {
AMDFW_OPT_HELP = 'h',
AMDFW_OPT_SOFT_FUSE = 1UL << 0, /* Print Softfuse */
+ AMDFW_OPT_RO_LIST = 1UL << 1, /* List entries in AMDFW RO */
};
static char const optstring[] = {AMDFW_OPT_HELP};
@@ -213,6 +424,7 @@ 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},
+ {"ro-list", no_argument, 0, AMDFW_OPT_RO_LIST},
};
static void print_usage(void)
@@ -220,6 +432,7 @@ 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");
+ printf("--ro-list List the programs under AMDFW in RO region\n");
}
int main(int argc, char **argv)
@@ -248,6 +461,7 @@ int main(int argc, char **argv)
return 0;
case AMDFW_OPT_SOFT_FUSE:
+ case AMDFW_OPT_RO_LIST:
selected_functions |= opt;
break;
@@ -285,6 +499,13 @@ int main(int argc, char **argv)
}
}
+ if (selected_functions & AMDFW_OPT_RO_LIST) {
+ if (list_amdfw_ro(fw, &fw_header) != 0) {
+ fclose(fw);
+ return 1;
+ }
+ }
+
fclose(fw);
return 0;
}