amdfwtool: Move some funtions to other categorized source files
To reduce the size of amdfwtool.c which is already too big. Change-Id: Ib80eeb42f59a3dda04402b2feaadc1d178ed989e Signed-off-by: Zheng Bao <fishbaozi@gmail.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/73910 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Fred Reitberger <reitbergerfred@gmail.com>
This commit is contained in:
parent
94927888c7
commit
f080cd5463
|
@ -9,7 +9,7 @@ endif
|
|||
|
||||
READ_SRC = amdfwread.c
|
||||
READ_OBJ = $(READ_SRC:%.c=%.o)
|
||||
TOOL_SRC = amdfwtool.c data_parse.c
|
||||
TOOL_SRC = amdfwtool.c data_parse.c signed_psp.c handle_file.c
|
||||
TOOL_OBJ = $(TOOL_SRC:%.c=%.o)
|
||||
HEADER=amdfwtool.h
|
||||
TARGETS = amdfwread amdfwtool
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
amdfwtoolobj = amdfwtool.o data_parse.o
|
||||
amdfwtoolobj = amdfwtool.o data_parse.o signed_psp.o handle_file.o
|
||||
amdfwreadobj = amdfwread.o
|
||||
|
||||
AMDFWTOOLCFLAGS=-O2 -Wall -Wextra -Wshadow -Werror
|
||||
|
|
|
@ -73,7 +73,6 @@
|
|||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
@ -91,24 +90,9 @@
|
|||
#define MIN_ROM_KB 256
|
||||
|
||||
#define _MAX(A, B) (((A) > (B)) ? (A) : (B))
|
||||
#define ERASE_ALIGNMENT 0x1000U
|
||||
#define TABLE_ALIGNMENT 0x1000U
|
||||
#define BLOB_ALIGNMENT 0x100U
|
||||
#define TABLE_ERASE_ALIGNMENT _MAX(TABLE_ALIGNMENT, ERASE_ALIGNMENT)
|
||||
#define BLOB_ERASE_ALIGNMENT _MAX(BLOB_ALIGNMENT, ERASE_ALIGNMENT)
|
||||
|
||||
#define DEFAULT_SOFT_FUSE_CHAIN "0x1"
|
||||
|
||||
/* Defines related to hashing signed binaries */
|
||||
enum hash_header_ver {
|
||||
HASH_HDR_V1 = 1,
|
||||
};
|
||||
/* Signature ID enums are defined by PSP based on the algorithm used. */
|
||||
enum signature_id {
|
||||
SIG_ID_RSA2048,
|
||||
SIG_ID_RSA4096 = 2,
|
||||
};
|
||||
#define HASH_FILE_SUFFIX ".hash"
|
||||
#define EFS_FILE_SUFFIX ".efs"
|
||||
#define TMP_FILE_SUFFIX ".tmp"
|
||||
#define BODY_FILE_SUFFIX ".body"
|
||||
|
@ -776,66 +760,6 @@ static ssize_t copy_blob(void *dest, const char *src_file, size_t room)
|
|||
return bytes;
|
||||
}
|
||||
|
||||
static ssize_t read_from_file_to_buf(int fd, void *buf, size_t buf_size)
|
||||
{
|
||||
ssize_t bytes;
|
||||
size_t total_bytes = 0;
|
||||
|
||||
do {
|
||||
bytes = read(fd, buf + total_bytes, buf_size - total_bytes);
|
||||
if (bytes == 0) {
|
||||
fprintf(stderr, "Reached EOF probably\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (bytes < 0 && errno == EAGAIN)
|
||||
bytes = 0;
|
||||
|
||||
if (bytes < 0) {
|
||||
fprintf(stderr, "Read failure %s\n", strerror(errno));
|
||||
return bytes;
|
||||
}
|
||||
|
||||
total_bytes += bytes;
|
||||
} while (total_bytes < buf_size);
|
||||
|
||||
if (total_bytes != buf_size) {
|
||||
fprintf(stderr, "Read data size(%zu) != buffer size(%zu)\n",
|
||||
total_bytes, buf_size);
|
||||
return -1;
|
||||
}
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
static ssize_t write_from_buf_to_file(int fd, const void *buf, size_t buf_size)
|
||||
{
|
||||
ssize_t bytes;
|
||||
size_t total_bytes = 0;
|
||||
|
||||
do {
|
||||
bytes = write(fd, buf + total_bytes, buf_size - total_bytes);
|
||||
if (bytes < 0 && errno == EAGAIN)
|
||||
bytes = 0;
|
||||
|
||||
if (bytes < 0) {
|
||||
fprintf(stderr, "Write failure %s\n", strerror(errno));
|
||||
lseek(fd, SEEK_CUR, -total_bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
total_bytes += bytes;
|
||||
} while (total_bytes < buf_size);
|
||||
|
||||
if (total_bytes != buf_size) {
|
||||
fprintf(stderr, "Wrote more data(%zu) than buffer size(%zu)\n",
|
||||
total_bytes, buf_size);
|
||||
lseek(fd, SEEK_CUR, -total_bytes);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
static uint32_t get_psp_id(enum platform soc_id)
|
||||
{
|
||||
uint32_t psp_id;
|
||||
|
@ -871,99 +795,6 @@ static uint32_t get_psp_id(enum platform soc_id)
|
|||
return psp_id;
|
||||
}
|
||||
|
||||
static uint16_t get_psp_fw_type(enum platform soc_id, struct amd_fw_header *header)
|
||||
{
|
||||
switch (soc_id) {
|
||||
case PLATFORM_MENDOCINO:
|
||||
case PLATFORM_PHOENIX:
|
||||
case PLATFORM_GLINDA:
|
||||
/* Fallback to fw_type if fw_id is not populated, which serves the same
|
||||
purpose on older SoCs. */
|
||||
return header->fw_id ? header->fw_id : header->fw_type;
|
||||
default:
|
||||
return header->fw_type;
|
||||
}
|
||||
}
|
||||
|
||||
static int add_single_sha(amd_fw_entry_hash *entry, void *buf, enum platform soc_id)
|
||||
{
|
||||
uint8_t hash[SHA384_DIGEST_LENGTH];
|
||||
struct amd_fw_header *header = (struct amd_fw_header *)buf;
|
||||
/* Include only signed part for hash calculation. */
|
||||
size_t len = header->fw_size_signed + sizeof(struct amd_fw_header);
|
||||
uint8_t *body = (uint8_t *)buf;
|
||||
|
||||
if (len > header->size_total)
|
||||
return -1;
|
||||
|
||||
if (header->sig_id == SIG_ID_RSA4096) {
|
||||
SHA384(body, len, hash);
|
||||
entry->sha_len = SHA384_DIGEST_LENGTH;
|
||||
} else if (header->sig_id == SIG_ID_RSA2048) {
|
||||
SHA256(body, len, hash);
|
||||
entry->sha_len = SHA256_DIGEST_LENGTH;
|
||||
} else {
|
||||
fprintf(stderr, "%s: Unknown signature id: 0x%08x\n",
|
||||
__func__, header->sig_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(entry->sha, hash, entry->sha_len);
|
||||
entry->fw_id = get_psp_fw_type(soc_id, header);
|
||||
entry->subtype = header->fw_subtype;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_num_binaries(void *buf, size_t buf_size)
|
||||
{
|
||||
struct amd_fw_header *header = (struct amd_fw_header *)buf;
|
||||
size_t total_len = 0;
|
||||
int num_binaries = 0;
|
||||
|
||||
while (total_len < buf_size) {
|
||||
num_binaries++;
|
||||
total_len += header->size_total;
|
||||
header = (struct amd_fw_header *)(buf + total_len);
|
||||
}
|
||||
|
||||
if (total_len != buf_size) {
|
||||
fprintf(stderr, "Malformed binary\n");
|
||||
return -1;
|
||||
}
|
||||
return num_binaries;
|
||||
}
|
||||
|
||||
static int add_sha(amd_fw_entry *entry, void *buf, size_t buf_size, enum platform soc_id)
|
||||
{
|
||||
struct amd_fw_header *header = (struct amd_fw_header *)buf;
|
||||
/* Include only signed part for hash calculation. */
|
||||
size_t total_len = 0;
|
||||
int num_binaries = get_num_binaries(buf, buf_size);
|
||||
|
||||
if (num_binaries <= 0)
|
||||
return num_binaries;
|
||||
|
||||
entry->hash_entries = malloc(num_binaries * sizeof(amd_fw_entry_hash));
|
||||
if (!entry->hash_entries) {
|
||||
fprintf(stderr, "Error allocating memory to add FW hash\n");
|
||||
return -1;
|
||||
}
|
||||
entry->num_hash_entries = num_binaries;
|
||||
|
||||
/* Iterate through each binary */
|
||||
for (int i = 0; i < num_binaries; i++) {
|
||||
if (add_single_sha(&entry->hash_entries[i], buf + total_len, soc_id)) {
|
||||
free(entry->hash_entries);
|
||||
return -1;
|
||||
}
|
||||
total_len += header->size_total;
|
||||
header = (struct amd_fw_header *)(buf + total_len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void integrate_firmwares(context *ctx,
|
||||
embedded_firmware *romsig,
|
||||
amd_fw_entry *fw_table)
|
||||
|
@ -1026,247 +857,6 @@ static void dump_bdt_firmwares(amd_bios_entry *fw_table)
|
|||
}
|
||||
}
|
||||
|
||||
static void write_or_fail(int fd, void *ptr, size_t size)
|
||||
{
|
||||
ssize_t written;
|
||||
|
||||
written = write_from_buf_to_file(fd, ptr, size);
|
||||
if (written < 0 || (size_t)written != size) {
|
||||
fprintf(stderr, "%s: Error writing %zu bytes - written %zd bytes\n",
|
||||
__func__, size, written);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_one_psp_firmware_hash_entry(int fd, amd_fw_entry_hash *entry)
|
||||
{
|
||||
uint16_t type = entry->fw_id;
|
||||
uint16_t subtype = entry->subtype;
|
||||
|
||||
write_or_fail(fd, &type, sizeof(type));
|
||||
write_or_fail(fd, &subtype, sizeof(subtype));
|
||||
write_or_fail(fd, entry->sha, entry->sha_len);
|
||||
}
|
||||
|
||||
static void write_psp_firmware_hash(const char *filename,
|
||||
amd_fw_entry *fw_table)
|
||||
{
|
||||
struct psp_fw_hash_table hash_header = {0};
|
||||
int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
|
||||
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Error opening file: %s: %s\n",
|
||||
filename, strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
hash_header.version = HASH_HDR_V1;
|
||||
for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
||||
for (unsigned int j = 0; j < fw_table[i].num_hash_entries; j++) {
|
||||
if (fw_table[i].hash_entries[j].sha_len == SHA256_DIGEST_LENGTH) {
|
||||
hash_header.no_of_entries_256++;
|
||||
} else if (fw_table[i].hash_entries[j].sha_len ==
|
||||
SHA384_DIGEST_LENGTH) {
|
||||
hash_header.no_of_entries_384++;
|
||||
} else if (fw_table[i].hash_entries[j].sha_len) {
|
||||
fprintf(stderr, "%s: Error invalid sha_len %d\n",
|
||||
__func__, fw_table[i].hash_entries[j].sha_len);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write_or_fail(fd, &hash_header, sizeof(hash_header));
|
||||
|
||||
/* Add all the SHA256 hash entries first followed by SHA384 entries. PSP verstage
|
||||
processes the table in that order. Mixing and matching SHA256 and SHA384 entries
|
||||
will cause the hash verification failure at run-time. */
|
||||
for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
||||
for (unsigned int j = 0; j < fw_table[i].num_hash_entries; j++) {
|
||||
if (fw_table[i].hash_entries[j].sha_len == SHA256_DIGEST_LENGTH)
|
||||
write_one_psp_firmware_hash_entry(fd,
|
||||
&fw_table[i].hash_entries[j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
||||
for (unsigned int j = 0; j < fw_table[i].num_hash_entries; j++) {
|
||||
if (fw_table[i].hash_entries[j].sha_len == SHA384_DIGEST_LENGTH)
|
||||
write_one_psp_firmware_hash_entry(fd,
|
||||
&fw_table[i].hash_entries[j]);
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
||||
if (!fw_table[i].num_hash_entries || !fw_table[i].hash_entries)
|
||||
continue;
|
||||
|
||||
free(fw_table[i].hash_entries);
|
||||
fw_table[i].hash_entries = NULL;
|
||||
fw_table[i].num_hash_entries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* process_signed_psp_firmwares() - Process the signed PSP binaries to keep them separate
|
||||
* @signed_rom: Output file path grouping all the signed PSP binaries.
|
||||
* @fw_table: Table of all the PSP firmware entries/binaries to be processed.
|
||||
* @signed_start_addr: Offset of the FMAP section, within the flash device, to hold
|
||||
* the signed PSP binaries.
|
||||
* @soc_id: SoC ID of the PSP binaries.
|
||||
*/
|
||||
static void process_signed_psp_firmwares(const char *signed_rom,
|
||||
amd_fw_entry *fw_table,
|
||||
uint64_t signed_start_addr,
|
||||
enum platform soc_id)
|
||||
{
|
||||
unsigned int i;
|
||||
int fd;
|
||||
int signed_rom_fd;
|
||||
ssize_t bytes, align_bytes;
|
||||
uint8_t *buf;
|
||||
char *signed_rom_hash;
|
||||
size_t signed_rom_hash_strlen;
|
||||
struct amd_fw_header header;
|
||||
struct stat fd_stat;
|
||||
/* Every blob in amdfw*.rom has to start at address aligned to 0x100. Prepare an
|
||||
alignment data with 0xff to pad the blobs and meet the alignment requirement. */
|
||||
uint8_t align_data[BLOB_ALIGNMENT - 1];
|
||||
|
||||
memset(align_data, 0xff, sizeof(align_data));
|
||||
signed_rom_fd = open(signed_rom, O_RDWR | O_CREAT | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||
if (signed_rom_fd < 0) {
|
||||
fprintf(stderr, "Error opening file: %s: %s\n",
|
||||
signed_rom, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
||||
fw_table[i].num_hash_entries = 0;
|
||||
fw_table[i].hash_entries = NULL;
|
||||
|
||||
if (!(fw_table[i].filename) || fw_table[i].skip_hashing)
|
||||
continue;
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
|
||||
fd = open(fw_table[i].filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
/* Keep the file along with set of unsigned PSP binaries & continue. */
|
||||
fprintf(stderr, "Error opening file: %s: %s\n",
|
||||
fw_table[i].filename, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fstat(fd, &fd_stat)) {
|
||||
/* Keep the file along with set of unsigned PSP binaries & continue. */
|
||||
fprintf(stderr, "fstat error: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
bytes = read_from_file_to_buf(fd, &header, sizeof(struct amd_fw_header));
|
||||
if (bytes != (ssize_t)sizeof(struct amd_fw_header)) {
|
||||
/* Keep the file along with set of unsigned PSP binaries & continue. */
|
||||
fprintf(stderr, "%s: Error reading header from %s\n",
|
||||
__func__, fw_table[i].filename);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If firmware header looks like invalid, assume it's not signed */
|
||||
if (!header.fw_type && !header.fw_id) {
|
||||
fprintf(stderr, "%s: Invalid FWID for %s\n",
|
||||
__func__, fw_table[i].filename);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* PSP binary is not signed and should not be part of signed PSP binaries
|
||||
set. */
|
||||
if (header.sig_opt != 1) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
buf = malloc(fd_stat.st_size);
|
||||
if (!buf) {
|
||||
/* Keep the file along with set of unsigned PSP binaries & continue. */
|
||||
fprintf(stderr, "%s: failed to allocate memory with size %lld\n",
|
||||
__func__, (long long)fd_stat.st_size);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
lseek(fd, SEEK_SET, 0);
|
||||
bytes = read_from_file_to_buf(fd, buf, fd_stat.st_size);
|
||||
if (bytes != fd_stat.st_size) {
|
||||
/* Keep the file along with set of unsigned PSP binaries & continue. */
|
||||
fprintf(stderr, "%s: failed to read %s\n",
|
||||
__func__, fw_table[i].filename);
|
||||
free(buf);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
bytes = write_from_buf_to_file(signed_rom_fd, buf, fd_stat.st_size);
|
||||
if (bytes != fd_stat.st_size) {
|
||||
/* Keep the file along with set of unsigned PSP binaries & continue. */
|
||||
fprintf(stderr, "%s: failed to write %s\n",
|
||||
__func__, fw_table[i].filename);
|
||||
free(buf);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Write Blob alignment bytes */
|
||||
align_bytes = 0;
|
||||
if (fd_stat.st_size & (BLOB_ALIGNMENT - 1)) {
|
||||
align_bytes = BLOB_ALIGNMENT -
|
||||
(fd_stat.st_size & (BLOB_ALIGNMENT - 1));
|
||||
bytes = write_from_buf_to_file(signed_rom_fd, align_data, align_bytes);
|
||||
if (bytes != align_bytes) {
|
||||
fprintf(stderr, "%s: failed to write alignment data for %s\n",
|
||||
__func__, fw_table[i].filename);
|
||||
lseek(signed_rom_fd, SEEK_CUR, -fd_stat.st_size);
|
||||
free(buf);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (add_sha(&fw_table[i], buf, fd_stat.st_size, soc_id))
|
||||
exit(-1);
|
||||
|
||||
/* File is successfully processed and is part of signed PSP binaries set. */
|
||||
fw_table[i].fw_id = get_psp_fw_type(soc_id, &header);
|
||||
fw_table[i].addr_signed = signed_start_addr;
|
||||
fw_table[i].file_size = (uint32_t)fd_stat.st_size;
|
||||
|
||||
signed_start_addr += fd_stat.st_size + align_bytes;
|
||||
|
||||
free(buf);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
close(signed_rom_fd);
|
||||
|
||||
/* signed_rom file name + ".hash" + '\0' */
|
||||
signed_rom_hash_strlen = strlen(signed_rom) + strlen(HASH_FILE_SUFFIX) + 1;
|
||||
signed_rom_hash = malloc(signed_rom_hash_strlen);
|
||||
if (!signed_rom_hash) {
|
||||
fprintf(stderr, "malloc(%lu) failed\n", signed_rom_hash_strlen);
|
||||
exit(-1);
|
||||
}
|
||||
strcpy(signed_rom_hash, signed_rom);
|
||||
strcat(signed_rom_hash, HASH_FILE_SUFFIX);
|
||||
write_psp_firmware_hash(signed_rom_hash, fw_table);
|
||||
free(signed_rom_hash);
|
||||
}
|
||||
|
||||
static void integrate_psp_ab(context *ctx, psp_directory_table *pspdir,
|
||||
psp_directory_table *pspdir2, ish_directory_table *ish,
|
||||
amd_fw_type ab, enum platform soc_id)
|
||||
|
|
|
@ -9,6 +9,12 @@
|
|||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define ERASE_ALIGNMENT 0x1000U
|
||||
#define TABLE_ALIGNMENT 0x1000U
|
||||
#define BLOB_ALIGNMENT 0x100U
|
||||
#define TABLE_ERASE_ALIGNMENT _MAX(TABLE_ALIGNMENT, ERASE_ALIGNMENT)
|
||||
#define BLOB_ERASE_ALIGNMENT _MAX(BLOB_ALIGNMENT, ERASE_ALIGNMENT)
|
||||
|
||||
enum platform {
|
||||
PLATFORM_UNKNOWN,
|
||||
PLATFORM_CARRIZO,
|
||||
|
@ -408,7 +414,13 @@ typedef struct _amd_cb_config {
|
|||
|
||||
void register_fw_fuse(char *str);
|
||||
uint8_t process_config(FILE *config, amd_cb_config *cb_config);
|
||||
|
||||
void process_signed_psp_firmwares(const char *signed_rom,
|
||||
amd_fw_entry *fw_table,
|
||||
uint64_t signed_start_addr,
|
||||
enum platform soc_id);
|
||||
void write_or_fail(int fd, void *ptr, size_t size);
|
||||
ssize_t read_from_file_to_buf(int fd, void *buf, size_t buf_size);
|
||||
ssize_t write_from_buf_to_file(int fd, const void *buf, size_t buf_size);
|
||||
#define OK 0
|
||||
|
||||
#define LINE_EOF (1)
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "amdfwtool.h"
|
||||
|
||||
void write_or_fail(int fd, void *ptr, size_t size)
|
||||
{
|
||||
ssize_t written;
|
||||
|
||||
written = write_from_buf_to_file(fd, ptr, size);
|
||||
if (written < 0 || (size_t)written != size) {
|
||||
fprintf(stderr, "%s: Error writing %zu bytes - written %zd bytes\n",
|
||||
__func__, size, written);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t read_from_file_to_buf(int fd, void *buf, size_t buf_size)
|
||||
{
|
||||
ssize_t bytes;
|
||||
size_t total_bytes = 0;
|
||||
|
||||
do {
|
||||
bytes = read(fd, buf + total_bytes, buf_size - total_bytes);
|
||||
if (bytes == 0) {
|
||||
fprintf(stderr, "Reached EOF probably\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (bytes < 0 && errno == EAGAIN)
|
||||
bytes = 0;
|
||||
|
||||
if (bytes < 0) {
|
||||
fprintf(stderr, "Read failure %s\n", strerror(errno));
|
||||
return bytes;
|
||||
}
|
||||
|
||||
total_bytes += bytes;
|
||||
} while (total_bytes < buf_size);
|
||||
|
||||
if (total_bytes != buf_size) {
|
||||
fprintf(stderr, "Read data size(%zu) != buffer size(%zu)\n",
|
||||
total_bytes, buf_size);
|
||||
return -1;
|
||||
}
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
ssize_t write_from_buf_to_file(int fd, const void *buf, size_t buf_size)
|
||||
{
|
||||
ssize_t bytes;
|
||||
size_t total_bytes = 0;
|
||||
|
||||
do {
|
||||
bytes = write(fd, buf + total_bytes, buf_size - total_bytes);
|
||||
if (bytes < 0 && errno == EAGAIN)
|
||||
bytes = 0;
|
||||
|
||||
if (bytes < 0) {
|
||||
fprintf(stderr, "Write failure %s\n", strerror(errno));
|
||||
lseek(fd, SEEK_CUR, -total_bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
total_bytes += bytes;
|
||||
} while (total_bytes < buf_size);
|
||||
|
||||
if (total_bytes != buf_size) {
|
||||
fprintf(stderr, "Wrote more data(%zu) than buffer size(%zu)\n",
|
||||
total_bytes, buf_size);
|
||||
lseek(fd, SEEK_CUR, -total_bytes);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return buf_size;
|
||||
}
|
|
@ -0,0 +1,346 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "amdfwtool.h"
|
||||
|
||||
/* Defines related to hashing signed binaries */
|
||||
enum hash_header_ver {
|
||||
HASH_HDR_V1 = 1,
|
||||
};
|
||||
/* Signature ID enums are defined by PSP based on the algorithm used. */
|
||||
enum signature_id {
|
||||
SIG_ID_RSA2048,
|
||||
SIG_ID_RSA4096 = 2,
|
||||
};
|
||||
#define HASH_FILE_SUFFIX ".hash"
|
||||
|
||||
static uint16_t get_psp_fw_type(enum platform soc_id, struct amd_fw_header *header)
|
||||
{
|
||||
switch (soc_id) {
|
||||
case PLATFORM_MENDOCINO:
|
||||
case PLATFORM_PHOENIX:
|
||||
case PLATFORM_GLINDA:
|
||||
/* Fallback to fw_type if fw_id is not populated, which serves the same
|
||||
purpose on older SoCs. */
|
||||
return header->fw_id ? header->fw_id : header->fw_type;
|
||||
default:
|
||||
return header->fw_type;
|
||||
}
|
||||
}
|
||||
|
||||
static int add_single_sha(amd_fw_entry_hash *entry, void *buf, enum platform soc_id)
|
||||
{
|
||||
uint8_t hash[SHA384_DIGEST_LENGTH];
|
||||
struct amd_fw_header *header = (struct amd_fw_header *)buf;
|
||||
/* Include only signed part for hash calculation. */
|
||||
size_t len = header->fw_size_signed + sizeof(struct amd_fw_header);
|
||||
uint8_t *body = (uint8_t *)buf;
|
||||
|
||||
if (len > header->size_total)
|
||||
return -1;
|
||||
|
||||
if (header->sig_id == SIG_ID_RSA4096) {
|
||||
SHA384(body, len, hash);
|
||||
entry->sha_len = SHA384_DIGEST_LENGTH;
|
||||
} else if (header->sig_id == SIG_ID_RSA2048) {
|
||||
SHA256(body, len, hash);
|
||||
entry->sha_len = SHA256_DIGEST_LENGTH;
|
||||
} else {
|
||||
fprintf(stderr, "%s: Unknown signature id: 0x%08x\n",
|
||||
__func__, header->sig_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(entry->sha, hash, entry->sha_len);
|
||||
entry->fw_id = get_psp_fw_type(soc_id, header);
|
||||
entry->subtype = header->fw_subtype;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_num_binaries(void *buf, size_t buf_size)
|
||||
{
|
||||
struct amd_fw_header *header = (struct amd_fw_header *)buf;
|
||||
size_t total_len = 0;
|
||||
int num_binaries = 0;
|
||||
|
||||
while (total_len < buf_size) {
|
||||
num_binaries++;
|
||||
total_len += header->size_total;
|
||||
header = (struct amd_fw_header *)(buf + total_len);
|
||||
}
|
||||
|
||||
if (total_len != buf_size) {
|
||||
fprintf(stderr, "Malformed binary\n");
|
||||
return -1;
|
||||
}
|
||||
return num_binaries;
|
||||
}
|
||||
|
||||
static int add_sha(amd_fw_entry *entry, void *buf, size_t buf_size, enum platform soc_id)
|
||||
{
|
||||
struct amd_fw_header *header = (struct amd_fw_header *)buf;
|
||||
/* Include only signed part for hash calculation. */
|
||||
size_t total_len = 0;
|
||||
int num_binaries = get_num_binaries(buf, buf_size);
|
||||
|
||||
if (num_binaries <= 0)
|
||||
return num_binaries;
|
||||
|
||||
entry->hash_entries = malloc(num_binaries * sizeof(amd_fw_entry_hash));
|
||||
if (!entry->hash_entries) {
|
||||
fprintf(stderr, "Error allocating memory to add FW hash\n");
|
||||
return -1;
|
||||
}
|
||||
entry->num_hash_entries = num_binaries;
|
||||
|
||||
/* Iterate through each binary */
|
||||
for (int i = 0; i < num_binaries; i++) {
|
||||
if (add_single_sha(&entry->hash_entries[i], buf + total_len, soc_id)) {
|
||||
free(entry->hash_entries);
|
||||
return -1;
|
||||
}
|
||||
total_len += header->size_total;
|
||||
header = (struct amd_fw_header *)(buf + total_len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void write_one_psp_firmware_hash_entry(int fd, amd_fw_entry_hash *entry)
|
||||
{
|
||||
uint16_t type = entry->fw_id;
|
||||
uint16_t subtype = entry->subtype;
|
||||
|
||||
write_or_fail(fd, &type, sizeof(type));
|
||||
write_or_fail(fd, &subtype, sizeof(subtype));
|
||||
write_or_fail(fd, entry->sha, entry->sha_len);
|
||||
}
|
||||
|
||||
static void write_psp_firmware_hash(const char *filename,
|
||||
amd_fw_entry *fw_table)
|
||||
{
|
||||
struct psp_fw_hash_table hash_header = {0};
|
||||
int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
|
||||
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Error opening file: %s: %s\n",
|
||||
filename, strerror(errno));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
hash_header.version = HASH_HDR_V1;
|
||||
for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
||||
for (unsigned int j = 0; j < fw_table[i].num_hash_entries; j++) {
|
||||
if (fw_table[i].hash_entries[j].sha_len == SHA256_DIGEST_LENGTH) {
|
||||
hash_header.no_of_entries_256++;
|
||||
} else if (fw_table[i].hash_entries[j].sha_len ==
|
||||
SHA384_DIGEST_LENGTH) {
|
||||
hash_header.no_of_entries_384++;
|
||||
} else if (fw_table[i].hash_entries[j].sha_len) {
|
||||
fprintf(stderr, "%s: Error invalid sha_len %d\n",
|
||||
__func__, fw_table[i].hash_entries[j].sha_len);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write_or_fail(fd, &hash_header, sizeof(hash_header));
|
||||
|
||||
/* Add all the SHA256 hash entries first followed by SHA384 entries. PSP verstage
|
||||
processes the table in that order. Mixing and matching SHA256 and SHA384 entries
|
||||
will cause the hash verification failure at run-time. */
|
||||
for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
||||
for (unsigned int j = 0; j < fw_table[i].num_hash_entries; j++) {
|
||||
if (fw_table[i].hash_entries[j].sha_len == SHA256_DIGEST_LENGTH)
|
||||
write_one_psp_firmware_hash_entry(fd,
|
||||
&fw_table[i].hash_entries[j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
||||
for (unsigned int j = 0; j < fw_table[i].num_hash_entries; j++) {
|
||||
if (fw_table[i].hash_entries[j].sha_len == SHA384_DIGEST_LENGTH)
|
||||
write_one_psp_firmware_hash_entry(fd,
|
||||
&fw_table[i].hash_entries[j]);
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
for (unsigned int i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
||||
if (!fw_table[i].num_hash_entries || !fw_table[i].hash_entries)
|
||||
continue;
|
||||
|
||||
free(fw_table[i].hash_entries);
|
||||
fw_table[i].hash_entries = NULL;
|
||||
fw_table[i].num_hash_entries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* process_signed_psp_firmwares() - Process the signed PSP binaries to keep them separate
|
||||
* @signed_rom: Output file path grouping all the signed PSP binaries.
|
||||
* @fw_table: Table of all the PSP firmware entries/binaries to be processed.
|
||||
* @signed_start_addr: Offset of the FMAP section, within the flash device, to hold
|
||||
* the signed PSP binaries.
|
||||
* @soc_id: SoC ID of the PSP binaries.
|
||||
*/
|
||||
void process_signed_psp_firmwares(const char *signed_rom,
|
||||
amd_fw_entry *fw_table,
|
||||
uint64_t signed_start_addr,
|
||||
enum platform soc_id)
|
||||
{
|
||||
unsigned int i;
|
||||
int fd;
|
||||
int signed_rom_fd;
|
||||
ssize_t bytes, align_bytes;
|
||||
uint8_t *buf;
|
||||
char *signed_rom_hash;
|
||||
size_t signed_rom_hash_strlen;
|
||||
struct amd_fw_header header;
|
||||
struct stat fd_stat;
|
||||
/* Every blob in amdfw*.rom has to start at address aligned to 0x100. Prepare an
|
||||
alignment data with 0xff to pad the blobs and meet the alignment requirement. */
|
||||
uint8_t align_data[BLOB_ALIGNMENT - 1];
|
||||
|
||||
memset(align_data, 0xff, sizeof(align_data));
|
||||
signed_rom_fd = open(signed_rom, O_RDWR | O_CREAT | O_TRUNC,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||
if (signed_rom_fd < 0) {
|
||||
fprintf(stderr, "Error opening file: %s: %s\n",
|
||||
signed_rom, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; fw_table[i].type != AMD_FW_INVALID; i++) {
|
||||
fw_table[i].num_hash_entries = 0;
|
||||
fw_table[i].hash_entries = NULL;
|
||||
|
||||
if (!(fw_table[i].filename) || fw_table[i].skip_hashing)
|
||||
continue;
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
|
||||
fd = open(fw_table[i].filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
/* Keep the file along with set of unsigned PSP binaries & continue. */
|
||||
fprintf(stderr, "Error opening file: %s: %s\n",
|
||||
fw_table[i].filename, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fstat(fd, &fd_stat)) {
|
||||
/* Keep the file along with set of unsigned PSP binaries & continue. */
|
||||
fprintf(stderr, "fstat error: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
bytes = read_from_file_to_buf(fd, &header, sizeof(struct amd_fw_header));
|
||||
if (bytes != (ssize_t)sizeof(struct amd_fw_header)) {
|
||||
/* Keep the file along with set of unsigned PSP binaries & continue. */
|
||||
fprintf(stderr, "%s: Error reading header from %s\n",
|
||||
__func__, fw_table[i].filename);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If firmware header looks like invalid, assume it's not signed */
|
||||
if (!header.fw_type && !header.fw_id) {
|
||||
fprintf(stderr, "%s: Invalid FWID for %s\n",
|
||||
__func__, fw_table[i].filename);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* PSP binary is not signed and should not be part of signed PSP binaries
|
||||
set. */
|
||||
if (header.sig_opt != 1) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
buf = malloc(fd_stat.st_size);
|
||||
if (!buf) {
|
||||
/* Keep the file along with set of unsigned PSP binaries & continue. */
|
||||
fprintf(stderr, "%s: failed to allocate memory with size %lld\n",
|
||||
__func__, (long long)fd_stat.st_size);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
lseek(fd, SEEK_SET, 0);
|
||||
bytes = read_from_file_to_buf(fd, buf, fd_stat.st_size);
|
||||
if (bytes != fd_stat.st_size) {
|
||||
/* Keep the file along with set of unsigned PSP binaries & continue. */
|
||||
fprintf(stderr, "%s: failed to read %s\n",
|
||||
__func__, fw_table[i].filename);
|
||||
free(buf);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
bytes = write_from_buf_to_file(signed_rom_fd, buf, fd_stat.st_size);
|
||||
if (bytes != fd_stat.st_size) {
|
||||
/* Keep the file along with set of unsigned PSP binaries & continue. */
|
||||
fprintf(stderr, "%s: failed to write %s\n",
|
||||
__func__, fw_table[i].filename);
|
||||
free(buf);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Write Blob alignment bytes */
|
||||
align_bytes = 0;
|
||||
if (fd_stat.st_size & (BLOB_ALIGNMENT - 1)) {
|
||||
align_bytes = BLOB_ALIGNMENT -
|
||||
(fd_stat.st_size & (BLOB_ALIGNMENT - 1));
|
||||
bytes = write_from_buf_to_file(signed_rom_fd, align_data, align_bytes);
|
||||
if (bytes != align_bytes) {
|
||||
fprintf(stderr, "%s: failed to write alignment data for %s\n",
|
||||
__func__, fw_table[i].filename);
|
||||
lseek(signed_rom_fd, SEEK_CUR, -fd_stat.st_size);
|
||||
free(buf);
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (add_sha(&fw_table[i], buf, fd_stat.st_size, soc_id))
|
||||
exit(-1);
|
||||
|
||||
/* File is successfully processed and is part of signed PSP binaries set. */
|
||||
fw_table[i].fw_id = get_psp_fw_type(soc_id, &header);
|
||||
fw_table[i].addr_signed = signed_start_addr;
|
||||
fw_table[i].file_size = (uint32_t)fd_stat.st_size;
|
||||
|
||||
signed_start_addr += fd_stat.st_size + align_bytes;
|
||||
|
||||
free(buf);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
close(signed_rom_fd);
|
||||
|
||||
/* signed_rom file name + ".hash" + '\0' */
|
||||
signed_rom_hash_strlen = strlen(signed_rom) + strlen(HASH_FILE_SUFFIX) + 1;
|
||||
signed_rom_hash = malloc(signed_rom_hash_strlen);
|
||||
if (!signed_rom_hash) {
|
||||
fprintf(stderr, "malloc(%lu) failed\n", signed_rom_hash_strlen);
|
||||
exit(-1);
|
||||
}
|
||||
strcpy(signed_rom_hash, signed_rom);
|
||||
strcat(signed_rom_hash, HASH_FILE_SUFFIX);
|
||||
write_psp_firmware_hash(signed_rom_hash, fw_table);
|
||||
free(signed_rom_hash);
|
||||
}
|
Loading…
Reference in New Issue