1 /* SPDX-License-Identifier: GPL-2.0-only */
7 #include <openssl/sha.h>
14 #include "amdfwtool.h"
16 /* Defines related to hashing signed binaries */
17 enum hash_header_ver
{
21 /* Signature ID enums are defined by PSP based on the algorithm used. */
27 #define HASH_FILE_SUFFIX ".hash"
28 struct psp_fw_hash_file_info
{
31 struct psp_fw_hash_table hash_header
;
33 static struct psp_fw_hash_file_info hash_files
[MAX_NUM_HASH_TABLES
];
35 #define UUID_MAGIC "gpd.ta.appID"
37 static uint16_t get_psp_fw_type(enum platform soc_id
, struct amd_fw_header
*header
)
40 case PLATFORM_MENDOCINO
:
41 case PLATFORM_PHOENIX
:
43 /* Fallback to fw_type if fw_id is not populated, which serves the same
44 purpose on older SoCs. */
45 return header
->fw_id
? header
->fw_id
: header
->fw_type
;
47 return header
->fw_type
;
51 static void get_psp_fw_uuid(void *buf
, size_t buf_len
, uint8_t *uuid
)
53 void *ptr
= memmem(buf
, buf_len
, UUID_MAGIC
, strlen(UUID_MAGIC
));
57 memcpy(uuid
, ptr
+ strlen(UUID_MAGIC
), UUID_LEN_BYTES
);
60 static int add_single_sha(amd_fw_entry_hash
*entry
, void *buf
, enum platform soc_id
,
61 fwid_type_t fwid_type
)
63 uint8_t hash
[SHA384_DIGEST_LENGTH
];
64 struct amd_fw_header
*header
= (struct amd_fw_header
*)buf
;
65 /* Include only signed part for hash calculation. */
66 size_t len
= header
->fw_size_signed
+ sizeof(struct amd_fw_header
);
67 uint8_t *body
= (uint8_t *)buf
;
69 if (len
> header
->size_total
)
72 if (header
->sig_id
== SIG_ID_RSA4096
) {
73 SHA384(body
, len
, hash
);
74 entry
->sha_len
= SHA384_DIGEST_LENGTH
;
75 } else if (header
->sig_id
== SIG_ID_RSA2048
) {
76 SHA256(body
, len
, hash
);
77 entry
->sha_len
= SHA256_DIGEST_LENGTH
;
79 fprintf(stderr
, "%s: Unknown signature id: 0x%08x\n",
80 __func__
, header
->sig_id
);
84 memcpy(entry
->sha
, hash
, entry
->sha_len
);
85 entry
->fwid_type
= fwid_type
;
86 if (fwid_type
== FWID_TYPE_UUID
)
87 get_psp_fw_uuid(buf
, header
->size_total
, entry
->uuid
);
89 entry
->fw_id
= get_psp_fw_type(soc_id
, header
);
90 entry
->subtype
= header
->fw_subtype
;
95 static int get_num_binaries(void *buf
, size_t buf_size
)
97 struct amd_fw_header
*header
= (struct amd_fw_header
*)buf
;
101 while (total_len
< buf_size
) {
103 total_len
+= header
->size_total
;
104 header
= (struct amd_fw_header
*)(buf
+ total_len
);
107 if (total_len
!= buf_size
) {
108 fprintf(stderr
, "Malformed binary\n");
114 static int add_sha(amd_fw_entry
*entry
, void *buf
, size_t buf_size
, enum platform soc_id
)
116 struct amd_fw_header
*header
= (struct amd_fw_header
*)buf
;
117 /* Include only signed part for hash calculation. */
118 size_t total_len
= 0;
119 int num_binaries
= get_num_binaries(buf
, buf_size
);
121 if (num_binaries
<= 0)
124 entry
->hash_entries
= calloc(num_binaries
, sizeof(amd_fw_entry_hash
));
125 if (!entry
->hash_entries
) {
126 fprintf(stderr
, "Error allocating memory to add FW hash\n");
129 entry
->num_hash_entries
= num_binaries
;
131 /* Iterate through each binary */
132 for (int i
= 0; i
< num_binaries
; i
++) {
133 if (add_single_sha(&entry
->hash_entries
[i
], buf
+ total_len
, soc_id
,
135 free(entry
->hash_entries
);
138 total_len
+= header
->size_total
;
139 header
= (struct amd_fw_header
*)(buf
+ total_len
);
145 static void write_one_psp_firmware_hash_entry(int fd
, amd_fw_entry_hash
*entry
,
148 uint16_t subtype
= entry
->subtype
;
150 if (hash_files
[hash_tbl_id
].hash_header
.version
== HASH_HDR_V2
) {
151 write_or_fail(fd
, entry
->uuid
, UUID_LEN_BYTES
);
153 write_or_fail(fd
, &entry
->fw_id
, sizeof(entry
->fw_id
));
154 write_or_fail(fd
, &subtype
, sizeof(subtype
));
156 write_or_fail(fd
, entry
->sha
, entry
->sha_len
);
159 static void open_psp_fw_hash_files(const char *file_prefix
)
161 size_t hash_file_strlen
;
162 char *hash_file_name
;
164 /* Hash Table ID is part of the file name. For now only single digit ID is
165 supported and is sufficient. Hence assert MAX_NUM_HASH_TABLES < 10 before
166 constructing file name. Revisit later when > 10 hash tables are required. */
167 assert(MAX_NUM_HASH_TABLES
< 10);
168 /* file_prefix + ".[1-9]" + ".hash" + '\0' */
169 hash_file_strlen
= strlen(file_prefix
) + 2 + strlen(HASH_FILE_SUFFIX
) + 1;
170 hash_file_name
= malloc(hash_file_strlen
);
171 if (!hash_file_name
) {
172 fprintf(stderr
, "malloc(%lu) failed\n", hash_file_strlen
);
176 for (unsigned int i
= 0; i
< MAX_NUM_HASH_TABLES
; i
++) {
177 /* Hash table IDs are expected to be contiguous and hence holes are not
179 if (!hash_files
[i
].present
)
183 snprintf(hash_file_name
, hash_file_strlen
, "%s.%d%s",
184 file_prefix
, i
, HASH_FILE_SUFFIX
);
186 /* Default file name without number for backwards compatibility. */
187 snprintf(hash_file_name
, hash_file_strlen
, "%s%s",
188 file_prefix
, HASH_FILE_SUFFIX
);
190 hash_files
[i
].fd
= open(hash_file_name
, O_RDWR
| O_CREAT
| O_TRUNC
, 0666);
191 if (hash_files
[i
].fd
< 0) {
192 fprintf(stderr
, "Error opening file: %s: %s\n",
193 hash_file_name
, strerror(errno
));
194 free(hash_file_name
);
198 free(hash_file_name
);
201 static void close_psp_fw_hash_files(void)
203 for (unsigned int i
= 0; i
< MAX_NUM_HASH_TABLES
; i
++) {
204 if (!hash_files
[i
].present
)
207 close(hash_files
[i
].fd
);
211 static void write_psp_firmware_hash(amd_fw_entry
*fw_table
)
215 for (unsigned int i
= 0; fw_table
[i
].type
!= AMD_FW_INVALID
; i
++) {
216 hash_tbl_id
= fw_table
[i
].hash_tbl_id
;
217 assert(hash_files
[hash_tbl_id
].present
);
219 for (unsigned int j
= 0; j
< fw_table
[i
].num_hash_entries
; j
++) {
220 if (fw_table
[i
].hash_entries
[j
].sha_len
== SHA256_DIGEST_LENGTH
) {
221 hash_files
[hash_tbl_id
].hash_header
.no_of_entries_256
++;
222 } else if (fw_table
[i
].hash_entries
[j
].sha_len
==
223 SHA384_DIGEST_LENGTH
) {
224 hash_files
[hash_tbl_id
].hash_header
.no_of_entries_384
++;
225 } else if (fw_table
[i
].hash_entries
[j
].sha_len
) {
226 fprintf(stderr
, "%s: Error invalid sha_len %d\n",
227 __func__
, fw_table
[i
].hash_entries
[j
].sha_len
);
233 for (unsigned int i
= 0; i
< MAX_NUM_HASH_TABLES
; i
++) {
234 if (!hash_files
[i
].present
)
236 write_or_fail(hash_files
[i
].fd
, &hash_files
[i
].hash_header
,
237 sizeof(hash_files
[i
].hash_header
));
238 /* Add a reserved field as expected by version 2 header */
239 if (hash_files
[i
].hash_header
.version
== HASH_HDR_V2
) {
240 uint16_t reserved
= 0;
242 write_or_fail(hash_files
[i
].fd
, &reserved
, sizeof(reserved
));
246 /* Add all the SHA256 hash entries first followed by SHA384 entries. PSP verstage
247 processes the table in that order. Mixing and matching SHA256 and SHA384 entries
248 will cause the hash verification failure at run-time. */
249 for (unsigned int i
= 0; fw_table
[i
].type
!= AMD_FW_INVALID
; i
++) {
250 hash_tbl_id
= fw_table
[i
].hash_tbl_id
;
251 for (unsigned int j
= 0; j
< fw_table
[i
].num_hash_entries
; j
++) {
252 if (fw_table
[i
].hash_entries
[j
].sha_len
== SHA256_DIGEST_LENGTH
)
253 write_one_psp_firmware_hash_entry(hash_files
[hash_tbl_id
].fd
,
254 &fw_table
[i
].hash_entries
[j
], hash_tbl_id
);
258 for (unsigned int i
= 0; fw_table
[i
].type
!= AMD_FW_INVALID
; i
++) {
259 hash_tbl_id
= fw_table
[i
].hash_tbl_id
;
260 for (unsigned int j
= 0; j
< fw_table
[i
].num_hash_entries
; j
++) {
261 if (fw_table
[i
].hash_entries
[j
].sha_len
== SHA384_DIGEST_LENGTH
)
262 write_one_psp_firmware_hash_entry(hash_files
[hash_tbl_id
].fd
,
263 &fw_table
[i
].hash_entries
[j
], hash_tbl_id
);
267 for (unsigned int i
= 0; fw_table
[i
].type
!= AMD_FW_INVALID
; i
++) {
268 if (!fw_table
[i
].num_hash_entries
|| !fw_table
[i
].hash_entries
)
271 free(fw_table
[i
].hash_entries
);
272 fw_table
[i
].hash_entries
= NULL
;
273 fw_table
[i
].num_hash_entries
= 0;
277 static void update_hash_files_config(amd_fw_entry
*fw_table
)
279 uint16_t version
= fw_table
->fwid_type
== FWID_TYPE_UUID
? HASH_HDR_V2
: HASH_HDR_V1
;
281 hash_files
[fw_table
->hash_tbl_id
].present
= true;
282 if (version
> hash_files
[fw_table
->hash_tbl_id
].hash_header
.version
)
283 hash_files
[fw_table
->hash_tbl_id
].hash_header
.version
= version
;
287 * process_signed_psp_firmwares() - Process the signed PSP binaries to keep them separate
288 * @signed_rom: Output file path grouping all the signed PSP binaries.
289 * @fw_table: Table of all the PSP firmware entries/binaries to be processed.
290 * @signed_start_addr: Offset of the FMAP section, within the flash device, to hold
291 * the signed PSP binaries.
292 * @soc_id: SoC ID of the PSP binaries.
294 void process_signed_psp_firmwares(const char *signed_rom
,
295 amd_fw_entry
*fw_table
,
296 uint64_t signed_start_addr
,
297 enum platform soc_id
)
302 ssize_t bytes
, align_bytes
;
304 struct amd_fw_header header
;
306 /* Every blob in amdfw*.rom has to start at address aligned to 0x100. Prepare an
307 alignment data with 0xff to pad the blobs and meet the alignment requirement. */
308 uint8_t align_data
[BLOB_ALIGNMENT
- 1];
310 memset(align_data
, 0xff, sizeof(align_data
));
311 signed_rom_fd
= open(signed_rom
, O_RDWR
| O_CREAT
| O_TRUNC
,
312 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
| S_IROTH
| S_IWOTH
);
313 if (signed_rom_fd
< 0) {
314 fprintf(stderr
, "Error opening file: %s: %s\n",
315 signed_rom
, strerror(errno
));
319 for (i
= 0; fw_table
[i
].type
!= AMD_FW_INVALID
; i
++) {
320 fw_table
[i
].num_hash_entries
= 0;
321 fw_table
[i
].hash_entries
= NULL
;
323 if (!(fw_table
[i
].filename
) || fw_table
[i
].skip_hashing
)
326 memset(&header
, 0, sizeof(header
));
328 fd
= open(fw_table
[i
].filename
, O_RDONLY
);
330 /* Keep the file along with set of unsigned PSP binaries & continue. */
331 fprintf(stderr
, "Error opening file: %s: %s\n",
332 fw_table
[i
].filename
, strerror(errno
));
336 if (fstat(fd
, &fd_stat
)) {
337 /* Keep the file along with set of unsigned PSP binaries & continue. */
338 fprintf(stderr
, "fstat error: %s\n", strerror(errno
));
343 bytes
= read_from_file_to_buf(fd
, &header
, sizeof(struct amd_fw_header
));
344 if (bytes
!= (ssize_t
)sizeof(struct amd_fw_header
)) {
345 /* Keep the file along with set of unsigned PSP binaries & continue. */
346 fprintf(stderr
, "%s: Error reading header from %s\n",
347 __func__
, fw_table
[i
].filename
);
352 /* If firmware header looks like invalid, assume it's not signed */
353 if (!header
.fw_type
&& !header
.fw_id
) {
354 fprintf(stderr
, "%s: Invalid FWID for %s\n",
355 __func__
, fw_table
[i
].filename
);
361 /* PSP binary is not signed and should not be part of signed PSP binaries
363 if (header
.sig_opt
!= 1) {
368 buf
= malloc(fd_stat
.st_size
);
370 /* Keep the file along with set of unsigned PSP binaries & continue. */
371 fprintf(stderr
, "%s: failed to allocate memory with size %lld\n",
372 __func__
, (long long)fd_stat
.st_size
);
377 lseek(fd
, SEEK_SET
, 0);
378 bytes
= read_from_file_to_buf(fd
, buf
, fd_stat
.st_size
);
379 if (bytes
!= fd_stat
.st_size
) {
380 /* Keep the file along with set of unsigned PSP binaries & continue. */
381 fprintf(stderr
, "%s: failed to read %s\n",
382 __func__
, fw_table
[i
].filename
);
388 bytes
= write_from_buf_to_file(signed_rom_fd
, buf
, fd_stat
.st_size
);
389 if (bytes
!= fd_stat
.st_size
) {
390 /* Keep the file along with set of unsigned PSP binaries & continue. */
391 fprintf(stderr
, "%s: failed to write %s\n",
392 __func__
, fw_table
[i
].filename
);
398 /* Write Blob alignment bytes */
400 if (fd_stat
.st_size
& (BLOB_ALIGNMENT
- 1)) {
401 align_bytes
= BLOB_ALIGNMENT
-
402 (fd_stat
.st_size
& (BLOB_ALIGNMENT
- 1));
403 bytes
= write_from_buf_to_file(signed_rom_fd
, align_data
, align_bytes
);
404 if (bytes
!= align_bytes
) {
405 fprintf(stderr
, "%s: failed to write alignment data for %s\n",
406 __func__
, fw_table
[i
].filename
);
407 lseek(signed_rom_fd
, SEEK_CUR
, -fd_stat
.st_size
);
414 if (add_sha(&fw_table
[i
], buf
, fd_stat
.st_size
, soc_id
))
417 /* File is successfully processed and is part of signed PSP binaries set. */
418 fw_table
[i
].addr_signed
= signed_start_addr
;
419 fw_table
[i
].file_size
= (uint32_t)fd_stat
.st_size
;
420 update_hash_files_config(&fw_table
[i
]);
422 signed_start_addr
+= fd_stat
.st_size
+ align_bytes
;
428 close(signed_rom_fd
);
430 open_psp_fw_hash_files(signed_rom
);
431 write_psp_firmware_hash(fw_table
);
432 close_psp_fw_hash_files();