amdfwtool: Add support for AMD's BIOS A/B recovery feature

The rom layout for A/B recovery:
EFS -> PSP L1 0x48 -> PSP L2 A -> BIOS L2 A
              0x4A -> PSP L2 B -> BIOS L2 B

The coreboot doesn't implement the AMD's A/B recovery. This is only
for the ROM layout. To save some flash space, the entire B section can
be eliminated.

To enable A/B recovery in PSP layout, add "--recovery-ab" to
amdfwtool.

TEST=Majolica(Cezanne)

Change-Id: I27f5d3476f648fcecafb8d258ccb6cfad4f50036
Signed-off-by: Zheng Bao <fishbaozi@gmail.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/56773
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Felix Held <felix-coreboot@felixheld.de>
diff --git a/util/amdfwtool/amdfwtool.c b/util/amdfwtool/amdfwtool.c
index e586748..38629b1 100644
--- a/util/amdfwtool/amdfwtool.c
+++ b/util/amdfwtool/amdfwtool.c
@@ -165,6 +165,7 @@
 	printf("--load-s0i3                    Set if load s0i3 firmware\n");
 	printf("--verstage <FILE>              Add verstage\n");
 	printf("--verstage_sig                 Add verstage signature\n");
+	printf("--recovery-ab                  Use the recovery A/B layout\n");
 	printf("\nBIOS options:\n");
 	printf("--instance <number>            Sets instance field for the next BIOS\n");
 	printf("                               firmware\n");
@@ -220,60 +221,60 @@
 }
 
 amd_fw_entry amd_psp_fw_table[] = {
-	{ .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_PUBKEY, .level = PSP_BOTH | PSP_BOTH_AB },
+	{ .type = AMD_FW_PSP_BOOTLOADER, .level = PSP_BOTH | PSP_LVL1_AB },
+	{ .type = AMD_FW_PSP_SMU_FIRMWARE, .subprog = 0, .level = PSP_BOTH | PSP_LVL2_AB },
 	{ .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_DEBUG_UNLOCK, .level = PSP_LVL2 },
-	{ .type = AMD_HW_IPCFG, .level = PSP_LVL2 },
-	{ .type = AMD_WRAPPED_IKEK, .level = PSP_BOTH },
-	{ .type = AMD_TOKEN_UNLOCK, .level = PSP_BOTH },
-	{ .type = AMD_SEC_GASKET, .subprog = 0, .level = PSP_BOTH },
-	{ .type = AMD_SEC_GASKET, .subprog = 2, .level = PSP_BOTH },
-	{ .type = AMD_SEC_GASKET, .subprog = 1, .level = PSP_BOTH },
-	{ .type = AMD_MP2_FW, .subprog = 2, .level = PSP_LVL2 },
-	{ .type = AMD_MP2_FW, .subprog = 1, .level = PSP_LVL2 },
-	{ .type = AMD_MP2_FW, .subprog = 0, .level = PSP_LVL2 },
-	{ .type = AMD_DRIVER_ENTRIES, .level = PSP_LVL2 },
-	{ .type = AMD_FW_KVM_IMAGE, .level = PSP_LVL2},
-	{ .type = AMD_S0I3_DRIVER, .level = PSP_LVL2 },
-	{ .type = AMD_VBIOS_BTLOADER, .level = PSP_BOTH },
-	{ .type = AMD_FW_TOS_SEC_POLICY, .level = PSP_BOTH },
-	{ .type = AMD_FW_USB_PHY, .level = PSP_LVL2 },
-	{ .type = AMD_FW_DRTM_TA, .level = PSP_LVL2 },
-	{ .type = AMD_FW_KEYDB_BL, .level = PSP_BOTH },
-	{ .type = AMD_FW_KEYDB_TOS, .level = PSP_LVL2 },
-	{ .type = AMD_FW_SPL, .level = PSP_LVL2 },
-	{ .type = AMD_FW_DMCU_ERAM, .level = PSP_LVL2 },
-	{ .type = AMD_FW_DMCU_ISR, .level = PSP_LVL2 },
-	{ .type = AMD_RPMC_NVRAM, .level = PSP_LVL2 },
-	{ .type = AMD_FW_PSP_BOOTLOADER_AB, .level = PSP_LVL2 },
-	{ .type = AMD_ABL0, .level = PSP_BOTH },
-	{ .type = AMD_ABL1, .level = PSP_BOTH },
-	{ .type = AMD_ABL2, .level = PSP_BOTH },
-	{ .type = AMD_ABL3, .level = PSP_BOTH },
-	{ .type = AMD_ABL4, .level = PSP_BOTH },
-	{ .type = AMD_ABL5, .level = PSP_BOTH },
-	{ .type = AMD_ABL6, .level = PSP_BOTH },
-	{ .type = AMD_ABL7, .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_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_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 },
+	{ .type = AMD_FW_PSP_SMU_FIRMWARE, .subprog = 1, .level = PSP_BOTH | PSP_LVL2_AB },
+	{ .type = AMD_FW_PSP_SMU_FIRMWARE2, .subprog = 1, .level = PSP_BOTH | PSP_LVL2_AB },
+	{ .type = AMD_FW_PSP_SMU_FIRMWARE2, .level = PSP_BOTH | PSP_LVL2_AB },
+	{ .type = AMD_FW_PSP_SMUSCS, .level = PSP_BOTH },
+	{ .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_BOTH_AB },
+	{ .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 },
+	{ .type = AMD_SEC_GASKET, .subprog = 1, .level = PSP_BOTH | PSP_LVL2_AB },
+	{ .type = AMD_MP2_FW, .subprog = 2, .level = PSP_LVL2 | PSP_LVL2_AB },
+	{ .type = AMD_MP2_FW, .subprog = 1, .level = PSP_LVL2 | PSP_LVL2_AB },
+	{ .type = AMD_MP2_FW, .subprog = 0, .level = PSP_LVL2 | PSP_LVL2_AB },
+	{ .type = AMD_DRIVER_ENTRIES, .level = PSP_LVL2 | PSP_LVL2_AB },
+	{ .type = AMD_FW_KVM_IMAGE, .level = PSP_LVL2 | PSP_LVL2_AB },
+	{ .type = AMD_S0I3_DRIVER, .level = PSP_LVL2 | PSP_LVL2_AB },
+	{ .type = AMD_VBIOS_BTLOADER, .level = PSP_BOTH | PSP_LVL2_AB },
+	{ .type = AMD_FW_TOS_SEC_POLICY, .level = PSP_BOTH | PSP_LVL2_AB },
+	{ .type = AMD_FW_USB_PHY, .level = PSP_LVL2 | PSP_LVL2_AB },
+	{ .type = AMD_FW_DRTM_TA, .level = PSP_LVL2 | PSP_LVL2_AB },
+	{ .type = AMD_FW_KEYDB_BL, .level = PSP_BOTH | PSP_LVL2_AB },
+	{ .type = AMD_FW_KEYDB_TOS, .level = PSP_LVL2 | PSP_LVL2_AB },
+	{ .type = AMD_FW_SPL, .level = PSP_LVL2 | PSP_LVL2_AB },
+	{ .type = AMD_FW_DMCU_ERAM, .level = PSP_LVL2 | PSP_LVL2_AB },
+	{ .type = AMD_FW_DMCU_ISR, .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_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 },
+	{ .type = AMD_ABL3, .level = PSP_BOTH | PSP_LVL2_AB },
+	{ .type = AMD_ABL4, .level = PSP_BOTH | PSP_LVL2_AB },
+	{ .type = AMD_ABL5, .level = PSP_BOTH | PSP_LVL2_AB },
+	{ .type = AMD_ABL6, .level = PSP_BOTH | PSP_LVL2_AB },
+	{ .type = AMD_ABL7, .level = PSP_BOTH | PSP_LVL2_AB },
+	{ .type = AMD_FW_PSP_SMU_FIRMWARE, .subprog = 1, .level = PSP_BOTH | PSP_LVL2_AB },
+	{ .type = AMD_FW_PSP_SMU_FIRMWARE2, .subprog = 1, .level = PSP_BOTH | PSP_LVL2_AB },
 	{ .type = AMD_FW_PSP_WHITELIST, .level = PSP_LVL2 },
-	{ .type = AMD_FW_PSP_VERSTAGE, .level = PSP_BOTH },
-	{ .type = AMD_FW_VERSTAGE_SIG, .level = PSP_BOTH },
+	{ .type = AMD_FW_PSP_VERSTAGE, .level = PSP_BOTH | PSP_BOTH_AB },
+	{ .type = AMD_FW_VERSTAGE_SIG, .level = PSP_BOTH | PSP_BOTH_AB },
 	{ .type = AMD_FW_INVALID },
 };
 
@@ -405,6 +406,7 @@
 		ctx->current = ALIGN(ctx->current, TABLE_ALIGNMENT);
 
 	ptr = BUFF_CURRENT(*ctx);
+	((psp_directory_header *)ptr)->num_entries = 0;
 	((psp_directory_header *)ptr)->additional_info = 0;
 	((psp_directory_header *)ptr)->additional_info_fields.address_mode = ctx->address_mode;
 	ctx->current += sizeof(psp_directory_header)
@@ -662,9 +664,34 @@
 	}
 }
 
+static void integrate_psp_ab(context *ctx, psp_directory_table *pspdir,
+		psp_directory_table *pspdir2, amd_fw_type ab)
+{
+	uint32_t count;
+	uint32_t current_table_save;
+
+	current_table_save = ctx->current_table;
+	ctx->current_table = (char *)pspdir - ctx->rom;
+	count = pspdir->header.num_entries;
+	assert_fw_entry(count, MAX_PSP_ENTRIES, ctx);
+	pspdir->entries[count].type = (uint8_t)ab;
+	pspdir->entries[count].subprog = 0;
+	pspdir->entries[count].rsvd = 0;
+	pspdir->entries[count].addr = BUFF_TO_RUN_MODE(*ctx, pspdir2, ADDRESS_MODE_1_REL_BIOS);
+	pspdir->entries[count].address_mode = SET_ADDR_MODE(pspdir, ADDRESS_MODE_1_REL_BIOS);
+	pspdir->entries[count].size = pspdir2->header.num_entries *
+				sizeof(psp_directory_entry) +
+				sizeof(psp_directory_header);
+
+	count++;
+	pspdir->header.num_entries = count;
+	ctx->current_table = current_table_save;
+}
+
 static void integrate_psp_firmwares(context *ctx,
 					psp_directory_table *pspdir,
 					psp_directory_table *pspdir2,
+					psp_directory_table *pspdir2_b,
 					amd_fw_entry *fw_table,
 					uint32_t cookie,
 					amd_cb_config *cb_config)
@@ -673,6 +700,7 @@
 	unsigned int i, count;
 	int level;
 	uint32_t current_table_save;
+	bool recovery_ab = cb_config->recovery_ab;
 
 	/* This function can create a primary table, a secondary table, or a
 	 * flattened table which contains all applicable types.  These if-else
@@ -690,6 +718,14 @@
 	else
 		level = PSP_BOTH;
 
+	if (recovery_ab) {
+		if (cookie == PSPL2_COOKIE)
+			level = PSP_LVL2_AB;
+		else if (pspdir2)
+			level = PSP_LVL1_AB;
+		else
+			level = PSP_BOTH_AB;
+	}
 	current_table_save = ctx->current_table;
 	ctx->current_table = (char *)pspdir - ctx->rom;
 	ctx->current = ALIGN(ctx->current, TABLE_ALIGNMENT);
@@ -771,7 +807,13 @@
 		}
 	}
 
-	if (pspdir2) {
+	if (recovery_ab && (pspdir2 != NULL)) {
+		pspdir->header.num_entries = count;
+		integrate_psp_ab(ctx, pspdir, pspdir2, AMD_FW_RECOVERYAB_A);
+		if (pspdir2_b != NULL)
+			integrate_psp_ab(ctx, pspdir, pspdir2_b, AMD_FW_RECOVERYAB_B);
+		count = pspdir->header.num_entries;
+	} else if (pspdir2 != NULL) {
 		assert_fw_entry(count, MAX_PSP_ENTRIES, ctx);
 		pspdir->entries[count].type = AMD_FW_L2_PTR;
 		pspdir->entries[count].subprog = 0;
@@ -791,6 +833,42 @@
 	ctx->current_table = current_table_save;
 }
 
+static void add_psp_firmware_entry(context *ctx,
+					psp_directory_table *pspdir,
+					void *table, amd_fw_type type, uint32_t size)
+{
+	uint32_t count = pspdir->header.num_entries;
+	uint32_t index;
+	uint32_t current_table_save;
+
+	current_table_save = ctx->current_table;
+	ctx->current_table = (char *)pspdir - ctx->rom;
+
+	/* If there is an entry of "type", replace it. */
+	for (index = 0; index < count; index++) {
+		if (pspdir->entries[index].type == (uint8_t)type)
+			break;
+	}
+
+	assert_fw_entry(count, MAX_PSP_ENTRIES, ctx);
+	pspdir->entries[index].type = (uint8_t)type;
+	pspdir->entries[index].subprog = 0;
+	pspdir->entries[index].rsvd = 0;
+	pspdir->entries[index].addr = BUFF_TO_RUN(*ctx, table);
+	pspdir->entries[index].address_mode = SET_ADDR_MODE_BY_TABLE(pspdir);
+	pspdir->entries[index].size = size;
+	if (index == count)
+		count++;
+
+	pspdir->header.num_entries = count;
+	pspdir->header.checksum = fletcher32(&pspdir->header.num_entries,
+					count * sizeof(psp_directory_entry)
+					+ sizeof(pspdir->header.num_entries)
+					+ sizeof(pspdir->header.additional_info));
+
+	ctx->current_table = current_table_save;
+}
+
 static void *new_bios_dir(context *ctx, bool multi)
 {
 	void *ptr;
@@ -1078,6 +1156,7 @@
 	AMDFW_OPT_IMC,
 	AMDFW_OPT_GEC,
 	AMDFW_OPT_COMBO,
+	AMDFW_OPT_RECOVERY_AB,
 	AMDFW_OPT_MULTILEVEL,
 	AMDFW_OPT_NVRAM,
 
@@ -1125,6 +1204,7 @@
 	{"gec",              required_argument, 0, AMDFW_OPT_GEC },
 	/* PSP Directory Table items */
 	{"combo-capable",          no_argument, 0, AMDFW_OPT_COMBO },
+	{"recovery-ab",            no_argument, 0, AMDFW_OPT_RECOVERY_AB },
 	{"multilevel",             no_argument, 0, AMDFW_OPT_MULTILEVEL },
 	{"nvram",            required_argument, 0, AMDFW_OPT_NVRAM },
 	{"soft-fuse",        required_argument, 0, AMDFW_OPT_FUSE },
@@ -1337,7 +1417,9 @@
 	char *tmp;
 	char *rom = NULL;
 	embedded_firmware *amd_romsig;
-	psp_directory_table *pspdir;
+	psp_directory_table *pspdir = NULL;
+	psp_directory_table *pspdir2 = NULL;
+	psp_directory_table *pspdir2_b = NULL;
 	bool comboable = false;
 	int fuse_defined = 0;
 	int targetfd;
@@ -1365,6 +1447,7 @@
 	cb_config.load_mp2_fw = false;
 	cb_config.s0i3 = false;
 	cb_config.multi_level = false;
+	cb_config.recovery_ab = false;
 
 	while (1) {
 		int optindex = 0;
@@ -1390,6 +1473,9 @@
 		case AMDFW_OPT_COMBO:
 			comboable = true;
 			break;
+		case AMDFW_OPT_RECOVERY_AB:
+			cb_config.recovery_ab = true;
+			break;
 		case AMDFW_OPT_MULTILEVEL:
 			cb_config.multi_level = true;
 			break;
@@ -1594,6 +1680,10 @@
 		retval = 1;
 	}
 
+	if (cb_config.recovery_ab) {
+		cb_config.multi_level = true;
+	}
+
 	if (retval) {
 		usage();
 		return retval;
@@ -1678,16 +1768,26 @@
 
 	if (cb_config.multi_level) {
 		/* Do 2nd PSP directory followed by 1st */
-		psp_directory_table *pspdir2 = new_psp_dir(&ctx, cb_config.multi_level);
-		integrate_psp_firmwares(&ctx, pspdir2, NULL,
-						amd_psp_fw_table, PSPL2_COOKIE, &cb_config);
+		pspdir2 = new_psp_dir(&ctx, cb_config.multi_level);
+		integrate_psp_firmwares(&ctx, pspdir2, NULL, NULL,
+					amd_psp_fw_table, PSPL2_COOKIE, &cb_config);
+		if (cb_config.recovery_ab) {
+			/* B is same as above directories for A */
+			/* Skip creating pspdir2_b here to save flash space. Related
+			 * biosdir2_b will be skipped automatically. */
+			pspdir2_b = new_psp_dir(&ctx, cb_config.multi_level);
+			integrate_psp_firmwares(&ctx, pspdir2_b, NULL, NULL,
+					amd_psp_fw_table, PSPL2_COOKIE, &cb_config);
+		} else {
+			pspdir2_b = NULL; /* More explicitly */
+		}
 		pspdir = new_psp_dir(&ctx, cb_config.multi_level);
-		integrate_psp_firmwares(&ctx, pspdir, pspdir2,
+		integrate_psp_firmwares(&ctx, pspdir, pspdir2, pspdir2_b,
 						amd_psp_fw_table, PSP_COOKIE, &cb_config);
 	} else {
 		/* flat: PSP 1 cookie and no pointer to 2nd table */
 		pspdir = new_psp_dir(&ctx, cb_config.multi_level);
-		integrate_psp_firmwares(&ctx, pspdir, NULL,
+		integrate_psp_firmwares(&ctx, pspdir, NULL, NULL,
 						amd_psp_fw_table, PSP_COOKIE, &cb_config);
 	}
 
@@ -1709,17 +1809,32 @@
 #endif
 
 	if (have_bios_tables(amd_bios_table)) {
-		bios_directory_table *biosdir;
+		bios_directory_table *biosdir = NULL;
 		if (cb_config.multi_level) {
 			/* Do 2nd level BIOS directory followed by 1st */
-			bios_directory_table *biosdir2 =
-						new_bios_dir(&ctx, cb_config.multi_level);
+			bios_directory_table *biosdir2 = NULL;
+			bios_directory_table *biosdir2_b = NULL;
+
+			biosdir2 = new_bios_dir(&ctx, cb_config.multi_level);
+
 			integrate_bios_firmwares(&ctx, biosdir2, NULL,
 						amd_bios_table, BDT2_COOKIE, &cb_config);
-
-			biosdir = new_bios_dir(&ctx, cb_config.multi_level);
-			integrate_bios_firmwares(&ctx, biosdir, biosdir2,
+			if (cb_config.recovery_ab) {
+				if (pspdir2_b != NULL) {
+					biosdir2_b = new_bios_dir(&ctx, cb_config.multi_level);
+					integrate_bios_firmwares(&ctx, biosdir2_b, NULL,
+						amd_bios_table, BDT2_COOKIE, &cb_config);
+				}
+				add_psp_firmware_entry(&ctx, pspdir2, biosdir2,
+					AMD_FW_BIOS_TABLE, TABLE_ALIGNMENT);
+				if (pspdir2_b != NULL)
+					add_psp_firmware_entry(&ctx, pspdir2_b, biosdir2_b,
+						AMD_FW_BIOS_TABLE, TABLE_ALIGNMENT);
+			} else {
+				biosdir = new_bios_dir(&ctx, cb_config.multi_level);
+				integrate_bios_firmwares(&ctx, biosdir, biosdir2,
 						amd_bios_table, BDT1_COOKIE, &cb_config);
+			}
 		} else {
 			/* flat: BDT1 cookie and no pointer to 2nd table */
 			biosdir = new_bios_dir(&ctx, cb_config.multi_level);
@@ -1730,7 +1845,8 @@
 		case PLATFORM_RENOIR:
 		case PLATFORM_LUCIENNE:
 		case PLATFORM_CEZANNE:
-			amd_romsig->bios3_entry = BUFF_TO_RUN(ctx, biosdir);
+			if (!cb_config.recovery_ab)
+				amd_romsig->bios3_entry = BUFF_TO_RUN(ctx, biosdir);
 			break;
 		case PLATFORM_MENDOCINO:
 			break;
diff --git a/util/amdfwtool/amdfwtool.h b/util/amdfwtool/amdfwtool.h
index 79e52fc..70afe3c 100644
--- a/util/amdfwtool/amdfwtool.h
+++ b/util/amdfwtool/amdfwtool.h
@@ -43,6 +43,9 @@
 	AMD_FW_USB_PHY = 0x44,
 	AMD_FW_TOS_SEC_POLICY = 0x45,
 	AMD_FW_DRTM_TA = 0x47,
+	AMD_FW_RECOVERYAB_A = 0x48,
+	AMD_FW_RECOVERYAB_B = 0x4A,
+	AMD_FW_BIOS_TABLE = 0x49,
 	AMD_FW_KEYDB_BL = 0x50,
 	AMD_FW_KEYDB_TOS = 0x51,
 	AMD_FW_PSP_VERSTAGE = 0x52,
@@ -209,7 +212,10 @@
 
 #define BDT_LVL1 (1 << 0)
 #define BDT_LVL2 (1 << 1)
+#define BDT_LVL1_AB (1 << 2)
+#define BDT_LVL2_AB (1 << 3)
 #define BDT_BOTH (BDT_LVL1 | BDT_LVL2)
+#define BDT_BOTH_AB (BDT_LVL1_AB | BDT_LVL2_AB)
 typedef struct _amd_bios_entry {
 	amd_bios_type type;
 	char *filename;
@@ -235,7 +241,10 @@
 
 #define PSP_LVL1 (1 << 0)
 #define PSP_LVL2 (1 << 1)
+#define PSP_LVL1_AB (1 << 2)
+#define PSP_LVL2_AB (1 << 3)
 #define PSP_BOTH (PSP_LVL1 | PSP_LVL2)
+#define PSP_BOTH_AB (PSP_LVL1_AB | PSP_LVL2_AB)
 typedef struct _amd_fw_entry {
 	amd_fw_type type;
 	char *filename;
@@ -252,6 +261,7 @@
 	bool multi_level;
 	bool s0i3;
 	bool have_mb_spl;
+	bool recovery_ab;
 } amd_cb_config;
 
 void register_fw_fuse(char *str);
diff --git a/util/amdfwtool/data_parse.c b/util/amdfwtool/data_parse.c
index d27b7bb..c69a389 100644
--- a/util/amdfwtool/data_parse.c
+++ b/util/amdfwtool/data_parse.c
@@ -78,18 +78,18 @@
 	}
 }
 
-#define SET_LEVEL(tableptr, l, TABLE)                    \
+#define SET_LEVEL(tableptr, l, TABLE, ab)     \
 	do {                                             \
 		switch ((l)) {                           \
 		case '1':				 \
-			(tableptr)->level = TABLE##_LVL1;\
+			(tableptr)->level = ab ? TABLE##_LVL1_AB : TABLE##_LVL1; \
 			break;                           \
 		case '2':                                \
-			(tableptr)->level = TABLE##_LVL2;\
+			(tableptr)->level = ab ? TABLE##_LVL2_AB : TABLE##_LVL2; \
 			break;                           \
 		case 'b':                                \
 		case 'B':                                \
-			(tableptr)->level = TABLE##_BOTH;\
+			(tableptr)->level = ab ? TABLE##_BOTH_AB : TABLE##_BOTH; \
 			break;                           \
 		default:                                 \
 			/* use default value */          \
@@ -114,9 +114,20 @@
 		} else {
 			fw_type = AMD_FW_SKIP;
 		}
+	} else if (strcmp(fw_name, "PSPBTLDR_AB_STAGE1_FILE") == 0) {
+		if (cb_config->recovery_ab) {
+			fw_type = AMD_FW_PSP_BOOTLOADER;
+			subprog = 0;
+		} else {
+			fw_type = AMD_FW_SKIP;
+		}
 	} else if (strcmp(fw_name, "PSPBTLDR_FILE") == 0) {
-		fw_type = AMD_FW_PSP_BOOTLOADER;
-		subprog = 0;
+		if (!cb_config->recovery_ab) {
+			fw_type = AMD_FW_PSP_BOOTLOADER;
+			subprog = 0;
+		} else {
+			fw_type = AMD_FW_SKIP;
+		}
 	} else if (strcmp(fw_name, "AMD_PUBKEY_FILE") == 0) {
 		fw_type = AMD_FW_PSP_PUBKEY;
 		subprog = 0;
@@ -302,7 +313,7 @@
 		fw_type = AMD_RPMC_NVRAM;
 		subprog = 0;
 	} else if (strcmp(fw_name, "PSPBTLDR_AB_FILE") == 0) {
-		if (!cb_config->have_whitelist) {
+		if (!cb_config->have_whitelist || cb_config->recovery_ab) {
 			fw_type = AMD_FW_PSP_BOOTLOADER_AB;
 			subprog = 0;
 		} else {
@@ -320,7 +331,8 @@
 			/* instance are not used in PSP table */
 			if (psp_tableptr->type == fw_type && psp_tableptr->subprog == subprog) {
 				psp_tableptr->filename = filename;
-				SET_LEVEL(psp_tableptr, level_to_set, PSP);
+				SET_LEVEL(psp_tableptr, level_to_set, PSP,
+					cb_config->recovery_ab);
 				break;
 			}
 			psp_tableptr++;
@@ -378,7 +390,8 @@
 					bhd_tableptr->subpr == subprog &&
 					bhd_tableptr->inst  == instance) {
 				bhd_tableptr->filename = filename;
-				SET_LEVEL(bhd_tableptr, level_to_set, BDT);
+				SET_LEVEL(bhd_tableptr, level_to_set, BDT,
+					cb_config->recovery_ab);
 				break;
 			}
 			bhd_tableptr++;
@@ -512,7 +525,10 @@
 				/* If the optional level field is present,
 				   extract the level char. */
 				if (match[3].rm_so != 0) {
-					ch_lvl = oneline[match[3].rm_so + 1];
+					if (cb_config->recovery_ab == 0)
+						ch_lvl = oneline[match[3].rm_so + 1];
+					else
+						ch_lvl = oneline[match[3].rm_so + 2];
 				}
 
 				if (find_register_fw_filename_psp_dir(