summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--util/amdfwtool/amdfwtool.c142
1 files 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 <FILE> Add pubkey\n");
printf("-b | --bootloader <FILE> Add bootloader\n");
printf("-S | --subprogram <number> 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);