diff options
-rw-r--r-- | util/amdfwtool/amdfwread.c | 221 |
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, + ¤t_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, + ¤t_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; } |