soc/intel/cmn/cse: Refactor CSE RW FW Version implementation

This patch introduces a CSE firmware specific data in order
to store Intel CSE and associated firmware related information which
requires a sync between Pre-RAM and Post-RAM phase.

This information will be used further to retrieve currently running
CSE RW firmware instead of fetching the version information by sending
a HECI cmd (which consumes 7ms-15ms depending upon the CSE operational
state).

Current implementation attempts to simply the CSE RW FW version store
and retrieval operations as below

* CSE sync in romstage (aka Pre-RAM) - Relying on .bss segment to store
  the CSE info data in absence of real physical memory and sync back into
  the CBMEM once available (after FSP-M exits).

* CSE sync in ramstage (aka Post-RAM) - Directly stored the CSE RW
  version into the CBMEM (as CBMEM is online).

BUG=b:285405031
TEST=Able to build and boot google/rex. Verified CSE RW FW version
(for LITE SKU) is getting displayed without impacting the boot time.

w/o this patch:
  10:start of ramstage                         722,257 (43)
  17:starting LZ4 decompress (ignore for x86)  723,777 (1,520)

w/ this patch:
  10:start of ramstage                         722,257 (43)
  17:starting LZ4 decompress (ignore for x86)  723,777 (1,520)

Change-Id: Ia873af512851a682cf1fac0e128d842562a316ab
Signed-off-by: Subrata Banik <subratabanik@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/77174
Reviewed-by: Julius Werner <jwerner@chromium.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Rizwan Qureshi <rizwan.qureshi@intel.com>
diff --git a/src/commonlib/bsd/include/commonlib/bsd/cbmem_id.h b/src/commonlib/bsd/include/commonlib/bsd/cbmem_id.h
index 8e6e6cd..342d8a3 100644
--- a/src/commonlib/bsd/include/commonlib/bsd/cbmem_id.h
+++ b/src/commonlib/bsd/include/commonlib/bsd/cbmem_id.h
@@ -88,6 +88,7 @@
 #define CBMEM_ID_AMD_STB	0x5f425453
 #define CBMEM_ID_AMD_MP2	0x5f32504d
 #define CBMEM_ID_CSE_PARTITION_VERSION 0x43535056
+#define CBMEM_ID_CSE_INFO	0x4553435F
 
 #define CBMEM_ID_TO_NAME_TABLE				 \
 	{ CBMEM_ID_ACPI,		"ACPI       " }, \
@@ -168,5 +169,6 @@
 	{ CBMEM_ID_MEM_CHIP_INFO,       "MEM CHIP INFO"},\
 	{ CBMEM_ID_AMD_STB,		"AMD STB"},\
 	{ CBMEM_ID_AMD_MP2,		"AMD MP2 BUFFER"},\
-	{ CBMEM_ID_CSE_PARTITION_VERSION, "CSE PARTITION VERSION"}
+	{ CBMEM_ID_CSE_PARTITION_VERSION, "CSE PARTITION VERSION"},\
+	{ CBMEM_ID_CSE_INFO,		"CSE SPECIFIC INFORMATION"}
 #endif /* _CBMEM_ID_H_ */
diff --git a/src/soc/intel/common/block/cse/cse.c b/src/soc/intel/common/block/cse/cse.c
index 939c793..af1ca92 100644
--- a/src/soc/intel/common/block/cse/cse.c
+++ b/src/soc/intel/common/block/cse/cse.c
@@ -1420,15 +1420,15 @@
 
 static void intel_cse_get_rw_version(void)
 {
-	struct cse_fw_partition_info *version = cbmem_find(CBMEM_ID_CSE_PARTITION_VERSION);
-	if (version == NULL)
+	struct cse_specific_info *info = cbmem_find(CBMEM_ID_CSE_INFO);
+	if (info == NULL)
 		return;
 
 	printk(BIOS_DEBUG, "CSE RW Firmware Version: %d.%d.%d.%d\n",
-		version->cur_cse_fw_version.major,
-		version->cur_cse_fw_version.minor,
-		version->cur_cse_fw_version.hotfix,
-		version->cur_cse_fw_version.build);
+		info->cse_fwp_version.cur_cse_fw_version.major,
+		info->cse_fwp_version.cur_cse_fw_version.minor,
+		info->cse_fwp_version.cur_cse_fw_version.hotfix,
+		info->cse_fwp_version.cur_cse_fw_version.build);
 }
 
 /*
diff --git a/src/soc/intel/common/block/cse/cse_lite.c b/src/soc/intel/common/block/cse/cse_lite.c
index 6203baa..908b5de 100644
--- a/src/soc/intel/common/block/cse/cse_lite.c
+++ b/src/soc/intel/common/block/cse/cse_lite.c
@@ -6,6 +6,7 @@
 #include <commonlib/region.h>
 #include <console/console.h>
 #include <cpu/cpu.h>
+#include <crc_byte.h>
 #include <fmap.h>
 #include <intelbasecode/debug_feature.h>
 #include <intelblocks/cse.h>
@@ -134,6 +135,8 @@
 
 static const char * const cse_regions[] = {"RO", "RW"};
 
+static struct cse_specific_info cse_info;
+
 void cse_log_ro_write_protection_info(bool mfg_mode)
 {
 	bool cse_ro_wp_en = is_spi_wp_cse_ro_en();
@@ -198,6 +201,88 @@
 	return &cse_bp_info->bp_entries[bp];
 }
 
+static bool is_cbmem_cse_info_valid(const struct cse_specific_info *info)
+{
+	uint32_t crc = ~CRC(info, offsetof(struct cse_specific_info, crc), crc32_byte);
+
+	/*
+	 * Authenticate the CBMEM persistent data.
+	 *
+	 * The underlying assumption is that an event (i.e., CSE upgrade/downgrade) which
+	 * could change the values stored in this region has to also trigger the global
+	 * reset. Hence, CBMEM persistent data won't be available any time after such
+	 * event (global reset or cold reset) being initiated.
+	 *
+	 * During warm boot scenarios CBMEM contents remain persistent hence, we don't
+	 * want to override the existing data in CBMEM to avoid any additional boot latency.
+	 */
+	if (info->crc != crc)
+		return false;
+
+	return true;
+}
+
+static void cbmem_store_cse_info_crc(struct cse_specific_info *info)
+{
+	info->crc = ~CRC(info, offsetof(struct cse_specific_info, crc), crc32_byte);
+}
+
+/*
+ * Helper function that stores current CSE firmware version to CBMEM memory,
+ * except during recovery mode.
+ */
+static void cse_store_rw_fw_version(const struct cse_bp_entry *cse_bp)
+{
+	if (vboot_recovery_mode_enabled())
+		return;
+
+	if (CONFIG(SOC_INTEL_CSE_LITE_SYNC_IN_ROMSTAGE)) {
+		memcpy(&(cse_info.cse_fwp_version.cur_cse_fw_version), &(cse_bp->fw_ver),
+			 sizeof(struct fw_version));
+		return;
+	}
+
+	struct cse_specific_info *info = cbmem_add(CBMEM_ID_CSE_INFO, sizeof(*info));
+	if (!info)
+		return;
+
+	/* Avoid CBMEM update if CBMEM already has persistent data */
+	if (is_cbmem_cse_info_valid(info))
+		return;
+
+	memset(info, 0, sizeof(*info));
+
+	memcpy(&(info->cse_fwp_version.cur_cse_fw_version), &(cse_bp->fw_ver),
+		 sizeof(struct fw_version));
+
+	/* Update the CRC */
+	cbmem_store_cse_info_crc(info);
+}
+
+#if CONFIG(SOC_INTEL_CSE_LITE_SYNC_IN_ROMSTAGE)
+/* Function to copy PRERAM CSE specific info to pertinent CBMEM. */
+static void preram_cse_info_sync_to_cbmem(int is_recovery)
+{
+	if (vboot_recovery_mode_enabled() || !CONFIG(SOC_INTEL_STORE_CSE_FW_VERSION))
+		return;
+
+	struct cse_specific_info *info = cbmem_add(CBMEM_ID_CSE_INFO, sizeof(*info));
+	if (!info)
+		return;
+
+	/* Avoid sync into CBMEM if CBMEM already has persistent data */
+	if (is_cbmem_cse_info_valid(info))
+		return;
+
+	memcpy(info, &cse_info, sizeof(struct cse_specific_info));
+
+	/* Update the CRC */
+	cbmem_store_cse_info_crc(info);
+}
+
+CBMEM_CREATION_HOOK(preram_cse_info_sync_to_cbmem);
+#endif
+
 static void cse_print_boot_partition_info(const struct cse_bp_info *cse_bp_info)
 {
 	const struct cse_bp_entry *cse_bp;
@@ -223,6 +308,10 @@
 			cse_bp->fw_ver.hotfix, cse_bp->fw_ver.build,
 			cse_bp->status, cse_bp->start_offset,
 			cse_bp->end_offset);
+
+	/* Store the CSE RW Firmware Version into CBMEM */
+	if (CONFIG(SOC_INTEL_STORE_CSE_FW_VERSION))
+		cse_store_rw_fw_version(cse_bp);
 }
 
 /*
@@ -1171,26 +1260,6 @@
 	timestamp_add_now(TS_CSE_FW_SYNC_END);
 }
 
-/*
- * Helper function that stores current CSE firmware version to CBMEM memory,
- * except during recovery mode.
- */
-static void cse_store_rw_fw_version(void)
-{
-	if (vboot_recovery_mode_enabled())
-		return;
-
-	struct get_bp_info_rsp cse_bp_info;
-	if (cse_get_bp_info(&cse_bp_info) != CB_SUCCESS) {
-		printk(BIOS_ERR, "cse_lite: Failed to get CSE boot partition info\n");
-		return;
-	}
-	const struct cse_bp_entry *cse_bp = cse_get_bp_entry(RW, &cse_bp_info.bp_info);
-	struct cse_fw_partition_info *version;
-	version = cbmem_add(CBMEM_ID_CSE_PARTITION_VERSION, sizeof(*version));
-	memcpy(&(version->cur_cse_fw_version), &(cse_bp->fw_ver), sizeof(struct fw_version));
-}
-
 static enum cb_err send_get_fpt_partition_info_cmd(enum fpt_partition_id id,
 	struct fw_version_resp *resp)
 {
@@ -1303,9 +1372,6 @@
 	if (CONFIG(SOC_INTEL_CSE_LITE_SYNC_IN_RAMSTAGE))
 		cse_fw_sync();
 
-	/* Store the CSE RW Firmware Version into CBMEM */
-	if (CONFIG(SOC_INTEL_STORE_CSE_FW_VERSION))
-		cse_store_rw_fw_version();
 	/*
 	 * Store the ISH RW Firmware Version into CBMEM if ISH partition
 	 * is available
diff --git a/src/soc/intel/common/block/include/intelblocks/cse.h b/src/soc/intel/common/block/include/intelblocks/cse.h
index 636b1f4..ae9fe94 100644
--- a/src/soc/intel/common/block/include/intelblocks/cse.h
+++ b/src/soc/intel/common/block/include/intelblocks/cse.h
@@ -160,6 +160,13 @@
 	struct cse_fw_ish_version_info ish_partition_info;
 };
 
+/* CSE Specific Information */
+struct cse_specific_info {
+	struct cse_fw_partition_info cse_fwp_version;
+	bool cse_downgrade_requested;
+	uint32_t crc;
+};
+
 /* CSE RX and TX error status */
 enum cse_tx_rx_status {
 	/*