From 24f73d4f535157452ba767410290cfdc484ca61c Mon Sep 17 00:00:00 2001 From: Marshall Dawson Date: Mon, 1 Apr 2019 10:48:43 -0600 Subject: util/amdfwtool: Add multilevel PSP directory table Add the ability to generate two PSP directory table levels. The PSP is capable of supporting two levels, with the primary intended to remain pristine for the life of the system, and the second updatable. In the event the second becomes corrupted, the primary is still sufficient to allow a recovery of the other. This patch modifies no directory table structures currently in use. The soc or southbridge must pass an argument to force building the secondary table. BUG=b:126593573 TEST=Used with WIP Picasso Change-Id: Id321f5142e461d4a7f3343c0835a09a1a1128728 Signed-off-by: Marshall Dawson Reviewed-on: https://review.coreboot.org/c/coreboot/+/33398 Tested-by: build bot (Jenkins) Reviewed-by: Martin Roth --- util/amdfwtool/amdfwtool.c | 142 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 110 insertions(+), 32 deletions(-) diff --git a/util/amdfwtool/amdfwtool.c b/util/amdfwtool/amdfwtool.c index 4a05dfe8a3..ac9ee48888 100644 --- a/util/amdfwtool/amdfwtool.c +++ b/util/amdfwtool/amdfwtool.c @@ -81,13 +81,15 @@ #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" #define EMBEDDED_FW_SIGNATURE 0x55aa55aa -#define PSP_COOKIE 0x50535024 /* 'PSP$' */ -#define PSP2_COOKIE 0x50535032 /* 'PSP2' */ +#define PSP_COOKIE 0x50535024 /* 'PSP$' */ +#define PSPL2_COOKIE 0x324c5024 /* '2LP$' */ +#define PSP2_COOKIE 0x50535032 /* 'PSP2' */ /* * Beginning with Family 15h Models 70h-7F, a.k.a Stoney Ridge, the PSP @@ -176,6 +178,7 @@ static void usage(void) printf("\nPSP options:\n"); printf("-A | --combo-capable Place PSP directory pointer at Embedded Firmware\n"); printf(" offset able to support combo directory\n"); + printf("-M | --multilevel Generate primary and secondary tables\n"); printf("-p | --pubkey Add pubkey\n"); printf("-b | --bootloader Add bootloader\n"); printf("-S | --subprogram Sets subprogram field for the next firmware\n"); @@ -213,36 +216,44 @@ typedef enum _amd_fw_type { AMD_FW_PSP_SMU_FIRMWARE2 = 18, AMD_PSP_FUSE_CHAIN = 11, AMD_FW_PSP_SMUSCS = 95, - + AMD_FW_L2_PTR = 0x40, AMD_FW_IMC, AMD_FW_GEC, AMD_FW_XHCI, AMD_FW_INVALID, } amd_fw_type; +#define PSP_LVL1 0x1 +#define PSP_LVL2 0x2 +#define PSP_BOTH (PSP_LVL1 | PSP_LVL2) typedef struct _amd_fw_entry { amd_fw_type type; uint8_t subprog; char *filename; + int level; uint64_t other; } amd_fw_entry; static amd_fw_entry amd_psp_fw_table[] = { - { .type = AMD_FW_PSP_PUBKEY }, - { .type = AMD_FW_PSP_BOOTLOADER }, - { .type = AMD_FW_PSP_SMU_FIRMWARE }, - { .type = AMD_FW_PSP_RECOVERY }, - { .type = AMD_FW_PSP_RTM_PUBKEY }, - { .type = AMD_FW_PSP_SECURED_OS }, - { .type = AMD_FW_PSP_NVRAM }, - { .type = AMD_FW_PSP_SECURED_DEBUG }, - { .type = AMD_FW_PSP_TRUSTLETS }, - { .type = AMD_FW_PSP_TRUSTLETKEY }, - { .type = AMD_FW_PSP_SMU_FIRMWARE, .subprog = 1 }, - { .type = AMD_FW_PSP_SMU_FIRMWARE2, .subprog = 1 }, - { .type = AMD_FW_PSP_SMU_FIRMWARE2 }, - { .type = AMD_FW_PSP_SMUSCS }, - { .type = AMD_PSP_FUSE_CHAIN }, + { .type = AMD_FW_PSP_PUBKEY, .level = PSP_BOTH }, + { .type = AMD_FW_PSP_BOOTLOADER, .level = PSP_BOTH }, + { .type = AMD_FW_PSP_SMU_FIRMWARE, .subprog = 0, .level = PSP_BOTH }, + { .type = AMD_FW_PSP_RECOVERY, .level = PSP_LVL1 }, + { .type = AMD_FW_PSP_RTM_PUBKEY, .level = PSP_BOTH }, + { .type = AMD_FW_PSP_SECURED_OS, .level = PSP_LVL2 }, + { .type = AMD_FW_PSP_NVRAM, .level = PSP_LVL2 }, + { .type = AMD_FW_PSP_SMU_FIRMWARE, .subprog = 2, .level = PSP_BOTH }, + { .type = AMD_FW_PSP_SECURED_DEBUG, .level = PSP_LVL2 }, + { .type = AMD_FW_PSP_TRUSTLETS, .level = PSP_LVL2 }, + { .type = AMD_FW_PSP_TRUSTLETKEY, .level = PSP_LVL2 }, + { .type = AMD_FW_PSP_SMU_FIRMWARE2, .subprog = 2, .level = PSP_BOTH }, + { .type = AMD_FW_PSP_SMU_FIRMWARE, .subprog = 1, .level = PSP_BOTH }, + { .type = AMD_FW_PSP_SMU_FIRMWARE2, .subprog = 1, .level = PSP_BOTH }, + { .type = AMD_FW_PSP_SMU_FIRMWARE2, .level = PSP_BOTH }, + { .type = AMD_FW_PSP_SMUSCS, .level = PSP_BOTH }, + { .type = AMD_PSP_FUSE_CHAIN, .level = PSP_LVL2 }, + { .type = AMD_FW_PSP_SMU_FIRMWARE, .subprog = 1, .level = PSP_BOTH }, + { .type = AMD_FW_PSP_SMU_FIRMWARE2, .subprog = 1, .level = PSP_BOTH }, { .type = AMD_FW_INVALID }, }; @@ -319,11 +330,20 @@ typedef struct _context { #define BUFF_TO_RUN(ctx, ptr) RUN_OFFSET((ctx), ((char *)(ptr) - (ctx).rom)) #define BUFF_ROOM(ctx) ((ctx).rom_size - (ctx).current) -static void *new_psp_dir(context *ctx) +static void *new_psp_dir(context *ctx, int multi) { void *ptr; - ctx->current = ALIGN(ctx->current, TABLE_ALIGNMENT); + /* + * Force both onto boundary when multi. Primary table is after + * updatable table, so alignment ensures primary can stay intact + * if secondary is reprogrammed. + */ + if (multi) + ctx->current = ALIGN(ctx->current, TABLE_ERASE_ALIGNMENT); + else + ctx->current = ALIGN(ctx->current, TABLE_ALIGNMENT); + ptr = BUFF_CURRENT(*ctx); ctx->current += sizeof(psp_directory_header) + MAX_PSP_ENTRIES * sizeof(psp_directory_entry); @@ -343,9 +363,15 @@ static void *new_combo_dir(context *ctx) static void fill_dir_header(void *directory, uint32_t count, uint32_t cookie) { - if (cookie == PSP2_COOKIE) { + psp_combo_directory *cdir = directory; + psp_directory_table *dir = directory; + + if (!count) + return; + + switch (cookie) { + case PSP2_COOKIE: /* caller is responsible for lookup mode */ - psp_combo_directory *cdir = directory; cdir->header.cookie = cookie; cdir->header.num_entries = count; cdir->header.reserved[0] = 0; @@ -356,8 +382,9 @@ static void fill_dir_header(void *directory, uint32_t count, uint32_t cookie) + sizeof(cdir->header.num_entries) + sizeof(cdir->header.lookup) + 2 * sizeof(cdir->header.reserved[0])); - } else { - psp_directory_table *dir = directory; + break; + case PSP_COOKIE: + case PSPL2_COOKIE: dir->header.cookie = cookie; dir->header.num_entries = count; dir->header.reserved = 0; @@ -366,6 +393,7 @@ static void fill_dir_header(void *directory, uint32_t count, uint32_t cookie) count * sizeof(psp_directory_entry) + sizeof(dir->header.num_entries) + sizeof(dir->header.reserved)); + break; } } @@ -444,14 +472,34 @@ static void integrate_firmwares(context *ctx, static void integrate_psp_firmwares(context *ctx, psp_directory_table *pspdir, - amd_fw_entry *fw_table) + psp_directory_table *pspdir2, + amd_fw_entry *fw_table, + uint32_t cookie) { ssize_t bytes; unsigned int i, count; + int level; + + /* This function can create a primary table, a secondary table, or a + * flattened table which contains all applicable types. These if-else + * statements infer what the caller intended. If a 2nd-level cookie + * is passed, clearly a 2nd-level table is intended. However, a + * 1st-level cookie may indicate level 1 or flattened. If the caller + * passes a pointer to a 2nd-level table, then assume not flat. + */ + if (cookie == PSPL2_COOKIE) + level = PSP_LVL2; + else if (pspdir2) + level = PSP_LVL1; + else + level = PSP_BOTH; ctx->current = ALIGN(ctx->current, BLOB_ALIGNMENT); for (i = 0, count = 0; fw_table[i].type != AMD_FW_INVALID; i++) { + if (!(fw_table[i].level & level)) + continue; + if (fw_table[i].type == AMD_PSP_FUSE_CHAIN) { pspdir->entries[count].type = fw_table[i].type; pspdir->entries[count].subprog = fw_table[i].subprog; @@ -506,16 +554,28 @@ static void integrate_psp_firmwares(context *ctx, } } + if (pspdir2) { + pspdir->entries[count].type = AMD_FW_L2_PTR; + pspdir->entries[count].subprog = 0; + pspdir->entries[count].rsvd = 0; + pspdir->entries[count].size = sizeof(pspdir2->header) + + pspdir2->header.num_entries + * sizeof(psp_directory_entry); + + pspdir->entries[count].addr = BUFF_TO_RUN(*ctx, pspdir2); + count++; + } + if (count > MAX_PSP_ENTRIES) { printf("Error: PSP entries exceed max allowed items\n"); free(ctx->rom); exit(1); } - fill_dir_header(pspdir, count, PSP_COOKIE); + fill_dir_header(pspdir, count, cookie); } -static const char *optstring = "x:i:g:AS:p:b:s:r:k:c:n:d:t:u:w:m:T:o:f:l:h"; +static const char *optstring = "x:i:g:AMS:p:b:s:r:k:c:n:d:t:u:w:m:T:o:f:l:h"; static struct option long_options[] = { {"xhci", required_argument, 0, 'x' }, @@ -523,6 +583,7 @@ static struct option long_options[] = { {"gec", required_argument, 0, 'g' }, /* PSP */ {"combo-capable", no_argument, 0, 'A' }, + {"multilevel", no_argument, 0, 'M' }, {"subprogram", required_argument, 0, 'S' }, {"pubkey", required_argument, 0, 'p' }, {"bootloader", required_argument, 0, 'b' }, @@ -599,6 +660,7 @@ int main(int argc, char **argv) uint32_t romsig_offset; uint32_t rom_base_address; uint8_t sub = 0; + int multi = 0; while (1) { int optindex = 0; @@ -611,19 +673,22 @@ int main(int argc, char **argv) switch (c) { case 'x': register_fw_filename(AMD_FW_XHCI, sub, optarg); - sub = 0; /* subprogram is N/A but clear anyway */ + sub = 0; break; case 'i': register_fw_filename(AMD_FW_IMC, sub, optarg); - sub = 0; /* subprogram is N/A but clear anyway */ + sub = 0; break; case 'g': register_fw_filename(AMD_FW_GEC, sub, optarg); - sub = 0; /* subprogram is N/A but clear anyway */ + sub = 0; break; case 'A': comboable = 1; break; + case 'M': + multi = 1; + break; case 'S': sub = (uint8_t)strtoul(optarg, &tmp, 16); break; @@ -788,8 +853,21 @@ int main(int argc, char **argv) ctx.current = ALIGN(ctx.current, 0x10000U); /* todo: is necessary? */ - pspdir = new_psp_dir(&ctx); - integrate_psp_firmwares(&ctx, pspdir, amd_psp_fw_table); + if (multi) { + /* Do 2nd PSP directory followed by 1st */ + psp_directory_table *pspdir2 = new_psp_dir(&ctx, multi); + integrate_psp_firmwares(&ctx, pspdir2, 0, + amd_psp_fw_table, PSPL2_COOKIE); + + pspdir = new_psp_dir(&ctx, multi); + integrate_psp_firmwares(&ctx, pspdir, pspdir2, + amd_psp_fw_table, PSP_COOKIE); + } else { + /* flat: PSP 1 cookie and no pointer to 2nd table */ + pspdir = new_psp_dir(&ctx, multi); + integrate_psp_firmwares(&ctx, pspdir, 0, + amd_psp_fw_table, PSP_COOKIE); + } if (comboable) amd_romsig->comboable = BUFF_TO_RUN(ctx, pspdir); -- cgit v1.2.3