blob: 24f76e8762e54e90fa980b47953aa22c4de10e26 [file] [log] [blame]
Zheng Baof080cd52023-03-22 12:50:36 +08001/* SPDX-License-Identifier: GPL-2.0-only */
2
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -06003#include <assert.h>
Zheng Baof080cd52023-03-22 12:50:36 +08004#include <fcntl.h>
5#include <errno.h>
6#include <limits.h>
7#include <openssl/sha.h>
8#include <stdio.h>
9#include <sys/stat.h>
10#include <unistd.h>
11#include <string.h>
12#include <stdlib.h>
13
14#include "amdfwtool.h"
15
16/* Defines related to hashing signed binaries */
17enum hash_header_ver {
18 HASH_HDR_V1 = 1,
Karthikeyan Ramasubramanianabaca2a2023-07-13 17:24:13 -060019 HASH_HDR_V2,
Zheng Baof080cd52023-03-22 12:50:36 +080020};
21/* Signature ID enums are defined by PSP based on the algorithm used. */
22enum signature_id {
23 SIG_ID_RSA2048,
24 SIG_ID_RSA4096 = 2,
25};
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -060026
Zheng Baof080cd52023-03-22 12:50:36 +080027#define HASH_FILE_SUFFIX ".hash"
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -060028struct psp_fw_hash_file_info {
29 int fd;
30 bool present;
31 struct psp_fw_hash_table hash_header;
32};
33static struct psp_fw_hash_file_info hash_files[MAX_NUM_HASH_TABLES];
Zheng Baof080cd52023-03-22 12:50:36 +080034
Karthikeyan Ramasubramanianabaca2a2023-07-13 17:24:13 -060035#define UUID_MAGIC "gpd.ta.appID"
36
Zheng Baof080cd52023-03-22 12:50:36 +080037static uint16_t get_psp_fw_type(enum platform soc_id, struct amd_fw_header *header)
38{
39 switch (soc_id) {
40 case PLATFORM_MENDOCINO:
41 case PLATFORM_PHOENIX:
42 case PLATFORM_GLINDA:
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;
46 default:
47 return header->fw_type;
48 }
49}
50
Karthikeyan Ramasubramanianabaca2a2023-07-13 17:24:13 -060051static void get_psp_fw_uuid(void *buf, size_t buf_len, uint8_t *uuid)
52{
53 void *ptr = memmem(buf, buf_len, UUID_MAGIC, strlen(UUID_MAGIC));
54
55 assert(ptr != NULL);
56
57 memcpy(uuid, ptr + strlen(UUID_MAGIC), UUID_LEN_BYTES);
58}
59
60static int add_single_sha(amd_fw_entry_hash *entry, void *buf, enum platform soc_id,
61 fwid_type_t fwid_type)
Zheng Baof080cd52023-03-22 12:50:36 +080062{
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;
68
69 if (len > header->size_total)
70 return -1;
71
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;
78 } else {
79 fprintf(stderr, "%s: Unknown signature id: 0x%08x\n",
80 __func__, header->sig_id);
81 return -1;
82 }
83
84 memcpy(entry->sha, hash, entry->sha_len);
Karthikeyan Ramasubramanianabaca2a2023-07-13 17:24:13 -060085 entry->fwid_type = fwid_type;
86 if (fwid_type == FWID_TYPE_UUID)
87 get_psp_fw_uuid(buf, header->size_total, entry->uuid);
88 else
89 entry->fw_id = get_psp_fw_type(soc_id, header);
Zheng Baof080cd52023-03-22 12:50:36 +080090 entry->subtype = header->fw_subtype;
91
92 return 0;
93}
94
95static int get_num_binaries(void *buf, size_t buf_size)
96{
97 struct amd_fw_header *header = (struct amd_fw_header *)buf;
98 size_t total_len = 0;
99 int num_binaries = 0;
100
101 while (total_len < buf_size) {
102 num_binaries++;
103 total_len += header->size_total;
104 header = (struct amd_fw_header *)(buf + total_len);
105 }
106
107 if (total_len != buf_size) {
108 fprintf(stderr, "Malformed binary\n");
109 return -1;
110 }
111 return num_binaries;
112}
113
114static int add_sha(amd_fw_entry *entry, void *buf, size_t buf_size, enum platform soc_id)
115{
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);
120
121 if (num_binaries <= 0)
122 return num_binaries;
123
Karthikeyan Ramasubramanianabaca2a2023-07-13 17:24:13 -0600124 entry->hash_entries = calloc(num_binaries, sizeof(amd_fw_entry_hash));
Zheng Baof080cd52023-03-22 12:50:36 +0800125 if (!entry->hash_entries) {
126 fprintf(stderr, "Error allocating memory to add FW hash\n");
127 return -1;
128 }
129 entry->num_hash_entries = num_binaries;
130
131 /* Iterate through each binary */
132 for (int i = 0; i < num_binaries; i++) {
Karthikeyan Ramasubramanianabaca2a2023-07-13 17:24:13 -0600133 if (add_single_sha(&entry->hash_entries[i], buf + total_len, soc_id,
134 entry->fwid_type)) {
Zheng Baof080cd52023-03-22 12:50:36 +0800135 free(entry->hash_entries);
136 return -1;
137 }
138 total_len += header->size_total;
139 header = (struct amd_fw_header *)(buf + total_len);
140 }
141
142 return 0;
143}
144
Karthikeyan Ramasubramanianabaca2a2023-07-13 17:24:13 -0600145static void write_one_psp_firmware_hash_entry(int fd, amd_fw_entry_hash *entry,
146 uint8_t hash_tbl_id)
Zheng Baof080cd52023-03-22 12:50:36 +0800147{
Zheng Baof080cd52023-03-22 12:50:36 +0800148 uint16_t subtype = entry->subtype;
149
Karthikeyan Ramasubramanianabaca2a2023-07-13 17:24:13 -0600150 if (hash_files[hash_tbl_id].hash_header.version == HASH_HDR_V2) {
151 write_or_fail(fd, entry->uuid, UUID_LEN_BYTES);
152 } else {
153 write_or_fail(fd, &entry->fw_id, sizeof(entry->fw_id));
154 write_or_fail(fd, &subtype, sizeof(subtype));
155 }
Zheng Baof080cd52023-03-22 12:50:36 +0800156 write_or_fail(fd, entry->sha, entry->sha_len);
157}
158
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600159static void open_psp_fw_hash_files(const char *file_prefix)
Zheng Baof080cd52023-03-22 12:50:36 +0800160{
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600161 size_t hash_file_strlen;
162 char *hash_file_name;
Zheng Baof080cd52023-03-22 12:50:36 +0800163
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600164 /* 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);
Zheng Baof080cd52023-03-22 12:50:36 +0800173 exit(-1);
174 }
175
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600176 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
178 expected. */
179 if (!hash_files[i].present)
180 break;
181
182 if (i)
183 snprintf(hash_file_name, hash_file_strlen, "%s.%d%s",
184 file_prefix, i, HASH_FILE_SUFFIX);
185 else
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);
189
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);
195 exit(-1);
196 }
197 }
198 free(hash_file_name);
199}
200
201static void close_psp_fw_hash_files(void)
202{
203 for (unsigned int i = 0; i < MAX_NUM_HASH_TABLES; i++) {
204 if (!hash_files[i].present)
205 break;
206
207 close(hash_files[i].fd);
208 }
209}
210
211static void write_psp_firmware_hash(amd_fw_entry *fw_table)
212{
213 uint8_t hash_tbl_id;
214
Zheng Baof080cd52023-03-22 12:50:36 +0800215 for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600216 hash_tbl_id = fw_table[i].hash_tbl_id;
217 assert(hash_files[hash_tbl_id].present);
218
Zheng Baof080cd52023-03-22 12:50:36 +0800219 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) {
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600221 hash_files[hash_tbl_id].hash_header.no_of_entries_256++;
Zheng Baof080cd52023-03-22 12:50:36 +0800222 } else if (fw_table[i].hash_entries[j].sha_len ==
223 SHA384_DIGEST_LENGTH) {
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600224 hash_files[hash_tbl_id].hash_header.no_of_entries_384++;
Zheng Baof080cd52023-03-22 12:50:36 +0800225 } 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);
228 exit(-1);
229 }
230 }
231 }
232
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600233 for (unsigned int i = 0; i < MAX_NUM_HASH_TABLES; i++) {
234 if (!hash_files[i].present)
235 continue;
236 write_or_fail(hash_files[i].fd, &hash_files[i].hash_header,
237 sizeof(hash_files[i].hash_header));
Karthikeyan Ramasubramanianabaca2a2023-07-13 17:24:13 -0600238 /* 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;
241
242 write_or_fail(hash_files[i].fd, &reserved, sizeof(reserved));
243 }
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600244 }
Zheng Baof080cd52023-03-22 12:50:36 +0800245
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++) {
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600250 hash_tbl_id = fw_table[i].hash_tbl_id;
Zheng Baof080cd52023-03-22 12:50:36 +0800251 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)
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600253 write_one_psp_firmware_hash_entry(hash_files[hash_tbl_id].fd,
Karthikeyan Ramasubramanianabaca2a2023-07-13 17:24:13 -0600254 &fw_table[i].hash_entries[j], hash_tbl_id);
Zheng Baof080cd52023-03-22 12:50:36 +0800255 }
256 }
257
258 for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600259 hash_tbl_id = fw_table[i].hash_tbl_id;
Zheng Baof080cd52023-03-22 12:50:36 +0800260 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)
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600262 write_one_psp_firmware_hash_entry(hash_files[hash_tbl_id].fd,
Karthikeyan Ramasubramanianabaca2a2023-07-13 17:24:13 -0600263 &fw_table[i].hash_entries[j], hash_tbl_id);
Zheng Baof080cd52023-03-22 12:50:36 +0800264 }
265 }
266
Zheng Baof080cd52023-03-22 12:50:36 +0800267 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)
269 continue;
270
271 free(fw_table[i].hash_entries);
272 fw_table[i].hash_entries = NULL;
273 fw_table[i].num_hash_entries = 0;
274 }
275}
276
Karthikeyan Ramasubramanianabaca2a2023-07-13 17:24:13 -0600277static void update_hash_files_config(amd_fw_entry *fw_table)
278{
279 uint16_t version = fw_table->fwid_type == FWID_TYPE_UUID ? HASH_HDR_V2 : HASH_HDR_V1;
280
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;
284}
285
Zheng Baof080cd52023-03-22 12:50:36 +0800286/**
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.
293 */
294void 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)
298{
299 unsigned int i;
300 int fd;
301 int signed_rom_fd;
302 ssize_t bytes, align_bytes;
303 uint8_t *buf;
Zheng Baof080cd52023-03-22 12:50:36 +0800304 struct amd_fw_header header;
305 struct stat fd_stat;
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];
309
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));
316 return;
317 }
318
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;
322
323 if (!(fw_table[i].filename) || fw_table[i].skip_hashing)
324 continue;
325
326 memset(&header, 0, sizeof(header));
327
328 fd = open(fw_table[i].filename, O_RDONLY);
329 if (fd < 0) {
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));
333 continue;
334 }
335
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));
339 close(fd);
340 continue;
341 }
342
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);
348 close(fd);
349 continue;
350 }
351
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);
356 close(fd);
357 continue;
358 }
359
360
361 /* PSP binary is not signed and should not be part of signed PSP binaries
362 set. */
363 if (header.sig_opt != 1) {
364 close(fd);
365 continue;
366 }
367
368 buf = malloc(fd_stat.st_size);
369 if (!buf) {
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);
373 close(fd);
374 continue;
375 }
376
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);
383 free(buf);
384 close(fd);
385 continue;
386 }
387
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);
393 free(buf);
394 close(fd);
395 continue;
396 }
397
398 /* Write Blob alignment bytes */
399 align_bytes = 0;
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);
408 free(buf);
409 close(fd);
410 continue;
411 }
412 }
413
414 if (add_sha(&fw_table[i], buf, fd_stat.st_size, soc_id))
415 exit(-1);
416
417 /* File is successfully processed and is part of signed PSP binaries set. */
Zheng Baof080cd52023-03-22 12:50:36 +0800418 fw_table[i].addr_signed = signed_start_addr;
419 fw_table[i].file_size = (uint32_t)fd_stat.st_size;
Karthikeyan Ramasubramanianabaca2a2023-07-13 17:24:13 -0600420 update_hash_files_config(&fw_table[i]);
Zheng Baof080cd52023-03-22 12:50:36 +0800421
422 signed_start_addr += fd_stat.st_size + align_bytes;
423
424 free(buf);
425 close(fd);
426 }
427
428 close(signed_rom_fd);
429
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600430 open_psp_fw_hash_files(signed_rom);
431 write_psp_firmware_hash(fw_table);
432 close_psp_fw_hash_files();
Zheng Baof080cd52023-03-22 12:50:36 +0800433}