1 /* SPDX-License-Identifier: GPL-2.0-only */
11 /* An address can be relative to the image/file start but it can also be the address when
12 * the image is mapped at 0xff000000. Used to ensure that we only attempt to read within
13 * the limits of the file. */
14 #define SPI_ROM_BASE 0xff000000
15 #define FILE_REL_MASK 0xffffff
17 #define ERR(...) fprintf(stderr, __VA_ARGS__)
19 /* Possible locations for the header */
20 const uint32_t fw_header_offsets
[] = {
28 /* Converts addresses to be relative to the start of the file */
29 static uint64_t relative_offset(uint32_t header_offset
, uint64_t addr
, uint64_t mode
)
32 /* Since this utility operates on the BIOS file, physical address is converted
33 relative to the start of the BIOS file. */
34 case AMD_ADDR_PHYSICAL
:
35 if (addr
< SPI_ROM_BASE
|| addr
> (SPI_ROM_BASE
+ FILE_REL_MASK
)) {
36 ERR("Invalid address(%lx) or mode(%lx)\n", addr
, mode
);
37 /* TODO: fix amdfwtool to program the right address/mode. In guybrush,
38 * lots of addresses are marked as physical, but they are relative to
39 * BIOS. Until that is fixed, just leave an error message. */
42 return addr
& FILE_REL_MASK
;
44 case AMD_ADDR_REL_BIOS
:
45 if (addr
> FILE_REL_MASK
) {
46 ERR("Invalid address(%lx) or mode(%lx)\n", addr
, mode
);
49 return addr
& FILE_REL_MASK
;
51 case AMD_ADDR_REL_TAB
:
52 return addr
+ header_offset
;
55 ERR("Unsupported mode %lu\n", mode
);
60 static int read_header(FILE *fw
, uint32_t offset
, void *header
, size_t header_size
)
62 if (fseek(fw
, offset
, SEEK_SET
) != 0) {
63 ERR("Failed to seek to file offset 0x%x\n", offset
);
67 if (fread(header
, header_size
, 1, fw
) != 1) {
68 ERR("Failed to read header at 0x%x\n", offset
);
75 static int read_fw_header(FILE *fw
, uint32_t offset
, embedded_firmware
*fw_header
)
77 if (read_header(fw
, offset
, fw_header
, sizeof(embedded_firmware
))) {
78 ERR("Failed to read fw header at 0x%x\n", offset
);
82 return fw_header
->signature
!= EMBEDDED_FW_SIGNATURE
;
85 static int read_psp_directory(FILE *fw
, uint32_t offset
, uint32_t expected_cookie
,
86 psp_directory_header
*header
, psp_directory_entry
**entries
,
89 offset
&= FILE_REL_MASK
;
91 if (read_header(fw
, offset
, header
, sizeof(psp_directory_header
))) {
92 ERR("Failed to read PSP header\n");
96 /* Ensure that we have a PSP directory */
97 if (header
->cookie
!= expected_cookie
) {
98 ERR("Invalid PSP header cookie value found: 0x%x, expected: 0x%x\n",
99 header
->cookie
, expected_cookie
);
103 /* Read the entries */
104 *num_entries
= header
->num_entries
;
105 *entries
= malloc(sizeof(psp_directory_entry
) * header
->num_entries
);
106 if (fread(*entries
, sizeof(psp_directory_entry
), header
->num_entries
, fw
)
107 != header
->num_entries
) {
108 ERR("Failed to read %d PSP entries\n", header
->num_entries
);
115 static int read_ish_directory(FILE *fw
, uint32_t offset
, ish_directory_table
*table
)
117 return read_header(fw
, offset
& FILE_REL_MASK
, table
, sizeof(*table
));
120 static int read_bios_directory(FILE *fw
, uint32_t offset
, uint32_t expected_cookie
,
121 bios_directory_hdr
*header
, bios_directory_entry
**entries
,
124 offset
&= FILE_REL_MASK
;
126 if (read_header(fw
, offset
, header
, sizeof(bios_directory_hdr
))) {
127 ERR("Failed to read BIOS header\n");
131 /* Ensure that we have a BIOS directory */
132 if (header
->cookie
!= expected_cookie
) {
133 ERR("Invalid BIOS header cookie value found: 0x%x, expected: 0x%x\n",
134 header
->cookie
, expected_cookie
);
138 /* Read the entries */
139 *num_entries
= header
->num_entries
;
140 *entries
= malloc(sizeof(bios_directory_entry
) * header
->num_entries
);
141 if (fread(*entries
, sizeof(bios_directory_entry
), header
->num_entries
, fw
)
142 != header
->num_entries
) {
143 ERR("Failed to read %d BIOS entries\n", header
->num_entries
);
150 static int read_soft_fuse(FILE *fw
, const embedded_firmware
*fw_header
)
152 psp_directory_entry
*current_entries
= NULL
;
153 size_t num_current_entries
= 0;
155 uint32_t psp_offset
= 0;
156 /* 0xffffffff indicates that the offset is in new_psp_directory */
157 if (fw_header
->psp_directory
!= 0xffffffff)
158 psp_offset
= fw_header
->psp_directory
;
160 psp_offset
= fw_header
->new_psp_directory
;
162 psp_directory_header header
;
163 if (read_psp_directory(fw
, psp_offset
, PSP_COOKIE
, &header
,
164 ¤t_entries
, &num_current_entries
) != 0)
168 uint32_t l2_dir_offset
= 0;
169 uint32_t ish_dir_offset
;
170 ish_directory_table ish_dir
;
172 for (size_t i
= 0; i
< num_current_entries
; i
++) {
173 uint32_t type
= current_entries
[i
].type
;
174 uint64_t mode
= current_entries
[i
].address_mode
;
175 uint64_t addr
= current_entries
[i
].addr
;
179 case AMD_PSP_FUSE_CHAIN
:
180 fuse
= mode
<< 62 | addr
;
182 printf("Soft-fuse:0x%lx\n", fuse
);
183 free(current_entries
);
187 /* There's a second level PSP directory to read */
188 if (l2_dir_offset
!= 0) {
189 ERR("Duplicate PSP L2 Entry, prior offset: %08x\n",
191 free(current_entries
);
195 l2_dir_offset
= relative_offset(psp_offset
, addr
, mode
);
198 case AMD_FW_RECOVERYAB_A
:
199 if (l2_dir_offset
!= 0) {
200 ERR("Duplicate PSP L2 Entry, prior offset: %08x\n",
202 free(current_entries
);
206 ish_dir_offset
= relative_offset(psp_offset
, addr
, mode
);
207 if (read_ish_directory(fw
, ish_dir_offset
, &ish_dir
) != 0) {
208 ERR("Error reading ISH directory\n");
209 free(current_entries
);
213 l2_dir_offset
= ish_dir
.pl2_location
;
217 /* No-op, continue to the next entry. */
222 free(current_entries
);
224 /* Didn't find an L2 PSP directory so we can stop */
225 if (l2_dir_offset
== 0)
228 /* Read the L2 PSP directory */
229 if (read_psp_directory(fw
, l2_dir_offset
, PSPL2_COOKIE
, &header
,
230 ¤t_entries
, &num_current_entries
) != 0)
237 #define MAX_NUM_LEVELS 10
238 #define MAX_INDENT_PER_LEVEL 4
239 #define MAX_INDENTATION_LEN (MAX_NUM_LEVELS * MAX_INDENT_PER_LEVEL + 1)
240 static void do_indentation_string(char *dest
, uint8_t level
)
242 for (uint8_t i
= 0; i
< level
&& i
< MAX_NUM_LEVELS
; i
++)
244 strcat(dest
, "+-->");
247 static int amdfw_bios_dir_walk(FILE *fw
, uint32_t bios_offset
, uint32_t cookie
, uint8_t level
)
249 bios_directory_entry
*current_entries
= NULL
;
250 size_t num_current_entries
= 0;
251 bios_directory_hdr header
;
252 uint32_t l2_dir_offset
= 0;
253 char indent
[MAX_INDENTATION_LEN
] = {0};
255 if (read_bios_directory(fw
, bios_offset
, cookie
, &header
,
256 ¤t_entries
, &num_current_entries
) != 0)
259 do_indentation_string(indent
, level
);
260 for (size_t i
= 0; i
< num_current_entries
; i
++) {
261 uint32_t type
= current_entries
[i
].type
;
262 uint64_t mode
= current_entries
[i
].address_mode
;
263 uint64_t addr
= current_entries
[i
].source
;
265 if (type
== AMD_BIOS_APOB
|| type
== AMD_BIOS_PSP_SHARED_MEM
)
266 printf("%sBIOS%s: 0x%02x 0x%lx(DRAM-Address)\n",
267 indent
, cookie
== BHD_COOKIE
? "L1" : "L2",
268 type
, current_entries
[i
].dest
);
269 else if (type
== AMD_BIOS_APOB_NV
)
270 printf("%sBIOS%s: 0x%02x 0x%08lx 0x%08x\n",
271 indent
, cookie
== BHD_COOKIE
? "L1" : "L2",
272 type
, relative_offset(bios_offset
, addr
, AMD_ADDR_PHYSICAL
),
273 current_entries
[i
].size
);
275 printf("%sBIOS%s: 0x%02x 0x%08lx 0x%08x\n",
276 indent
, cookie
== BHD_COOKIE
? "L1" : "L2",
277 type
, relative_offset(bios_offset
, addr
, mode
),
278 current_entries
[i
].size
);
280 if (type
== AMD_BIOS_L2_PTR
) {
281 /* There's a second level BIOS directory to read */
282 if (l2_dir_offset
!= 0) {
283 ERR("Duplicate BIOS L2 Entry, prior offset: %08x\n",
285 free(current_entries
);
289 l2_dir_offset
= relative_offset(bios_offset
, addr
, mode
);
290 printf(" %sBIOSL2: Dir 0x%08x\n", indent
, l2_dir_offset
);
291 amdfw_bios_dir_walk(fw
, l2_dir_offset
, BHDL2_COOKIE
, level
+ 2);
295 free(current_entries
);
299 static int amdfw_psp_dir_walk(FILE *fw
, uint32_t psp_offset
, uint32_t cookie
, uint8_t level
)
301 psp_directory_entry
*current_entries
= NULL
;
302 size_t num_current_entries
= 0;
303 psp_directory_header header
;
304 uint32_t l2_dir_offset
= 0;
305 uint32_t bios_dir_offset
= 0;
306 uint32_t ish_dir_offset
= 0;
307 ish_directory_table ish_dir
;
308 char indent
[MAX_INDENTATION_LEN
] = {0};
310 if (read_psp_directory(fw
, psp_offset
, cookie
, &header
,
311 ¤t_entries
, &num_current_entries
) != 0)
314 do_indentation_string(indent
, level
);
315 for (size_t i
= 0; i
< num_current_entries
; i
++) {
316 uint32_t type
= current_entries
[i
].type
;
317 uint64_t mode
= current_entries
[i
].address_mode
;
318 uint64_t addr
= current_entries
[i
].addr
;
320 if (type
== AMD_PSP_FUSE_CHAIN
)
321 printf("%sPSP%s: 0x%02x 0x%lx(Soft-fuse)\n",
322 indent
, cookie
== PSP_COOKIE
? "L1" : "L2",
323 type
, mode
<< 62 | addr
);
325 printf("%sPSP%s: 0x%02x 0x%08lx 0x%08x\n",
326 indent
, cookie
== PSP_COOKIE
? "L1" : "L2",
327 type
, relative_offset(psp_offset
, addr
, mode
),
328 current_entries
[i
].size
);
332 /* There's a second level PSP directory to read */
333 if (l2_dir_offset
!= 0) {
334 ERR("Duplicate PSP L2 Entry, prior offset: %08x\n",
336 free(current_entries
);
340 l2_dir_offset
= relative_offset(psp_offset
, addr
, mode
);
341 printf(" %sPSPL2: Dir 0x%08x\n", indent
, l2_dir_offset
);
342 amdfw_psp_dir_walk(fw
, l2_dir_offset
, PSPL2_COOKIE
, level
+ 2);
345 case AMD_FW_RECOVERYAB_A
:
346 if (l2_dir_offset
!= 0) {
347 ERR("Duplicate PSP L2 Entry, prior offset: %08x\n",
349 free(current_entries
);
353 ish_dir_offset
= relative_offset(psp_offset
, addr
, mode
);
354 if (read_ish_directory(fw
, ish_dir_offset
, &ish_dir
) != 0) {
355 ERR("Error reading ISH directory\n");
356 free(current_entries
);
360 l2_dir_offset
= ish_dir
.pl2_location
;
361 printf(" %sPSPL2: Dir 0x%08x\n", indent
, l2_dir_offset
);
362 amdfw_psp_dir_walk(fw
, l2_dir_offset
, PSPL2_COOKIE
, level
+ 2);
365 case AMD_FW_BIOS_TABLE
:
366 bios_dir_offset
= relative_offset(psp_offset
, addr
, mode
);
367 printf(" %sBIOSL2: Dir 0x%08x\n", indent
, bios_dir_offset
);
368 amdfw_bios_dir_walk(fw
, bios_dir_offset
, BHDL2_COOKIE
, level
+ 2);
372 /* No additional processing required, continue to the next entry. */
377 free(current_entries
);
381 static int list_amdfw_psp_dir(FILE *fw
, const embedded_firmware
*fw_header
)
383 uint32_t psp_offset
= 0;
385 /* 0xffffffff indicates that the offset is in new_psp_directory */
386 if (fw_header
->psp_directory
!= 0xffffffff)
387 psp_offset
= fw_header
->psp_directory
;
389 psp_offset
= fw_header
->new_psp_directory
;
391 printf("PSPL1: Dir 0x%08x\n", psp_offset
);
392 amdfw_psp_dir_walk(fw
, psp_offset
, PSP_COOKIE
, 0);
396 static int list_amdfw_bios_dir(FILE *fw
, const embedded_firmware
*fw_header
)
398 /* 0xffffffff implies that the SoC uses recovery A/B layout. Only BIOS L2 directory
399 is present and that too as part of PSP L2 directory. */
400 if (fw_header
->bios3_entry
!= 0xffffffff) {
401 printf("BIOSL1: Dir 0x%08x\n", fw_header
->bios3_entry
);
402 amdfw_bios_dir_walk(fw
, fw_header
->bios3_entry
, BHD_COOKIE
, 0);
408 static int list_amdfw_ro(FILE *fw
, const embedded_firmware
*fw_header
)
410 printf("Table: FW Offset Size\n");
411 list_amdfw_psp_dir(fw
, fw_header
);
412 list_amdfw_bios_dir(fw
, fw_header
);
417 AMDFW_OPT_HELP
= 'h',
418 AMDFW_OPT_SOFT_FUSE
= 1UL << 0, /* Print Softfuse */
419 AMDFW_OPT_RO_LIST
= 1UL << 1, /* List entries in AMDFW RO */
422 static char const optstring
[] = {AMDFW_OPT_HELP
};
424 static struct option long_options
[] = {
425 {"help", no_argument
, 0, AMDFW_OPT_HELP
},
426 {"soft-fuse", no_argument
, 0, AMDFW_OPT_SOFT_FUSE
},
427 {"ro-list", no_argument
, 0, AMDFW_OPT_RO_LIST
},
430 static void print_usage(void)
432 printf("amdfwread: Examine AMD firmware images\n");
433 printf("Usage: amdfwread [options] <file>\n");
434 printf("--soft-fuse Print soft fuse value\n");
435 printf("--ro-list List the programs under AMDFW in RO region\n");
438 int main(int argc
, char **argv
)
440 char *fw_file
= NULL
;
442 int selected_functions
= 0;
444 int opt
= getopt_long(argc
, argv
, optstring
, long_options
, NULL
);
447 if (optind
!= (argc
- 1)) {
448 /* Print usage if one and only one option i.e. filename is
454 fw_file
= argv
[optind
];
463 case AMDFW_OPT_SOFT_FUSE
:
464 case AMDFW_OPT_RO_LIST
:
465 selected_functions
|= opt
;
473 FILE *fw
= fopen(fw_file
, "rb");
475 ERR("Failed to open FW file %s\n", fw_file
);
479 /* Find the FW header by checking each possible location */
480 embedded_firmware fw_header
;
481 int found_header
= 0;
482 for (size_t i
= 0; i
< ARRAY_SIZE(fw_header_offsets
); i
++) {
483 if (read_fw_header(fw
, fw_header_offsets
[i
], &fw_header
) == 0) {
490 ERR("Failed to find FW header\n");
495 if (selected_functions
& AMDFW_OPT_SOFT_FUSE
) {
496 if (read_soft_fuse(fw
, &fw_header
) != 0) {
502 if (selected_functions
& AMDFW_OPT_RO_LIST
) {
503 if (list_amdfw_ro(fw
, &fw_header
) != 0) {