diff --git a/util/amdfwtool/amdfwtool.c b/util/amdfwtool/amdfwtool.c index c25c390734..ff4b6238ec 100644 --- a/util/amdfwtool/amdfwtool.c +++ b/util/amdfwtool/amdfwtool.c @@ -223,7 +223,7 @@ static void usage(void) } amd_fw_entry amd_psp_fw_table[] = { - { .type = AMD_FW_PSP_PUBKEY, .level = PSP_BOTH | PSP_LVL2_AB }, + { .type = AMD_FW_PSP_PUBKEY, .level = PSP_BOTH | PSP_LVL2_AB, .skip_hashing = true }, { .type = AMD_FW_PSP_BOOTLOADER, .level = PSP_BOTH | PSP_LVL2_AB }, { .type = AMD_FW_PSP_SMU_FIRMWARE, .subprog = 0, .level = PSP_BOTH | PSP_LVL2_AB }, { .type = AMD_FW_PSP_RECOVERY, .level = PSP_LVL1 }, @@ -231,7 +231,8 @@ amd_fw_entry amd_psp_fw_table[] = { { .type = AMD_FW_PSP_SECURED_OS, .level = PSP_LVL2 | PSP_LVL2_AB }, { .type = AMD_FW_PSP_NVRAM, .level = PSP_LVL2 | PSP_LVL2_AB }, { .type = AMD_FW_PSP_SMU_FIRMWARE, .subprog = 2, .level = PSP_BOTH | PSP_LVL2_AB }, - { .type = AMD_FW_PSP_SECURED_DEBUG, .level = PSP_LVL2 | PSP_LVL2_AB }, + { .type = AMD_FW_PSP_SECURED_DEBUG, .level = PSP_LVL2 | PSP_LVL2_AB, + .skip_hashing = true }, { .type = AMD_FW_PSP_TRUSTLETS, .level = PSP_LVL2 | PSP_LVL2_AB }, { .type = AMD_FW_PSP_TRUSTLETKEY, .level = PSP_LVL2 | PSP_LVL2_AB }, { .type = AMD_FW_PSP_SMU_FIRMWARE2, .subprog = 2, .level = PSP_BOTH | PSP_LVL2_AB }, @@ -242,7 +243,7 @@ amd_fw_entry amd_psp_fw_table[] = { { .type = AMD_PSP_FUSE_CHAIN, .level = PSP_LVL2 | PSP_LVL2_AB }, { .type = AMD_DEBUG_UNLOCK, .level = PSP_LVL2 | PSP_LVL2_AB }, { .type = AMD_HW_IPCFG, .level = PSP_LVL2 | PSP_LVL2_AB }, - { .type = AMD_WRAPPED_IKEK, .level = PSP_BOTH | PSP_LVL2_AB }, + { .type = AMD_WRAPPED_IKEK, .level = PSP_BOTH | PSP_LVL2_AB, .skip_hashing = true }, { .type = AMD_TOKEN_UNLOCK, .level = PSP_BOTH | PSP_LVL2_AB }, { .type = AMD_SEC_GASKET, .subprog = 0, .level = PSP_BOTH | PSP_LVL2_AB }, { .type = AMD_SEC_GASKET, .subprog = 2, .level = PSP_BOTH | PSP_LVL2_AB }, @@ -267,7 +268,7 @@ amd_fw_entry amd_psp_fw_table[] = { { .type = AMD_FW_SPIROM_CFG, .level = PSP_LVL2 | PSP_LVL2_AB }, { .type = AMD_RPMC_NVRAM, .level = PSP_LVL2 | PSP_LVL2_AB }, { .type = AMD_FW_PSP_BOOTLOADER_AB, .level = PSP_LVL2 | PSP_LVL2_AB }, - { .type = AMD_TA_IKEK, .level = PSP_BOTH | PSP_LVL2_AB }, + { .type = AMD_TA_IKEK, .level = PSP_BOTH | PSP_LVL2_AB, .skip_hashing = true }, { .type = AMD_ABL0, .level = PSP_BOTH | PSP_LVL2_AB }, { .type = AMD_ABL1, .level = PSP_BOTH | PSP_LVL2_AB }, { .type = AMD_ABL2, .level = PSP_BOTH | PSP_LVL2_AB }, @@ -543,6 +544,66 @@ 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; +} + enum platform { PLATFORM_UNKNOWN, PLATFORM_STONEYRIDGE, @@ -582,6 +643,18 @@ 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: + /* 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 void integrate_firmwares(context *ctx, embedded_firmware *romsig, amd_fw_entry *fw_table) @@ -674,6 +747,149 @@ static void free_bdt_firmware_filenames(amd_bios_entry *fw_table) } } +/** + * 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; + 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++) { + 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; + } + + if (header.size_total != fd_stat.st_size) { + 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; + } + } + + /* 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); +} + 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) @@ -825,22 +1041,31 @@ static void integrate_psp_firmwares(context *ctx, count++; } else if (fw_table[i].filename != NULL) { - bytes = copy_blob(BUFF_CURRENT(*ctx), - fw_table[i].filename, BUFF_ROOM(*ctx)); - if (bytes < 0) { - free(ctx->rom); - exit(1); + if (fw_table[i].addr_signed) { + pspdir->entries[count].addr = + RUN_OFFSET(*ctx, fw_table[i].addr_signed); + pspdir->entries[count].address_mode = + SET_ADDR_MODE_BY_TABLE(pspdir); + bytes = fw_table[i].file_size; + } else { + bytes = copy_blob(BUFF_CURRENT(*ctx), + fw_table[i].filename, BUFF_ROOM(*ctx)); + if (bytes < 0) { + free(ctx->rom); + exit(1); + } + pspdir->entries[count].addr = RUN_CURRENT(*ctx); + pspdir->entries[count].address_mode = + SET_ADDR_MODE_BY_TABLE(pspdir); + ctx->current = ALIGN(ctx->current + bytes, + BLOB_ALIGNMENT); } pspdir->entries[count].type = fw_table[i].type; pspdir->entries[count].subprog = fw_table[i].subprog; pspdir->entries[count].rsvd = 0; pspdir->entries[count].size = (uint32_t)bytes; - pspdir->entries[count].addr = RUN_CURRENT(*ctx); - pspdir->entries[count].address_mode = SET_ADDR_MODE_BY_TABLE(pspdir); - ctx->current = ALIGN(ctx->current + bytes, - BLOB_ALIGNMENT); count++; } else { /* This APU doesn't have this firmware. */ @@ -1257,6 +1482,8 @@ enum { AMDFW_OPT_SHAREDMEM, AMDFW_OPT_SHAREDMEM_SIZE, AMDFW_OPT_SOC_NAME, + AMDFW_OPT_SIGNED_OUTPUT, + AMDFW_OPT_SIGNED_ADDR, /* begin after ASCII characters */ LONGOPT_SPI_READ_MODE = 256, LONGOPT_SPI_SPEED = 257, @@ -1317,6 +1544,9 @@ static struct option long_options[] = { {"sharedmem-size", required_argument, 0, AMDFW_OPT_SHAREDMEM_SIZE }, {"soc-name", required_argument, 0, AMDFW_OPT_SOC_NAME }, + {"signed-output", required_argument, 0, AMDFW_OPT_SIGNED_OUTPUT }, + {"signed-addr", required_argument, 0, AMDFW_OPT_SIGNED_ADDR }, + {"config", required_argument, 0, AMDFW_OPT_CONFIG }, {"debug", no_argument, 0, AMDFW_OPT_DEBUG }, {"help", no_argument, 0, AMDFW_OPT_HELP }, @@ -1567,6 +1797,8 @@ int main(int argc, char **argv) uint8_t efs_spi_readmode = 0xff; uint8_t efs_spi_speed = 0xff; uint8_t efs_spi_micron_flag = 0xff; + const char *signed_output_file = NULL; + uint64_t signed_start_addr = 0x0; amd_cb_config cb_config = { 0 }; int debug = 0; @@ -1713,6 +1945,14 @@ int main(int argc, char **argv) } sub = instance = 0; break; + case AMDFW_OPT_SIGNED_OUTPUT: + signed_output_file = optarg; + sub = instance = 0; + break; + case AMDFW_OPT_SIGNED_ADDR: + signed_start_addr = strtoull(optarg, NULL, 10); + sub = instance = 0; + break; case LONGOPT_SPI_READ_MODE: efs_spi_readmode = strtoull(optarg, NULL, 16); sub = instance = 0; @@ -1919,6 +2159,14 @@ int main(int argc, char **argv) ctx.current = ALIGN(ctx.current, 0x10000U); /* TODO: is it necessary? */ ctx.current_table = 0; + /* If the tool is invoked with command-line options to keep the signed PSP + binaries separate, process the signed binaries first. */ + if (signed_output_file && signed_start_addr) + process_signed_psp_firmwares(signed_output_file, + amd_psp_fw_table, + signed_start_addr, + soc_id); + if (cb_config.multi_level) { /* Do 2nd PSP directory followed by 1st */ pspdir2 = new_psp_dir(&ctx, cb_config.multi_level); diff --git a/util/amdfwtool/amdfwtool.h b/util/amdfwtool/amdfwtool.h index 6b13edcb79..fd04064f46 100644 --- a/util/amdfwtool/amdfwtool.h +++ b/util/amdfwtool/amdfwtool.h @@ -275,14 +275,52 @@ typedef struct _ish_directory_table { #define PSP_BOTH_AB (PSP_LVL1_AB | PSP_LVL2_AB) typedef struct _amd_fw_entry { amd_fw_type type; + /* Mendocino and later SoCs use fw_id instead of fw_type. fw_type is still around + for backwards compatibility. fw_id can be populated from the PSP binary file. */ + uint16_t fw_id; char *filename; uint8_t subprog; uint64_t dest; size_t size; int level; uint64_t other; + /* If the binary is signed and the tool is invoked to keep the signed binaries separate, + then this field is populated with the offset of the concerned PSP binary (relative to + BIOS or PSP Directory table). */ + uint64_t addr_signed; + uint32_t file_size; + /* Some files that don't have amd_fw_header have to be skipped from hashing. These files + include but not limited to: *iKek*, *.tkn, *.stkn */ + bool skip_hashing; } amd_fw_entry; +/* Most PSP binaries, if not all, have the following header format. */ +struct amd_fw_header { + uint8_t reserved_0[20]; + uint32_t fw_size_signed; + uint8_t reserved_18[24]; + /* 1 if the image is signed, 0 otherwise */ + uint32_t sig_opt; + uint32_t sig_id; + uint8_t sig_param[16]; + uint32_t comp_opt; + uint8_t reserved_4c[4]; + uint32_t uncomp_size; + uint32_t comp_size; + /* Starting MDN fw_id is populated instead of fw_type. */ + uint16_t fw_id; + uint8_t reserved_5a[18]; + uint32_t size_total; + uint8_t reserved_70[12]; + /* Starting MDN fw_id is populated instead of fw_type. fw_type will still be around + for backwards compatibility. */ + uint8_t fw_type; + uint8_t fw_subtype; + uint8_t fw_subprog; + uint8_t reserved_7f; + uint8_t reserved_80[128]; +} __packed; + typedef struct _amd_cb_config { bool have_whitelist; bool unlock_secure;