blob: 1a95c5c2677a9a1017dcf9039e30f2f081ce44ef [file] [log] [blame]
Robert Zieba29bc79f2022-03-14 15:59:12 -06001/* SPDX-License-Identifier: GPL-2.0-only */
2#include <getopt.h>
3#include <stddef.h>
4#include <stdio.h>
5#include <stdint.h>
6#include <stdlib.h>
7#include <unistd.h>
8#include "amdfwtool.h"
9
10/* An address can be relative to the image/file start but it can also be the address when
11 * the image is mapped at 0xff000000. Used to ensure that we only attempt to read within
12 * the limits of the file. */
13#define FILE_REL_MASK 0xffffff
14
15#define ERR(...) fprintf(stderr, __VA_ARGS__)
16
17/* Possible locations for the header */
18const uint32_t fw_header_offsets[] = {
19 0xfa0000,
20 0xe20000,
21 0xc20000,
22 0x820000,
23 0x020000,
24};
25
26/* Converts addresses to be relative to the start of the file */
27static uint64_t relative_offset(const psp_directory_header *header, uint32_t header_offset,
28 const psp_directory_entry *entry, size_t entry_index)
29{
30 amd_addr_mode mode = header->additional_info_fields.address_mode;
31 if (mode == AMD_ADDR_REL_BIOS) {
32 /* Entry address mode override directory mode with this value */
33 mode = entry->address_mode;
34 }
35
36 switch (mode) {
37 case AMD_ADDR_REL_BIOS:
38 return entry->addr + header_offset;
39
40 case AMD_ADDR_REL_SLOT:
41 return entry->addr + header_offset + sizeof(psp_directory_header) +
42 entry_index * sizeof(psp_directory_entry);
43
44 default:
45 break;
46 }
47
48 return entry->addr & FILE_REL_MASK;
49}
50
51static int read_fw_header(FILE *fw, uint32_t offset, embedded_firmware *fw_header)
52{
53 if (fseek(fw, offset, SEEK_SET) != 0) {
54 ERR("Failed to seek to fw header offset 0x%x\n", offset);
55 return 1;
56 }
57
58 if (fread(fw_header, sizeof(embedded_firmware), 1, fw) != 1) {
59 ERR("Failed to read fw header at 0x%x\n", offset);
60 return 1;
61 }
62
63 return fw_header->signature != EMBEDDED_FW_SIGNATURE;
64}
65
66static int read_psp_directory(FILE *fw, uint32_t offset, uint32_t expected_cookie,
67 psp_directory_header *header, psp_directory_entry **entries,
68 size_t *num_entries)
69{
70 offset &= FILE_REL_MASK;
71
72 if (fseek(fw, offset, SEEK_SET) != 0) {
73 ERR("Failed to seek to PSP header at 0x%x\n", offset);
74 return 1;
75 }
76
77 if (fread(header, sizeof(psp_directory_header), 1, fw) != 1) {
78 ERR("Failed to read PSP header cookie\n");
79 return 1;
80 }
81
82 /* Ensure that we have a PSP directory */
83 if (header->cookie != expected_cookie) {
84 ERR("Invalid PSP header cookie value found: 0x%x, expected: 0x%x\n",
85 expected_cookie, header->cookie);
86 return 1;
87 }
88
89 /* Read the entries */
90 *num_entries = header->num_entries;
91 *entries = malloc(sizeof(psp_directory_entry) * header->num_entries);
92 if (fread(*entries, sizeof(psp_directory_entry), header->num_entries, fw)
93 != header->num_entries) {
94 ERR("Failed to read %d PSP entries\n", header->num_entries);
95 return 1;
96 }
97
98 return 0;
99}
100
101static int read_soft_fuse(FILE *fw, const embedded_firmware *fw_header)
102{
103 psp_directory_entry *current_entries = NULL;
104 size_t num_current_entries = 0;
105
106 uint32_t psp_offset = 0;
107 /* 0xffffffff indicates that the offset is in new_psp_directory */
108 if (fw_header->psp_directory != 0xffffffff)
109 psp_offset = fw_header->psp_directory;
110 else
111 psp_offset = fw_header->new_psp_directory;
112
113 psp_directory_header header;
114 if (read_psp_directory(fw, psp_offset, PSP_COOKIE, &header,
115 &current_entries, &num_current_entries) != 0)
116 return 1;
117
118 while (1) {
119 uint32_t l2_dir_offset = 0;
120
121 for (size_t i = 0; i < num_current_entries; i++) {
122 uint32_t type = current_entries[i].type;
123 if (type == AMD_PSP_FUSE_CHAIN) {
124 uint64_t mode = current_entries[i].address_mode;
125 uint64_t addr = current_entries[i].addr;
126 uint64_t fuse = mode << 62 | addr;
127
128 printf("Soft-fuse:0x%lx\n", fuse);
129 return 0;
130 } else if (type == AMD_FW_L2_PTR) {
131 /* There's a second level PSP directory to read */
132 if (l2_dir_offset != 0)
133 return 1;
134
135 l2_dir_offset = relative_offset(&header, psp_offset,
136 &current_entries[i], i);
137 }
138 }
139
140 free(current_entries);
141
142 /* Didn't find an L2 PSP directory so we can stop */
143 if (l2_dir_offset == 0)
144 break;
145
146 /* Read the L2 PSP directory */
147 if (read_psp_directory(fw, l2_dir_offset, PSPL2_COOKIE, &header,
148 &current_entries, &num_current_entries) != 0)
149 break;
150 }
151
152 return 1;
153}
154
155enum {
156 AMDFW_OPT_HELP = 'h',
157
158 /* When bit 31 is set, options are a bitfield of info to print from the
159 firmware image. */
160 AMDFW_OPT_SOFT_FUSE = 0xF0000001,
161};
162
163static char const optstring[] = {AMDFW_OPT_HELP};
164
165static struct option long_options[] = {
166 {"help", no_argument, 0, AMDFW_OPT_HELP},
167 {"soft-fuse", no_argument, 0, AMDFW_OPT_SOFT_FUSE},
168};
169
170static void print_usage(void)
171{
172 printf("amdfwread: Examine AMD firmware images\n");
173 printf("Usage: amdfwread [options] <file>\n");
174 printf("--soft-fuse Print soft fuse value\n");
175}
176
177int main(int argc, char **argv)
178{
179 char *fw_file = NULL;
180
181 int selected_functions = 0;
182 int index = 0;
183 while (1) {
184 int opt = getopt_long(argc, argv, optstring, long_options, &index);
185
186 if (opt == -1) {
187 index++;
188 if (index >= argc) {
189 /* Print usage if we didn't get any arguments */
190 print_usage();
191 return 0;
192 }
193
194 fw_file = argv[index];
195 break;
196 }
197
198 if (opt == AMDFW_OPT_HELP) {
199 print_usage();
200 return 0;
201 }
202
203 selected_functions |= opt;
204 }
205
206 FILE *fw = fopen(fw_file, "rb");
207 if (!fw) {
208 ERR("Failed to open FW file %s\n", fw_file);
209 return 1;
210 }
211
212 /* Find the FW header by checking each possible location */
213 embedded_firmware fw_header;
214 int found_header = 0;
215 for (size_t i = 0; i < ARRAY_SIZE(fw_header_offsets); i++) {
216 if (read_fw_header(fw, fw_header_offsets[i], &fw_header) == 0) {
217 found_header = 1;
218 break;
219 }
220 }
221
222 if (!found_header) {
223 ERR("Failed to find FW header\n");
224 fclose(fw);
225 return 1;
226 }
227
228 if (selected_functions & AMDFW_OPT_SOFT_FUSE) {
229 if (read_soft_fuse(fw, &fw_header) != 0) {
230 fclose(fw);
231 return 1;
232 }
233 }
234
235 fclose(fw);
236 return 0;
237}