blob: 2e19f72002fb4dd30fd734f133ce133c0b6808b7 [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,
19};
20/* Signature ID enums are defined by PSP based on the algorithm used. */
21enum signature_id {
22 SIG_ID_RSA2048,
23 SIG_ID_RSA4096 = 2,
24};
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -060025
Zheng Baof080cd52023-03-22 12:50:36 +080026#define HASH_FILE_SUFFIX ".hash"
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -060027struct psp_fw_hash_file_info {
28 int fd;
29 bool present;
30 struct psp_fw_hash_table hash_header;
31};
32static struct psp_fw_hash_file_info hash_files[MAX_NUM_HASH_TABLES];
Zheng Baof080cd52023-03-22 12:50:36 +080033
34static uint16_t get_psp_fw_type(enum platform soc_id, struct amd_fw_header *header)
35{
36 switch (soc_id) {
37 case PLATFORM_MENDOCINO:
38 case PLATFORM_PHOENIX:
39 case PLATFORM_GLINDA:
40 /* Fallback to fw_type if fw_id is not populated, which serves the same
41 purpose on older SoCs. */
42 return header->fw_id ? header->fw_id : header->fw_type;
43 default:
44 return header->fw_type;
45 }
46}
47
48static int add_single_sha(amd_fw_entry_hash *entry, void *buf, enum platform soc_id)
49{
50 uint8_t hash[SHA384_DIGEST_LENGTH];
51 struct amd_fw_header *header = (struct amd_fw_header *)buf;
52 /* Include only signed part for hash calculation. */
53 size_t len = header->fw_size_signed + sizeof(struct amd_fw_header);
54 uint8_t *body = (uint8_t *)buf;
55
56 if (len > header->size_total)
57 return -1;
58
59 if (header->sig_id == SIG_ID_RSA4096) {
60 SHA384(body, len, hash);
61 entry->sha_len = SHA384_DIGEST_LENGTH;
62 } else if (header->sig_id == SIG_ID_RSA2048) {
63 SHA256(body, len, hash);
64 entry->sha_len = SHA256_DIGEST_LENGTH;
65 } else {
66 fprintf(stderr, "%s: Unknown signature id: 0x%08x\n",
67 __func__, header->sig_id);
68 return -1;
69 }
70
71 memcpy(entry->sha, hash, entry->sha_len);
72 entry->fw_id = get_psp_fw_type(soc_id, header);
73 entry->subtype = header->fw_subtype;
74
75 return 0;
76}
77
78static int get_num_binaries(void *buf, size_t buf_size)
79{
80 struct amd_fw_header *header = (struct amd_fw_header *)buf;
81 size_t total_len = 0;
82 int num_binaries = 0;
83
84 while (total_len < buf_size) {
85 num_binaries++;
86 total_len += header->size_total;
87 header = (struct amd_fw_header *)(buf + total_len);
88 }
89
90 if (total_len != buf_size) {
91 fprintf(stderr, "Malformed binary\n");
92 return -1;
93 }
94 return num_binaries;
95}
96
97static int add_sha(amd_fw_entry *entry, void *buf, size_t buf_size, enum platform soc_id)
98{
99 struct amd_fw_header *header = (struct amd_fw_header *)buf;
100 /* Include only signed part for hash calculation. */
101 size_t total_len = 0;
102 int num_binaries = get_num_binaries(buf, buf_size);
103
104 if (num_binaries <= 0)
105 return num_binaries;
106
107 entry->hash_entries = malloc(num_binaries * sizeof(amd_fw_entry_hash));
108 if (!entry->hash_entries) {
109 fprintf(stderr, "Error allocating memory to add FW hash\n");
110 return -1;
111 }
112 entry->num_hash_entries = num_binaries;
113
114 /* Iterate through each binary */
115 for (int i = 0; i < num_binaries; i++) {
116 if (add_single_sha(&entry->hash_entries[i], buf + total_len, soc_id)) {
117 free(entry->hash_entries);
118 return -1;
119 }
120 total_len += header->size_total;
121 header = (struct amd_fw_header *)(buf + total_len);
122 }
123
124 return 0;
125}
126
127static void write_one_psp_firmware_hash_entry(int fd, amd_fw_entry_hash *entry)
128{
129 uint16_t type = entry->fw_id;
130 uint16_t subtype = entry->subtype;
131
132 write_or_fail(fd, &type, sizeof(type));
133 write_or_fail(fd, &subtype, sizeof(subtype));
134 write_or_fail(fd, entry->sha, entry->sha_len);
135}
136
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600137static void open_psp_fw_hash_files(const char *file_prefix)
Zheng Baof080cd52023-03-22 12:50:36 +0800138{
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600139 size_t hash_file_strlen;
140 char *hash_file_name;
Zheng Baof080cd52023-03-22 12:50:36 +0800141
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600142 /* Hash Table ID is part of the file name. For now only single digit ID is
143 supported and is sufficient. Hence assert MAX_NUM_HASH_TABLES < 10 before
144 constructing file name. Revisit later when > 10 hash tables are required. */
145 assert(MAX_NUM_HASH_TABLES < 10);
146 /* file_prefix + ".[1-9]" + ".hash" + '\0' */
147 hash_file_strlen = strlen(file_prefix) + 2 + strlen(HASH_FILE_SUFFIX) + 1;
148 hash_file_name = malloc(hash_file_strlen);
149 if (!hash_file_name) {
150 fprintf(stderr, "malloc(%lu) failed\n", hash_file_strlen);
Zheng Baof080cd52023-03-22 12:50:36 +0800151 exit(-1);
152 }
153
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600154 for (unsigned int i = 0; i < MAX_NUM_HASH_TABLES; i++) {
155 /* Hash table IDs are expected to be contiguous and hence holes are not
156 expected. */
157 if (!hash_files[i].present)
158 break;
159
160 if (i)
161 snprintf(hash_file_name, hash_file_strlen, "%s.%d%s",
162 file_prefix, i, HASH_FILE_SUFFIX);
163 else
164 /* Default file name without number for backwards compatibility. */
165 snprintf(hash_file_name, hash_file_strlen, "%s%s",
166 file_prefix, HASH_FILE_SUFFIX);
167
168 hash_files[i].fd = open(hash_file_name, O_RDWR | O_CREAT | O_TRUNC, 0666);
169 if (hash_files[i].fd < 0) {
170 fprintf(stderr, "Error opening file: %s: %s\n",
171 hash_file_name, strerror(errno));
172 free(hash_file_name);
173 exit(-1);
174 }
175 }
176 free(hash_file_name);
177}
178
179static void close_psp_fw_hash_files(void)
180{
181 for (unsigned int i = 0; i < MAX_NUM_HASH_TABLES; i++) {
182 if (!hash_files[i].present)
183 break;
184
185 close(hash_files[i].fd);
186 }
187}
188
189static void write_psp_firmware_hash(amd_fw_entry *fw_table)
190{
191 uint8_t hash_tbl_id;
192
193 for (unsigned int i = 0; i < MAX_NUM_HASH_TABLES; i++) {
194 if (!hash_files[i].present)
195 continue;
196 hash_files[i].hash_header.version = HASH_HDR_V1;
197 }
198
Zheng Baof080cd52023-03-22 12:50:36 +0800199 for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600200 hash_tbl_id = fw_table[i].hash_tbl_id;
201 assert(hash_files[hash_tbl_id].present);
202
Zheng Baof080cd52023-03-22 12:50:36 +0800203 for (unsigned int j = 0; j < fw_table[i].num_hash_entries; j++) {
204 if (fw_table[i].hash_entries[j].sha_len == SHA256_DIGEST_LENGTH) {
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600205 hash_files[hash_tbl_id].hash_header.no_of_entries_256++;
Zheng Baof080cd52023-03-22 12:50:36 +0800206 } else if (fw_table[i].hash_entries[j].sha_len ==
207 SHA384_DIGEST_LENGTH) {
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600208 hash_files[hash_tbl_id].hash_header.no_of_entries_384++;
Zheng Baof080cd52023-03-22 12:50:36 +0800209 } else if (fw_table[i].hash_entries[j].sha_len) {
210 fprintf(stderr, "%s: Error invalid sha_len %d\n",
211 __func__, fw_table[i].hash_entries[j].sha_len);
212 exit(-1);
213 }
214 }
215 }
216
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600217 for (unsigned int i = 0; i < MAX_NUM_HASH_TABLES; i++) {
218 if (!hash_files[i].present)
219 continue;
220 write_or_fail(hash_files[i].fd, &hash_files[i].hash_header,
221 sizeof(hash_files[i].hash_header));
222 }
Zheng Baof080cd52023-03-22 12:50:36 +0800223
224 /* Add all the SHA256 hash entries first followed by SHA384 entries. PSP verstage
225 processes the table in that order. Mixing and matching SHA256 and SHA384 entries
226 will cause the hash verification failure at run-time. */
227 for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600228 hash_tbl_id = fw_table[i].hash_tbl_id;
Zheng Baof080cd52023-03-22 12:50:36 +0800229 for (unsigned int j = 0; j < fw_table[i].num_hash_entries; j++) {
230 if (fw_table[i].hash_entries[j].sha_len == SHA256_DIGEST_LENGTH)
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600231 write_one_psp_firmware_hash_entry(hash_files[hash_tbl_id].fd,
232 &fw_table[i].hash_entries[j]);
Zheng Baof080cd52023-03-22 12:50:36 +0800233 }
234 }
235
236 for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600237 hash_tbl_id = fw_table[i].hash_tbl_id;
Zheng Baof080cd52023-03-22 12:50:36 +0800238 for (unsigned int j = 0; j < fw_table[i].num_hash_entries; j++) {
239 if (fw_table[i].hash_entries[j].sha_len == SHA384_DIGEST_LENGTH)
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600240 write_one_psp_firmware_hash_entry(hash_files[hash_tbl_id].fd,
241 &fw_table[i].hash_entries[j]);
Zheng Baof080cd52023-03-22 12:50:36 +0800242 }
243 }
244
Zheng Baof080cd52023-03-22 12:50:36 +0800245 for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
246 if (!fw_table[i].num_hash_entries || !fw_table[i].hash_entries)
247 continue;
248
249 free(fw_table[i].hash_entries);
250 fw_table[i].hash_entries = NULL;
251 fw_table[i].num_hash_entries = 0;
252 }
253}
254
255/**
256 * process_signed_psp_firmwares() - Process the signed PSP binaries to keep them separate
257 * @signed_rom: Output file path grouping all the signed PSP binaries.
258 * @fw_table: Table of all the PSP firmware entries/binaries to be processed.
259 * @signed_start_addr: Offset of the FMAP section, within the flash device, to hold
260 * the signed PSP binaries.
261 * @soc_id: SoC ID of the PSP binaries.
262 */
263void process_signed_psp_firmwares(const char *signed_rom,
264 amd_fw_entry *fw_table,
265 uint64_t signed_start_addr,
266 enum platform soc_id)
267{
268 unsigned int i;
269 int fd;
270 int signed_rom_fd;
271 ssize_t bytes, align_bytes;
272 uint8_t *buf;
Zheng Baof080cd52023-03-22 12:50:36 +0800273 struct amd_fw_header header;
274 struct stat fd_stat;
275 /* Every blob in amdfw*.rom has to start at address aligned to 0x100. Prepare an
276 alignment data with 0xff to pad the blobs and meet the alignment requirement. */
277 uint8_t align_data[BLOB_ALIGNMENT - 1];
278
279 memset(align_data, 0xff, sizeof(align_data));
280 signed_rom_fd = open(signed_rom, O_RDWR | O_CREAT | O_TRUNC,
281 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
282 if (signed_rom_fd < 0) {
283 fprintf(stderr, "Error opening file: %s: %s\n",
284 signed_rom, strerror(errno));
285 return;
286 }
287
288 for (i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
289 fw_table[i].num_hash_entries = 0;
290 fw_table[i].hash_entries = NULL;
291
292 if (!(fw_table[i].filename) || fw_table[i].skip_hashing)
293 continue;
294
295 memset(&header, 0, sizeof(header));
296
297 fd = open(fw_table[i].filename, O_RDONLY);
298 if (fd < 0) {
299 /* Keep the file along with set of unsigned PSP binaries & continue. */
300 fprintf(stderr, "Error opening file: %s: %s\n",
301 fw_table[i].filename, strerror(errno));
302 continue;
303 }
304
305 if (fstat(fd, &fd_stat)) {
306 /* Keep the file along with set of unsigned PSP binaries & continue. */
307 fprintf(stderr, "fstat error: %s\n", strerror(errno));
308 close(fd);
309 continue;
310 }
311
312 bytes = read_from_file_to_buf(fd, &header, sizeof(struct amd_fw_header));
313 if (bytes != (ssize_t)sizeof(struct amd_fw_header)) {
314 /* Keep the file along with set of unsigned PSP binaries & continue. */
315 fprintf(stderr, "%s: Error reading header from %s\n",
316 __func__, fw_table[i].filename);
317 close(fd);
318 continue;
319 }
320
321 /* If firmware header looks like invalid, assume it's not signed */
322 if (!header.fw_type && !header.fw_id) {
323 fprintf(stderr, "%s: Invalid FWID for %s\n",
324 __func__, fw_table[i].filename);
325 close(fd);
326 continue;
327 }
328
329
330 /* PSP binary is not signed and should not be part of signed PSP binaries
331 set. */
332 if (header.sig_opt != 1) {
333 close(fd);
334 continue;
335 }
336
337 buf = malloc(fd_stat.st_size);
338 if (!buf) {
339 /* Keep the file along with set of unsigned PSP binaries & continue. */
340 fprintf(stderr, "%s: failed to allocate memory with size %lld\n",
341 __func__, (long long)fd_stat.st_size);
342 close(fd);
343 continue;
344 }
345
346 lseek(fd, SEEK_SET, 0);
347 bytes = read_from_file_to_buf(fd, buf, fd_stat.st_size);
348 if (bytes != fd_stat.st_size) {
349 /* Keep the file along with set of unsigned PSP binaries & continue. */
350 fprintf(stderr, "%s: failed to read %s\n",
351 __func__, fw_table[i].filename);
352 free(buf);
353 close(fd);
354 continue;
355 }
356
357 bytes = write_from_buf_to_file(signed_rom_fd, buf, fd_stat.st_size);
358 if (bytes != fd_stat.st_size) {
359 /* Keep the file along with set of unsigned PSP binaries & continue. */
360 fprintf(stderr, "%s: failed to write %s\n",
361 __func__, fw_table[i].filename);
362 free(buf);
363 close(fd);
364 continue;
365 }
366
367 /* Write Blob alignment bytes */
368 align_bytes = 0;
369 if (fd_stat.st_size & (BLOB_ALIGNMENT - 1)) {
370 align_bytes = BLOB_ALIGNMENT -
371 (fd_stat.st_size & (BLOB_ALIGNMENT - 1));
372 bytes = write_from_buf_to_file(signed_rom_fd, align_data, align_bytes);
373 if (bytes != align_bytes) {
374 fprintf(stderr, "%s: failed to write alignment data for %s\n",
375 __func__, fw_table[i].filename);
376 lseek(signed_rom_fd, SEEK_CUR, -fd_stat.st_size);
377 free(buf);
378 close(fd);
379 continue;
380 }
381 }
382
383 if (add_sha(&fw_table[i], buf, fd_stat.st_size, soc_id))
384 exit(-1);
385
386 /* File is successfully processed and is part of signed PSP binaries set. */
387 fw_table[i].fw_id = get_psp_fw_type(soc_id, &header);
388 fw_table[i].addr_signed = signed_start_addr;
389 fw_table[i].file_size = (uint32_t)fd_stat.st_size;
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600390 hash_files[fw_table[i].hash_tbl_id].present = true;
Zheng Baof080cd52023-03-22 12:50:36 +0800391
392 signed_start_addr += fd_stat.st_size + align_bytes;
393
394 free(buf);
395 close(fd);
396 }
397
398 close(signed_rom_fd);
399
Karthikeyan Ramasubramaniand7a5d9e2023-05-03 13:34:41 -0600400 open_psp_fw_hash_files(signed_rom);
401 write_psp_firmware_hash(fw_table);
402 close_psp_fw_hash_files();
Zheng Baof080cd52023-03-22 12:50:36 +0800403}