Dinesh Gehlot has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/75686?usp=email )
Change subject: soc/intel/cmd/blk/cse: Cache CSE version in CMOS memory for cold boots ......................................................................
soc/intel/cmd/blk/cse: Cache CSE version in CMOS memory for cold boots
This patch stores the current CSE version in CMOS memory and provide API's to access the version.
BUG=b:280722061
Signed-off-by: Dinesh Gehlot digehlot@google.com Change-Id: I9c3ff2aa7cbc02750ff0256a16c02578f507db06 --- M src/soc/intel/common/block/cse/Kconfig M src/soc/intel/common/block/cse/Makefile.inc A src/soc/intel/common/block/cse/cse_cmos.c M src/soc/intel/common/block/cse/cse_lite.c A src/soc/intel/common/block/include/intelblocks/cse_cmos.h 5 files changed, 202 insertions(+), 12 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/86/75686/1
diff --git a/src/soc/intel/common/block/cse/Kconfig b/src/soc/intel/common/block/cse/Kconfig index 26c623f..4426d5a 100644 --- a/src/soc/intel/common/block/cse/Kconfig +++ b/src/soc/intel/common/block/cse/Kconfig @@ -45,26 +45,34 @@ Use this config for SoC platform prior to CNL PCH (with postboot_sai implemented) to make `HECI1` device disable using private configuration register (PCR) write.
+config SOC_INTEL_PARTITION_FW_CMOS_OFFSET + int + default 72 + depends on SOC_INTEL_STORE_CSE_FPT_PARTITION_VERSION + help + This configuration option stores the starting offset of firmware partition versions in + CMOS memory. The offset should be byte aligned and must leave enough memory to store + required firmware partition versions. + config SOC_INTEL_STORE_CSE_FPT_PARTITION_VERSION bool default n depends on DRIVERS_INTEL_ISH help - This configuration option stores CSE FPT partitions' version in CBMEM memory. + This configuration option stores CSE FPT partitions' version in CMOS and CBMEM memory. This information can be used to identify the currently running firmware partition version.
- The cost of sending HECI command to read the CSE FPT is significant (~200ms) - hence, the idea is to read the CSE RW version on every cold reset (to cover - the CSE update scenarios) and store into CBMEM to avoid the cost of resending - the HECI command in all consecutive warm boots. + The cost of sending HECI command to read the CSE FPT is significant (~200ms) hence, + the idea is to read the CSE RW version on CSE firmware update and store into CMOS to + avoid the cost of resending the HECI command in subsequent reboots. The CBMEM memory + stored version is updated with CMOS memory stored version on each cold reboot.
- Later boot stages can just read the CBMEM ID to retrieve the ISH version if - required. + Later boot stages can just read the CBMEM ID to retrieve the ISH version if required.
- Additionally, ensure this feature is platform specific hence, only enabled - for the platform that would like to store the ISH version into the CBMEM and - parse to perform some additional work. + Additionally, ensure this feature is platform specific hence, only enabled for the + platform that would like to store the ISH version into the CMOS and CBMEM and parse to + perform some additional work.
config SOC_INTEL_CSE_SEND_EOP_EARLY bool "CSE send EOP early" diff --git a/src/soc/intel/common/block/cse/Makefile.inc b/src/soc/intel/common/block/cse/Makefile.inc index 3e11279..679c4f7 100644 --- a/src/soc/intel/common/block/cse/Makefile.inc +++ b/src/soc/intel/common/block/cse/Makefile.inc @@ -1,13 +1,14 @@ ## SPDX-License-Identifier: GPL-2.0-only romstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_CSE) += cse.c ramstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_CSE) += cse.c +ramstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_CSE) += disable_heci.c romstage-$(CONFIG_SOC_INTEL_CSE_LITE_SKU) += cse_lite.c ramstage-$(CONFIG_SOC_INTEL_CSE_LITE_SKU) += cse_lite.c ramstage-$(CONFIG_SOC_INTEL_CSE_HAVE_SPEC_SUPPORT) += cse_spec.c -ramstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_CSE) += disable_heci.c -smm-$(CONFIG_SOC_INTEL_COMMON_BLOCK_CSE) += disable_heci.c ramstage-$(CONFIG_SOC_INTEL_CSE_SET_EOP) += cse_eop.c romstage-$(CONFIG_SOC_INTEL_CSE_PRE_CPU_RESET_TELEMETRY) += telemetry.c +ramstage-$(CONFIG_SOC_INTEL_STORE_CSE_FPT_PARTITION_VERSION) += cse_cmos.c +smm-$(CONFIG_SOC_INTEL_COMMON_BLOCK_CSE) += disable_heci.c
ifeq ($(CONFIG_STITCH_ME_BIN),y)
diff --git a/src/soc/intel/common/block/cse/cse_cmos.c b/src/soc/intel/common/block/cse/cse_cmos.c new file mode 100644 index 0000000..0f8c96a --- /dev/null +++ b/src/soc/intel/common/block/cse/cse_cmos.c @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <console/console.h> +#include <intelblocks/cse_cmos.h> +#include <ip_checksum.h> +#include <pc80/mc146818rtc.h> + +#define PARTITION_FW_SIGNATURE 0x46575054 /* 'FWPT' */ + +/* + * We need a region in CMOS to store the firmware versions. + * + * This can either be declared as part of the option + * table or statically defined in the board config. + */ +#if CONFIG(USE_OPTION_TABLE) +# include "option_table.h" + +#ifndef CMOS_VSTART_partition_fw +#error "The `ramtop` CMOS entry is missing, please add it to your cmos.layout." +#endif + +#if CMOS_VSTART_partition_fw % 8 != 0 +#error "The `partition firmware` CMOS entry needs to be byte aligned, check your cmos.layout." +#endif // CMOS_VSTART_partition_fw % 8 != 0 + +#if CMOS_VLEN_partition_fw != (24 * 8) +#error "The partition firmware entry needs to be 24 bytes long, check your cmos.layout." +#endif + +# define PARTITION_FW_CMOS_OFFSET (CMOS_VSTART_partition_fw >> 3) + +#else +# if (CONFIG_SOC_INTEL_PARTITION_FW_CMOS_OFFSET != 0) +# define PARTITION_FW_CMOS_OFFSET CONFIG_SOC_INTEL_PARTITION_FW_CMOS_OFFSET +# else +# error "Must configure CONFIG_SOC_INTEL_PARTITION_FW_CMOS_OFFSET" +# endif +#endif + +struct cse_fw_table { + uint32_t signature; + struct fw_version cse_version; + uint16_t checksum; +} __packed; + +/* Helper function to read firmware version from cmos memory. */ +static uint32_t read_cmos_partition_version(struct cse_fw_table *version) +{ + u8 i, *p, offset = PARTITION_FW_CMOS_OFFSET; + u16 csum; + + for (p = (u8 *)version, i = 0; i < sizeof(*version); i++, p++) + *p = cmos_read(offset + i); + + /* Verify signature */ + if (version->signature != PARTITION_FW_SIGNATURE) { + printk(BIOS_DEBUG, "cse firmware partition invalid signature\n"); + return -1; + } + + /* Verify checksum over signature and firmware versions only */ + csum = compute_ip_checksum(version, offsetof(struct cse_fw_table, checksum)); + + if (csum != version->checksum) { + printk(BIOS_DEBUG, "cse firmware partition checksum mismatch\n"); + return -1; + } + + return 0; +} + +/* Helper function to write firmware version to cmos memory. */ +static void write_cmos_partition_version(struct cse_fw_table *version) +{ + u8 i, *p, offset = PARTITION_FW_CMOS_OFFSET; + + /* Checksum over signature and firmware versions only */ + version->checksum = compute_ip_checksum(version, offsetof(struct cse_fw_table, checksum)); + + for (p = (u8 *)version, i = 0; i < sizeof(*version); i++, p++) + cmos_write(*p, offset + i); +} + +/* Helper function to initialize cmos firmware version. */ +static void init_cmos_partition_version(struct cse_fw_table *version) +{ + u8 i, *p, offset = PARTITION_FW_CMOS_OFFSET; + version->signature = PARTITION_FW_SIGNATURE; + memset(&version->cse_version, 0, sizeof(struct fw_version)); + version->checksum = compute_ip_checksum(version, offsetof(struct cse_fw_table, checksum)); + + for (p = (u8 *)version, i = 0; i < sizeof(*version); i++, p++) + cmos_write(*p, offset + i); +} + +/* API that allows users to read CSE version stored in CMOS memory. */ +void get_cmos_cse_version(struct fw_version *cse_version) +{ + struct cse_fw_table version; + if (read_cmos_partition_version(&version)) { + /* + * CMOS failed to read the CSE version. This may be because the firmware version at + * cmos has not yet been initialized. + */ + init_cmos_partition_version(&version); + } + memcpy(cse_version, &version.cse_version, sizeof(struct fw_version)); +} + +/* API that allows users to update CSE version stored in CMOS memory. */ +void set_cmos_cse_version(const struct fw_version *cse_version) +{ + struct cse_fw_table version; + if (read_cmos_partition_version(&version)) { + /* + * CMOS failed to read the CSE version. This may be because the firmware version at + * cmos has not yet been initialized. + */ + init_cmos_partition_version(&version); + } + memcpy(&version.cse_version, cse_version, sizeof(struct fw_version)); + write_cmos_partition_version(&version); +} diff --git a/src/soc/intel/common/block/cse/cse_lite.c b/src/soc/intel/common/block/cse/cse_lite.c index 733f68b..2b4ec7a 100644 --- a/src/soc/intel/common/block/cse/cse_lite.c +++ b/src/soc/intel/common/block/cse/cse_lite.c @@ -10,6 +10,7 @@ #include <intelbasecode/debug_feature.h> #include <intelblocks/cse.h> #include <intelblocks/cse_layout.h> +#include <intelblocks/cse_cmos.h> #include <intelblocks/spi.h> #include <security/vboot/misc.h> #include <security/vboot/vboot_common.h> @@ -1295,6 +1296,47 @@ } }
+/* + * Helper function that stores current CSE firmware version and ISH version to + * CMOS memory, except during recovery mode. Also updates the current ISH version + * in CBMEM memory. + * + * To get the current ISH version, it first checks if the CMOS stored ISH version + * needs to be updated. If it does, the system then reads the ISH version from the + * CSE FPT using the HECI command. + * + * ISH IP is patched after DRAM is available so GEN_GET_IMAGE_FW_VERSION command + * has to be issued to CSE after DRAM Init to get ISH FW Version. + * + */ +static void store_cse_fw_partition_version_info(void) +{ + if (vboot_recovery_mode_enabled()) + return; + + if (!ENV_RAMSTAGE) + 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); + size_t vers_size = sizeof(struct fw_version); + struct fw_version prev_cse_fw_version; + get_cmos_cse_version(&prev_cse_fw_version); + + /* + * Compare if stored cse version (from the previous boot) is same as current + * running cse version. + */ + if (memcmp(&prev_cse_fw_version, &(cse_bp->fw_ver), vers_size)) { + /* write cse rw fw version to CMOS */ + set_cmos_cse_version(&(cse_bp->fw_ver)); + } +} + static void ramstage_cse_misc_ops(void *unused) { if (acpi_get_sleep_type() == ACPI_S3) @@ -1309,6 +1351,7 @@ */ if (CONFIG(SOC_INTEL_STORE_CSE_FPT_PARTITION_VERSION) && soc_is_ish_partition_enabled()) { + store_cse_fw_partition_version_info(); store_cse_rw_fw_version(); store_ish_version(); } diff --git a/src/soc/intel/common/block/include/intelblocks/cse_cmos.h b/src/soc/intel/common/block/include/intelblocks/cse_cmos.h new file mode 100644 index 0000000..8026a19 --- /dev/null +++ b/src/soc/intel/common/block/include/intelblocks/cse_cmos.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef SOC_INTEL_COMMON_BLOCK_CSE_CMOS_H +#define SOC_INTEL_COMMON_BLOCK_CSE_CMOS_H + +#include <intelblocks/cse.h> + +/* Function to get the cse version stored in CMOS memory */ +void get_cmos_cse_version(struct fw_version *cse_version); + +/* Function to update the cse version stored in CMOS memory */ +void set_cmos_cse_version(const struct fw_version *cse_version); + +#endif /* SOC_INTEL_COMMON_BLOCK_CSE_CMOS_H */