soc/intel/cmd/blk/cse: Store fw versions in CMOS memory for cold boot

This patch addresses the increased boot time issue that occurs when ISH
store is enabled, such as in the "rex4es_ec_ish" variant.

During a cold reboot, the CBMEM memory resets and loses the stored
firmware versions. This causes the firmware versions to be fetched again
from the CSE, which increases the boot time by about 200 ms. This patch
stores a backup of the firmware version in CMOS and updates the CBMEM
memory during a cold reboot.

BUG=b:280722061
Test=Verified the changes on rex board.

Signed-off-by: Dinesh Gehlot <digehlot@google.com>
Change-Id: Ibc5a027aa2bb7217e5032f56fece0846783557a5
Reviewed-on: https://review.coreboot.org/c/coreboot/+/75755
Reviewed-by: Subrata Banik <subratabanik@google.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Dinesh Gehlot 2023-06-10 11:53:47 +00:00 committed by Subrata Banik
parent 91da19c3bc
commit a9232d820e
1 changed files with 145 additions and 44 deletions

View File

@ -17,6 +17,8 @@
#include <soc/intel/common/reset.h> #include <soc/intel/common/reset.h>
#include <timestamp.h> #include <timestamp.h>
#include "cse_lite_cmos.h"
#define BPDT_HEADER_SZ sizeof(struct bpdt_header) #define BPDT_HEADER_SZ sizeof(struct bpdt_header)
#define BPDT_ENTRY_SZ sizeof(struct bpdt_entry) #define BPDT_ENTRY_SZ sizeof(struct bpdt_entry)
#define SUBPART_HEADER_SZ sizeof(struct subpart_hdr) #define SUBPART_HEADER_SZ sizeof(struct subpart_hdr)
@ -133,6 +135,17 @@ struct get_bp_info_rsp {
struct cse_bp_info bp_info; struct cse_bp_info bp_info;
} __packed; } __packed;
enum cse_fw_state {
/* The CMOS and CBMEM have the current fw version. */
CSE_FW_WARM_BOOT,
/* The CMOS has the current fw version, and the CBMEM is wiped out. */
CSE_FW_COLD_BOOT,
/* The CMOS and CBMEM are not initialized or not same as running firmware version.*/
CSE_FW_INVALID,
};
static const char * const cse_regions[] = {"RO", "RW"}; static const char * const cse_regions[] = {"RO", "RW"};
static struct cse_specific_info cse_info; static struct cse_specific_info cse_info;
@ -201,7 +214,7 @@ static const struct cse_bp_entry *cse_get_bp_entry(enum boot_partition_id bp,
return &cse_bp_info->bp_entries[bp]; return &cse_bp_info->bp_entries[bp];
} }
static bool is_cbmem_cse_info_valid(const struct cse_specific_info *info) static bool is_cse_fpt_info_valid(const struct cse_specific_info *info)
{ {
uint32_t crc = ~CRC(info, offsetof(struct cse_specific_info, crc), crc32_byte); uint32_t crc = ~CRC(info, offsetof(struct cse_specific_info, crc), crc32_byte);
@ -222,11 +235,38 @@ static bool is_cbmem_cse_info_valid(const struct cse_specific_info *info)
return true; return true;
} }
static void cbmem_store_cse_info_crc(struct cse_specific_info *info) static void store_cse_info_crc(struct cse_specific_info *info)
{ {
info->crc = ~CRC(info, offsetof(struct cse_specific_info, crc), crc32_byte); info->crc = ~CRC(info, offsetof(struct cse_specific_info, crc), crc32_byte);
} }
static enum cse_fw_state get_cse_state(const struct fw_version *cur_cse_fw_ver,
struct fw_version *cmos_cse_fw_ver, const struct fw_version *cbmem_cse_fw_ver)
{
enum cse_fw_state state = CSE_FW_WARM_BOOT;
size_t size = sizeof(struct fw_version);
/*
* Compare if stored CSE version (from the previous boot) is same as current
* running CSE version.
*/
if (memcmp(cmos_cse_fw_ver, cur_cse_fw_ver, size)) {
/*
* CMOS CSE versioin is invalid, possibly two scenarios
* 1. CSE FW update
* 2. First boot
*/
state = CSE_FW_INVALID;
} else {
/*
* Check if current running CSE version is same as previous stored CSE
* version aka CBMEM region is still valid.
*/
if (memcmp(cbmem_cse_fw_ver, cur_cse_fw_ver, size))
state = CSE_FW_COLD_BOOT;
}
return state;
}
/* /*
* Helper function that stores current CSE firmware version to CBMEM memory, * Helper function that stores current CSE firmware version to CBMEM memory,
* except during recovery mode. * except during recovery mode.
@ -237,26 +277,44 @@ static void cse_store_rw_fw_version(const struct cse_bp_entry *cse_bp)
return; return;
if (CONFIG(SOC_INTEL_CSE_LITE_SYNC_IN_ROMSTAGE)) { if (CONFIG(SOC_INTEL_CSE_LITE_SYNC_IN_ROMSTAGE)) {
memcpy(&(cse_info.cse_fwp_version.cur_cse_fw_version), &(cse_bp->fw_ver), /* update current CSE version and return */
sizeof(struct fw_version)); memcpy(&(cse_info.cse_fwp_version.cur_cse_fw_version),
&(cse_bp->fw_ver), sizeof(struct fw_version));
return; return;
} }
struct cse_specific_info *info = cbmem_add(CBMEM_ID_CSE_INFO, sizeof(*info)); struct cse_specific_info *cse_info_in_cbmem = cbmem_add(CBMEM_ID_CSE_INFO,
if (!info) sizeof(*cse_info_in_cbmem));
if (!cse_info_in_cbmem)
return; return;
/* Avoid CBMEM update if CBMEM already has persistent data */ /* Avoid CBMEM update if CBMEM already has persistent data */
if (is_cbmem_cse_info_valid(info)) if (is_cse_fpt_info_valid(cse_info_in_cbmem))
return; return;
memset(info, 0, sizeof(*info)); struct cse_specific_info cse_info_in_cmos;
cmos_read_fw_partition_info(&cse_info_in_cmos);
memcpy(&(info->cse_fwp_version.cur_cse_fw_version), &(cse_bp->fw_ver), /* Get current cse firmware state */
sizeof(struct fw_version)); enum cse_fw_state fw_state = get_cse_state(&(cse_bp->fw_ver),
&(cse_info_in_cmos.cse_fwp_version.cur_cse_fw_version),
&(cse_info_in_cbmem->cse_fwp_version.cur_cse_fw_version));
/* Reset CBMEM data and update current CSE version */
memset(cse_info_in_cbmem, 0, sizeof(*cse_info_in_cbmem));
memcpy(&(cse_info_in_cbmem->cse_fwp_version.cur_cse_fw_version),
&(cse_bp->fw_ver), sizeof(struct fw_version));
/* Update the CRC */ /* Update the CRC */
cbmem_store_cse_info_crc(info); store_cse_info_crc(cse_info_in_cbmem);
if (fw_state == CSE_FW_INVALID) {
/*
* Current CMOS data is outdated, which could be due to CSE update or
* rollback, hence, need to update CMOS with current CSE FPT versions.
*/
cmos_write_fw_partition_info(cse_info_in_cbmem);
}
} }
#if CONFIG(SOC_INTEL_CSE_LITE_SYNC_IN_ROMSTAGE) #if CONFIG(SOC_INTEL_CSE_LITE_SYNC_IN_ROMSTAGE)
@ -266,18 +324,35 @@ static void preram_cse_info_sync_to_cbmem(int is_recovery)
if (vboot_recovery_mode_enabled() || !CONFIG(SOC_INTEL_STORE_CSE_FW_VERSION)) if (vboot_recovery_mode_enabled() || !CONFIG(SOC_INTEL_STORE_CSE_FW_VERSION))
return; return;
struct cse_specific_info *info = cbmem_add(CBMEM_ID_CSE_INFO, sizeof(*info)); struct cse_specific_info *cse_info_in_cbmem = cbmem_add(CBMEM_ID_CSE_INFO,
if (!info) sizeof(*cse_info_in_cbmem));
if (!cse_info_in_cbmem)
return; return;
/* Avoid sync into CBMEM if CBMEM already has persistent data */ /* Warm Reboot: Avoid sync into CBMEM if CBMEM already has persistent data */
if (is_cbmem_cse_info_valid(info)) if (is_cse_fpt_info_valid(cse_info_in_cbmem))
return; return;
memcpy(info, &cse_info, sizeof(struct cse_specific_info)); /* Update CBMEM with PRERAM CSE specific info and update the CRC */
memcpy(cse_info_in_cbmem, &cse_info, sizeof(struct cse_specific_info));
store_cse_info_crc(cse_info_in_cbmem);
/* Update the CRC */ struct cse_specific_info cse_info_in_cmos;
cbmem_store_cse_info_crc(info); cmos_read_fw_partition_info(&cse_info_in_cmos);
if (!memcmp(&(cse_info_in_cmos.cse_fwp_version.cur_cse_fw_version),
&(cse_info_in_cbmem->cse_fwp_version.cur_cse_fw_version),
sizeof(struct fw_version))) {
/* Cold Reboot: Avoid sync into CMOS if CMOS already has persistent data */
if (is_cse_fpt_info_valid(&cse_info_in_cmos))
return;
}
/*
* Current CMOS data is outdated, which could be due to CSE update or
* rollback, hence, need to update CMOS with current CSE FPT versions.
*/
cmos_write_fw_partition_info(cse_info_in_cbmem);
} }
CBMEM_CREATION_HOOK(preram_cse_info_sync_to_cbmem); CBMEM_CREATION_HOOK(preram_cse_info_sync_to_cbmem);
@ -1230,7 +1305,7 @@ static void do_cse_fw_sync(void)
cse_trigger_vboot_recovery(CSE_LITE_SKU_DATA_WIPE_ERROR); cse_trigger_vboot_recovery(CSE_LITE_SKU_DATA_WIPE_ERROR);
/* /*
* cse firmware update is skipped if SOC_INTEL_CSE_RW_UPDATE is not defined and * CSE firmware update is skipped if SOC_INTEL_CSE_RW_UPDATE is not defined and
* runtime debug control flag is not enabled. The driver triggers recovery if CSE CBFS * runtime debug control flag is not enabled. The driver triggers recovery if CSE CBFS
* RW metadata or CSE CBFS RW blob is not available. * RW metadata or CSE CBFS RW blob is not available.
*/ */
@ -1318,6 +1393,14 @@ static enum cb_err cse_get_fpt_partition_info(enum fpt_partition_id id,
return send_get_fpt_partition_info_cmd(id, resp); return send_get_fpt_partition_info_cmd(id, resp);
} }
static bool is_ish_version_valid(struct cse_fw_ish_version_info *version)
{
const struct fw_version invalid_fw = {0, 0, 0, 0};
if (!memcmp(&version->cur_ish_fw_version, &invalid_fw, sizeof(struct fw_version)))
return false;
return true;
}
/* /*
* Helper function to read ISH version from CSE FPT using HECI command. * Helper function to read ISH version from CSE FPT using HECI command.
* *
@ -1333,37 +1416,55 @@ static void store_ish_version(void)
if (vboot_recovery_mode_enabled()) if (vboot_recovery_mode_enabled())
return; return;
struct cse_specific_info *info = cbmem_find(CBMEM_ID_CSE_INFO); struct cse_specific_info *cse_info_in_cbmem = cbmem_find(CBMEM_ID_CSE_INFO);
if (info == NULL) if (cse_info_in_cbmem == NULL)
return; return;
struct cse_fw_partition_info *version = &(info->cse_fwp_version); struct cse_specific_info cse_info_in_cmos;
size_t size = sizeof(struct fw_version); cmos_read_fw_partition_info(&cse_info_in_cmos);
/* struct cse_fw_partition_info *cbmem_version = &(cse_info_in_cbmem->cse_fwp_version);
* Compare if stored cse version (from the previous boot) is same as current struct cse_fw_partition_info *cmos_version = &(cse_info_in_cmos.cse_fwp_version);
* running cse version.
*/
if (memcmp(&version->ish_partition_info.prev_cse_fw_version,
&version->cur_cse_fw_version, sizeof(struct fw_version))) {
/*
* Current running CSE version is different than previous stored CSE version
* which could be due to CSE update or rollback, hence, need to send ISHC
* partition info cmd to know the currently running ISH version.
*/
struct fw_version_resp resp; /* Get current cse firmware state */
if (cse_get_fpt_partition_info(FPT_PARTITION_NAME_ISHC, &resp) == CB_SUCCESS) { enum cse_fw_state fw_state = get_cse_state(
/* Update stored cse version with current version */ &(cbmem_version->cur_cse_fw_version),
memcpy(&(version->ish_partition_info.prev_cse_fw_version), &(cmos_version->ish_partition_info.prev_cse_fw_version),
&(version->cur_cse_fw_version), size); &(cbmem_version->ish_partition_info.prev_cse_fw_version));
/* Since cse version has been updated, ish version needs to be updated. */ if (fw_state == CSE_FW_WARM_BOOT) {
memcpy(&(version->ish_partition_info.cur_ish_fw_version), return;
&(resp.manifest_data.version), size); } else {
if (fw_state == CSE_FW_COLD_BOOT &&
is_ish_version_valid(&(cmos_version->ish_partition_info))) {
/* CMOS data is persistent across cold boots */
memcpy(&(cse_info_in_cbmem->cse_fwp_version.ish_partition_info),
&(cse_info_in_cmos.cse_fwp_version.ish_partition_info),
sizeof(struct cse_fw_ish_version_info));
store_cse_info_crc(cse_info_in_cbmem);
} else {
/*
* Current running CSE version is different than previous stored CSE version
* which could be due to CSE update or rollback, hence, need to send ISHC
* partition info cmd to know the currently running ISH version.
*/
struct fw_version_resp resp;
if (cse_get_fpt_partition_info(FPT_PARTITION_NAME_ISHC,
&resp) == CB_SUCCESS) {
/* Update stored CSE version with current cse version */
memcpy(&(cbmem_version->ish_partition_info.prev_cse_fw_version),
&(cbmem_version->cur_cse_fw_version), sizeof(struct fw_version));
/* Update the CRC */ /* Retrieve and update current ish version */
cbmem_store_cse_info_crc(info); memcpy(&(cbmem_version->ish_partition_info.cur_ish_fw_version),
&(resp.manifest_data.version), sizeof(struct fw_version));
/* Update the CRC */
store_cse_info_crc(cse_info_in_cbmem);
/* Update CMOS with current CSE FPT versions.*/
cmos_write_fw_partition_info(cse_info_in_cbmem);
}
} }
} }
} }