soc/intel/cse: Add function to get cse_bp_info early

PSR data is created and stored in CSE data partition. In platforms that
employ CSE Lite SKU firmware, a firmware downgrade involves clearing of
CSE data partition which results in PSR data being lost. The PSR data
needs to be preserved across the firmware downgrade flow. CSE Lite SKU
firmware supports command to backup PSR data, and this command can be
sent only in post-RAM stages. So the cse_fw_sync actions needs to be
moved to ramstage.

Sending cse_get_bp_info command in ramstage takes additional boot time
of ~45-55ms on rex. To avoid the boot time penalty, this patch provides
an API to get the cse_bp_info in early romstage. The response data is
then migrated to cbmem once memory is initialized. The same data in
cbmem can be utilized in ramstage to perform other cse_fw_sync actions.

This patch also adds check to validate cse_bp_info in cbmem and avoids
sending the command again if the data is valid.

BUG=b:273207144
TEST=Verify the command works in early romstage, data is migrated to
cbmem and valid data is available in ramstage on rex.

Change-Id: Ib1e72c950ba0f4911924805f501ec1bd54b6ba3c
Signed-off-by: Krishna Prasad Bhat <krishna.p.bhat.d@intel.com>
Signed-off-by: Rizwan Qureshi <rizwan.qureshi@intel.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/78053
Reviewed-by: Subrata Banik <subratabanik@google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/soc/intel/common/block/cse/cse_lite.c b/src/soc/intel/common/block/cse/cse_lite.c
index 140a56d..9ea6c78 100644
--- a/src/soc/intel/common/block/cse/cse_lite.c
+++ b/src/soc/intel/common/block/cse/cse_lite.c
@@ -12,6 +12,7 @@
 #include <intelblocks/cse.h>
 #include <intelblocks/cse_layout.h>
 #include <intelblocks/spi.h>
+#include <intelblocks/systemagent.h>
 #include <security/vboot/misc.h>
 #include <security/vboot/vboot_common.h>
 #include <soc/intel/common/reset.h>
@@ -420,6 +421,38 @@
 	return false;
 }
 
+static struct get_bp_info_rsp *sync_cse_bp_info_to_cbmem(void)
+{
+	struct get_bp_info_rsp *cse_bp_info_in_cbmem = cbmem_find(CBMEM_ID_CSE_BP_INFO);
+
+	if (cse_bp_info_in_cbmem != NULL)
+		return cse_bp_info_in_cbmem;
+
+	cse_bp_info_in_cbmem = cbmem_add(CBMEM_ID_CSE_BP_INFO,
+		sizeof(struct get_bp_info_rsp));
+
+	if (!cse_bp_info_in_cbmem) {
+		printk(BIOS_ERR, "Unable to store Boot Parition Info in cbmem\n");
+		return NULL;
+	}
+
+	/* Copy the CSE Boot Partition Info command response to cbmem */
+	memcpy(cse_bp_info_in_cbmem, &cse_bp_info_rsp, sizeof(struct get_bp_info_rsp));
+
+	return cse_bp_info_in_cbmem;
+}
+
+static bool is_cse_bp_info_valid(struct get_bp_info_rsp *bp_info_rsp)
+{
+	/*
+	 * In case the cse_bp_info_rsp header group ID, command is incorrect or is_resp is 0,
+	 * then return false to indicate cse_bp_info is not valid.
+	 */
+	return (bp_info_rsp->hdr.group_id != MKHI_GROUP_ID_BUP_COMMON ||
+		bp_info_rsp->hdr.command != MKHI_BUP_COMMON_GET_BOOT_PARTITION_INFO ||
+		!bp_info_rsp->hdr.is_resp) ? false : true;
+}
+
 static enum cb_err cse_get_bp_info(void)
 {
 	struct get_bp_info_req {
@@ -433,6 +466,33 @@
 		.reserved = {0},
 	};
 
+	/*
+	 * If SOC_INTEL_CSE_LITE_SYNC_IN_RAMSTAGE config is selected and memory has been
+	 * initialized, check if there is cse bp info response stored in cbmem. Once the data
+	 * is validated, copy it to the global cse_bp_info_rsp so that it can be used by other
+	 * functions. In case, data is not available in cbmem or invalid, continue to send the
+	 * GET_BOOT_PARTITION_INFO command, else return.
+	 */
+	if (CONFIG(SOC_INTEL_CSE_LITE_SYNC_IN_RAMSTAGE) && cbmem_online()) {
+		struct get_bp_info_rsp *cse_bp_info_in_cbmem = sync_cse_bp_info_to_cbmem();
+		if (cse_bp_info_in_cbmem) {
+			if (is_cse_bp_info_valid(cse_bp_info_in_cbmem)) {
+				memcpy(&cse_bp_info_rsp, cse_bp_info_in_cbmem,
+					sizeof(struct get_bp_info_rsp));
+				return CB_SUCCESS;
+			}
+		}
+	} else {
+		/*
+		 * If SOC_INTEL_CSE_LITE_SYNC_IN_ROMSTAGE config is selected, check if the
+		 * global cse bp info response stored in global cse_bp_info_rsp is valid.
+		 * In case, it is not valid, continue to send the GET_BOOT_PARTITION_INFO
+		 * command, else return.
+		 */
+		if (is_cse_bp_info_valid(&cse_bp_info_rsp))
+			return CB_SUCCESS;
+	}
+
 	if (!cse_is_bp_cmd_info_possible()) {
 		printk(BIOS_ERR, "cse_lite: CSE does not meet prerequisites\n");
 		return CB_ERR;
@@ -453,9 +513,29 @@
 	}
 
 	cse_print_boot_partition_info();
-
 	return CB_SUCCESS;
 }
+
+void cse_fill_bp_info(void)
+{
+	if (vboot_recovery_mode_enabled())
+		return;
+
+	if (cse_get_bp_info() != CB_SUCCESS)
+		cse_trigger_vboot_recovery(CSE_COMMUNICATION_ERROR);
+}
+
+/* Function to copy PRERAM CSE BP info to pertinent CBMEM. */
+static void preram_cse_bp_info_sync_to_cbmem(int is_recovery)
+{
+	if (vboot_recovery_mode_enabled())
+		return;
+
+	sync_cse_bp_info_to_cbmem();
+}
+
+CBMEM_CREATION_HOOK(preram_cse_bp_info_sync_to_cbmem);
+
 /*
  * It sends HECI command to notify CSE about its next boot partition. When coreboot wants
  * CSE to boot from certain partition (BP1 <RO> or BP2 <RW>), then this command can be used.
diff --git a/src/soc/intel/common/block/include/intelblocks/cse.h b/src/soc/intel/common/block/include/intelblocks/cse.h
index ae9fe94..0f73568 100644
--- a/src/soc/intel/common/block/include/intelblocks/cse.h
+++ b/src/soc/intel/common/block/include/intelblocks/cse.h
@@ -572,4 +572,7 @@
  */
 enum cb_err cse_get_fw_feature_state(uint32_t *feature_state);
 
+/* Fills the CSE Boot Partition Info response */
+void cse_fill_bp_info(void);
+
 #endif // SOC_INTEL_COMMON_CSE_H