util/amdfwtool: Support not passing recovery/backup APCB

If Recovery/Backup APCB is not passed, then AMD_BIOS_APCB_BK entry is
not populated. But PSP expects that bios directory entry to be
populated. Also on mainboards where both APCB and recovery APCB are same
(eg. Skyrim), 2 copies of the same APCB are added to amdfw*.rom. Update
amdfwtool to support not passing recovery/backup APCB. If the recovery
APCB is not passed, then populate AMD_BIOS_APCB_BK entry and make it
point to the same offset as AMD_BIOS_APCB entry.

BUG=b:240696002
TEST=Build and boot to OS in Skyrim. Ensure that the device can enter
recovery mode. Perform multiple suspend/resume cycles.

Change-Id: I031ba817573cd35160f5e219b1b373ddce69aa6b
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/73661
Reviewed-by: Fred Reitberger <reitbergerfred@gmail.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/util/amdfwtool/amdfwtool.c b/util/amdfwtool/amdfwtool.c
index 72a06cb..fc0668f 100644
--- a/util/amdfwtool/amdfwtool.c
+++ b/util/amdfwtool/amdfwtool.c
@@ -1586,6 +1586,35 @@
 	return -1;
 }
 
+static void add_bios_apcb_bk_entry(bios_directory_table *biosdir, unsigned int idx,
+						int inst, uint32_t size, uint64_t source)
+{
+	int i;
+
+	for (i = 0; amd_bios_table[i].type != AMD_BIOS_INVALID; i++) {
+		if (amd_bios_table[i].type == AMD_BIOS_APCB_BK &&
+					amd_bios_table[i].inst == inst)
+			break;
+	}
+
+	if (amd_bios_table[i].type != AMD_BIOS_APCB_BK)
+		return;
+
+	biosdir->entries[idx].type = amd_bios_table[i].type;
+	biosdir->entries[idx].region_type = amd_bios_table[i].region_type;
+	biosdir->entries[idx].dest = amd_bios_table[i].dest ?
+					amd_bios_table[i].dest : (uint64_t)-1;
+	biosdir->entries[idx].reset = amd_bios_table[i].reset;
+	biosdir->entries[idx].copy = amd_bios_table[i].copy;
+	biosdir->entries[idx].ro = amd_bios_table[i].ro;
+	biosdir->entries[idx].compressed = amd_bios_table[i].zlib;
+	biosdir->entries[idx].inst = amd_bios_table[i].inst;
+	biosdir->entries[idx].subprog = amd_bios_table[i].subpr;
+	biosdir->entries[idx].size = size;
+	biosdir->entries[idx].source = source;
+	biosdir->entries[idx].address_mode = SET_ADDR_MODE_BY_TABLE(biosdir);
+}
+
 static void integrate_bios_firmwares(context *ctx,
 					bios_directory_table *biosdir,
 					bios_directory_table *biosdir2,
@@ -1782,6 +1811,12 @@
 			biosdir->entries[count].address_mode = SET_ADDR_MODE_BY_TABLE(biosdir);
 
 			adjust_current_pointer(ctx, bytes, 0x100U);
+			if (fw_table[i].type == AMD_BIOS_APCB && !cb_config->have_apcb_bk) {
+				size = biosdir->entries[count].size;
+				source = biosdir->entries[count].source;
+				count++;
+				add_bios_apcb_bk_entry(biosdir, count, fw_table[i].inst, size, source);
+			}
 			break;
 		}
 
@@ -2290,11 +2325,13 @@
 			sub = 0;
 			break;
 		case AMDFW_OPT_APCB:
-			if ((instance & 0xF0) == 0)
+			if ((instance & 0xF0) == 0) {
 				register_bdt_data(AMD_BIOS_APCB, sub, instance & 0xF, optarg);
-			else
+			} else {
 				register_bdt_data(AMD_BIOS_APCB_BK, sub,
 							instance & 0xF, optarg);
+				cb_config.have_apcb_bk = 1;
+			}
 			sub = instance = 0;
 			break;
 		case AMDFW_OPT_APOBBASE:
diff --git a/util/amdfwtool/amdfwtool.h b/util/amdfwtool/amdfwtool.h
index 2dc25a2..23dc0ac 100644
--- a/util/amdfwtool/amdfwtool.h
+++ b/util/amdfwtool/amdfwtool.h
@@ -405,6 +405,7 @@
 	bool recovery_ab_single_copy;
 	bool need_ish;
 	bool use_combo;
+	bool have_apcb_bk;
 	enum platform soc_id;
 } amd_cb_config;