From ec321094f68d3fbfd13b2514aaa6405b1bcd4886 Mon Sep 17 00:00:00 2001 From: Rizwan Qureshi Date: Fri, 6 Sep 2019 20:28:43 +0530 Subject: soc/intel/common/basecode: Implement CSE update flow The following changes are done in this patch: 1. Get the CSE partition info containing version of CSE RW using GET_BOOT_PARTITION_INFO HECI command 2. Get the me_rw.version from the currently selected RW slot. 3. If the versions from the above 2 locations don't match start the update - If CSE's current boot partition is not RO, then * Set the CSE's next boot partition to RO using SET_BOOT_PARTITION HECI command. * Send global reset command to reset the system. - Enable HMRFPO (Host ME Region Flash Protection Override) operation mode using HMRFPO_ENABLE HECI command - Erase and Copy the CBFS CSE RW to CSE RW partition - Set the CSE's next boot partition to RW using SET_BOOT_PARTITION HECI command - Trigger global reset - The system should boot with the updated CSE RW partition. TEST=Verified basic update flows on hatch and helios. BUG=b:111330995 Change-Id: I12f6bba3324069d65edabaccd234006b0840e700 Signed-off-by: Rizwan Qureshi Signed-off-by: Sridhar Siricilla Signed-off-by: V Sowmya Reviewed-on: https://review.coreboot.org/c/coreboot/+/35403 Tested-by: build bot (Jenkins) Reviewed-by: Furquan Shaikh Reviewed-by: Tim Wawrzynczak Reviewed-by: Angel Pons --- src/soc/intel/common/block/cse/Kconfig | 12 ++ src/soc/intel/common/block/cse/cse_lite.c | 324 ++++++++++++++++++++++++++++-- 2 files changed, 316 insertions(+), 20 deletions(-) (limited to 'src/soc/intel/common/block/cse') diff --git a/src/soc/intel/common/block/cse/Kconfig b/src/soc/intel/common/block/cse/Kconfig index 9f09d89656..b6f49c61e8 100644 --- a/src/soc/intel/common/block/cse/Kconfig +++ b/src/soc/intel/common/block/cse/Kconfig @@ -19,3 +19,15 @@ config SOC_INTEL_CSE_LITE_SKU depends on CHROMEOS help Enables CSE Lite SKU + +config SOC_INTEL_CSE_FMAP_NAME + string "Name of CSE Region in FMAP" + default "SI_ME" + help + Name of CSE region in FMAP + +config SOC_INTEL_CSE_RW_CBFS_NAME + string "CBFS entry name for CSE RW blob" + default "me_rw" + help + CBFS entry name for Intel CSE CBFS RW blob diff --git a/src/soc/intel/common/block/cse/cse_lite.c b/src/soc/intel/common/block/cse/cse_lite.c index 8e43e35c47..a12f2d0d29 100644 --- a/src/soc/intel/common/block/cse/cse_lite.c +++ b/src/soc/intel/common/block/cse/cse_lite.c @@ -1,15 +1,30 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #include #include -#include +#include +#include +#include +#include +#include #include +#include #include #include #include +#include + +/* CSE RW version size reserved in the CSE CBFS RW binary */ +#define CSE_RW_VERSION_SZ 16 /* Converts bp index to boot partition string */ #define GET_BP_STR(bp_index) (bp_index ? "RW" : "RO") +/* CSE RW boot partition signature */ +#define CSE_RW_SIGNATURE 0x000055aa + +/* CSE RW boot partition signature size */ +#define CSE_RW_SIGN_SIZE sizeof(uint32_t) + /* * CSE Firmware supports 3 boot partitions. For CSE Lite SKU, only 2 boot partitions are * used and 3rd boot partition is set to BP_STATUS_PARTITION_NOT_PRESENT. @@ -22,10 +37,10 @@ /* CSE Lite SKU's valid bootable partition identifiers */ enum boot_partition_id { - /* RO(BP1) contains recovery/minimal boot FW */ + /* RO(BP1) contains recovery/minimal boot firmware */ RO = 0, - /* RW(BP2) contains fully functional CSE Firmware */ + /* RW(BP2) contains fully functional CSE firmware */ RW = 1 }; @@ -162,6 +177,49 @@ static const struct cse_bp_entry *cse_get_bp_entry(enum boot_partition_id bp, return &cse_bp_info->bp_entries[bp]; } +static void cse_get_bp_entry_range(const struct cse_bp_info *cse_bp_info, + enum boot_partition_id bp, uint32_t *start_offset, uint32_t *end_offset) +{ + const struct cse_bp_entry *cse_bp; + + cse_bp = cse_get_bp_entry(bp, cse_bp_info); + + if (start_offset) + *start_offset = cse_bp->start_offset; + + if (end_offset) + *end_offset = cse_bp->end_offset; + +} + +static const struct fw_version *cse_get_bp_entry_version(enum boot_partition_id bp, + const struct cse_bp_info *bp_info) +{ + const struct cse_bp_entry *cse_bp; + + cse_bp = cse_get_bp_entry(bp, bp_info); + return &cse_bp->fw_ver; +} + +static const struct fw_version *cse_get_rw_version(const struct cse_bp_info *cse_bp_info) +{ + return cse_get_bp_entry_version(RW, cse_bp_info); +} + +static bool cse_is_rw_bp_status_valid(const struct cse_bp_info *cse_bp_info) +{ + const struct cse_bp_entry *rw_bp; + + rw_bp = cse_get_bp_entry(RW, cse_bp_info); + + if (rw_bp->status == BP_STATUS_PARTITION_NOT_PRESENT || + rw_bp->status == BP_STATUS_GENERAL_FAILURE) { + printk(BIOS_ERR, "cse_lite: RW BP (status:%u) is not valid\n", rw_bp->status); + return false; + } + return true; +} + static void cse_print_boot_partition_info(const struct cse_bp_info *cse_bp_info) { const struct cse_bp_entry *cse_bp; @@ -196,6 +254,9 @@ static void cse_print_boot_partition_info(const struct cse_bp_info *cse_bp_info) * - When CSE boots from RW partition (COM: Normal and CWS: Normal) * - When CSE boots from RO partition (COM: Soft Temp Disable and CWS: Normal) * - After HMRFPO_ENABLE command is issued to CSE (COM: SECOVER_MEI_MSG and CWS: Normal) + * The prerequisite check should be handled in cse_get_bp_info() and + * cse_set_next_boot_partition() since the CSE's current operation mode is changed between these + * cmd handler calls. */ static bool cse_is_bp_cmd_info_possible(void) { @@ -294,40 +355,259 @@ static bool cse_set_next_boot_partition(enum boot_partition_id bp) return true; } +/* Set the CSE's next boot partition and issues system reset */ +static bool cse_set_and_boot_from_next_bp(enum boot_partition_id bp) +{ + if (!cse_set_next_boot_partition(bp)) + return false; + + do_global_reset(); + + die("cse_lite: Failed to reset the system\n"); + + /* Control never reaches here */ + return false; +} + static bool cse_boot_to_rw(const struct cse_bp_info *cse_bp_info) { if (cse_get_current_bp(cse_bp_info) == RW) return true; - if (!cse_set_next_boot_partition(RW)) + return cse_set_and_boot_from_next_bp(RW); +} + +static bool cse_boot_to_ro(const struct cse_bp_info *cse_bp_info) +{ + if (cse_get_current_bp(cse_bp_info) == RO) + return true; + + return cse_set_and_boot_from_next_bp(RO); +} + +static bool cse_get_rw_rdev(struct region_device *rdev) +{ + if (fmap_locate_area_as_rdev_rw(CONFIG_SOC_INTEL_CSE_FMAP_NAME, rdev) < 0) { + printk(BIOS_ERR, "cse_lite: Failed to locate %s in FMAP\n", + CONFIG_SOC_INTEL_CSE_FMAP_NAME); return false; + } - do_global_reset(); + return true; +} + +static bool cse_get_cbfs_rdev(struct region_device *source_rdev) +{ + struct cbfsf file_desc; - die("cse_lite: Failed to reset system\n"); + if (cbfs_boot_locate(&file_desc, CONFIG_SOC_INTEL_CSE_RW_CBFS_NAME, NULL) < 0) + return false; - /* Control never reaches here */ - return false; + cbfs_file_data(source_rdev, &file_desc); + return true; } -static bool cse_is_rw_status_valid(const struct cse_bp_info *cse_bp_info) +static bool cse_is_rw_bp_sign_valid(const struct region_device *target_rdev) { - const struct cse_bp_entry *rw_bp; + uint32_t cse_bp_sign; - /* RW(BP2) alone represents RW partition */ - rw_bp = cse_get_bp_entry(RW, cse_bp_info); + if (rdev_readat(target_rdev, &cse_bp_sign, 0, CSE_RW_SIGN_SIZE) != CSE_RW_SIGN_SIZE) { + printk(BIOS_ERR, "cse_lite: Failed to read RW boot partition signature\n"); + return false; + } - if (rw_bp->status == BP_STATUS_PARTITION_NOT_PRESENT || - rw_bp->status == BP_STATUS_GENERAL_FAILURE) { - printk(BIOS_ERR, "cse_lite: RW BP (status:%u) is not valid\n", rw_bp->status); + return cse_bp_sign == CSE_RW_SIGNATURE; +} + +static bool cse_get_target_rdev(const struct cse_bp_info *cse_bp_info, + struct region_device *target_rdev) +{ + struct region_device cse_region_rdev; + size_t size; + uint32_t start_offset; + uint32_t end_offset; + + if (!cse_get_rw_rdev(&cse_region_rdev)) + return false; + + cse_get_bp_entry_range(cse_bp_info, RW, &start_offset, &end_offset); + size = end_offset + 1 - start_offset; + + if (rdev_chain(target_rdev, &cse_region_rdev, start_offset, size)) + return false; + + printk(BIOS_DEBUG, "cse_lite: CSE RW partition: offset = 0x%x, size = 0x%x\n", + (uint32_t)start_offset, (uint32_t) size); + + return true; +} + +static bool cse_get_cbfs_rw_version(const struct region_device *source_rdev, + void *cse_cbfs_rw_ver) +{ + + if (rdev_readat(source_rdev, (void *) cse_cbfs_rw_ver, 0, sizeof(struct fw_version)) + != sizeof(struct fw_version)) { + printk(BIOS_ERR, "cse_lite: Failed to read CSE CBFW RW version\n"); + return false; + } + return true; +} + +/* + * Compare versions of CSE CBFS RW and CSE RW partition + * If ver_cmp_status = 0, no update is required + * If ver_cmp_status < 0, coreboot downgrades CSE RW region + * If ver_cmp_status > 0, coreboot upgrades CSE RW region + */ +static int cse_check_version_mismatch(const struct cse_bp_info *cse_bp_info, + const struct region_device *source_rdev) +{ + struct fw_version cse_cbfs_rw_ver; + const struct fw_version *cse_rw_ver; + + if (!cse_get_cbfs_rw_version(source_rdev, &cse_cbfs_rw_ver)) + return false; + + printk(BIOS_DEBUG, "cse_lite: CSE CBFS RW version : %d.%d.%d.%d\n", + cse_cbfs_rw_ver.major, + cse_cbfs_rw_ver.minor, + cse_cbfs_rw_ver.hotfix, + cse_cbfs_rw_ver.build); + + cse_rw_ver = cse_get_rw_version(cse_bp_info); + + if (cse_cbfs_rw_ver.major != cse_rw_ver->major) + return cse_cbfs_rw_ver.major - cse_rw_ver->major; + else if (cse_cbfs_rw_ver.minor != cse_rw_ver->minor) + return cse_cbfs_rw_ver.minor - cse_rw_ver->minor; + else if (cse_cbfs_rw_ver.hotfix != cse_rw_ver->hotfix) + return cse_cbfs_rw_ver.hotfix - cse_rw_ver->hotfix; + else + return cse_cbfs_rw_ver.build - cse_rw_ver->build; +} + +static bool cse_erase_rw_region(const struct region_device *target_rdev) +{ + + if (rdev_eraseat(target_rdev, 0, region_device_sz(target_rdev)) < 0) { + printk(BIOS_ERR, "cse_lite: CSE RW partition could not be erased\n"); return false; } return true; } -static bool cse_is_rw_info_valid(struct cse_bp_info *cse_bp_info) +static bool cse_copy_rw(const struct region_device *target_rdev, const void *buf, size_t offset, + size_t size) +{ + if (rdev_writeat(target_rdev, buf, offset, size) < 0) { + printk(BIOS_ERR, "cse_lite: Failed to update CSE firmware\n"); + return false; + } + + return true; +} + +static bool cse_is_rw_version_latest(const struct cse_bp_info *cse_bp_info, + const struct region_device *source_rdev) +{ + return !cse_check_version_mismatch(cse_bp_info, source_rdev); +} + +static bool cse_is_update_required(const struct cse_bp_info *cse_bp_info, + const struct region_device *source_rdev, struct region_device *target_rdev) +{ + return (!cse_is_rw_bp_sign_valid(target_rdev) || + !cse_is_rw_version_latest(cse_bp_info, source_rdev)); +} + +static bool cse_write_rw_region(const struct region_device *target_rdev, + const struct region_device *source_rdev) { - return cse_is_rw_status_valid(cse_bp_info); + void *cse_cbfs_rw = rdev_mmap(source_rdev, CSE_RW_VERSION_SZ, + region_device_sz(source_rdev) - CSE_RW_VERSION_SZ); + + /* Points to CSE CBFS RW image after boot partition signature */ + uint8_t *cse_cbfs_rw_wo_sign = (uint8_t *)cse_cbfs_rw + CSE_RW_SIGN_SIZE; + + /* Size of CSE CBFS RW image without boot partition signature */ + uint32_t cse_cbfs_rw_wo_sign_sz = region_device_sz(source_rdev) - + (CSE_RW_VERSION_SZ + CSE_RW_SIGN_SIZE); + + /* Update except CSE RW signature */ + if (!cse_copy_rw(target_rdev, cse_cbfs_rw_wo_sign, CSE_RW_SIGN_SIZE, + cse_cbfs_rw_wo_sign_sz)) + goto exit_rw_update; + + /* Update CSE RW signature to indicate update is complete */ + if (!cse_copy_rw(target_rdev, (void *)cse_cbfs_rw, 0, CSE_RW_SIGN_SIZE)) + goto exit_rw_update; + + rdev_munmap(source_rdev, cse_cbfs_rw_wo_sign); + return true; + +exit_rw_update: + rdev_munmap(source_rdev, cse_cbfs_rw_wo_sign); + return false; +} + +static bool cse_update_rw(const struct cse_bp_info *cse_bp_info, + const struct region_device *source_rdev, struct region_device *target_rdev) +{ + if (!cse_erase_rw_region(target_rdev)) + return false; + + if (!cse_write_rw_region(target_rdev, source_rdev)) + return false; + + printk(BIOS_INFO, "cse_lite: CSE RW Update Successful\n"); + return true; +} + +static bool cse_prep_for_rw_update(const struct cse_bp_info *cse_bp_info) +{ + /* + * To set CSE's operation mode to HMRFPO mode: + * 1. Ensure CSE to boot from RO(BP1) + * 2. Send HMRFPO_ENABLE command to CSE + */ + if (!cse_boot_to_ro(cse_bp_info)) + return false; + + return cse_hmrfpo_enable(); +} + +static uint8_t cse_trigger_fw_update(const struct cse_bp_info *cse_bp_info, + const struct region_device *source_rdev, struct region_device *target_rdev) +{ + if (!cse_prep_for_rw_update(cse_bp_info)) + return CSE_LITE_SKU_COMMUNICATION_ERROR; + + if (!cse_update_rw(cse_bp_info, source_rdev, target_rdev)) + return CSE_LITE_SKU_FW_UPDATE_ERROR; + + return 0; +} + +static uint8_t cse_fw_update(const struct cse_bp_info *cse_bp_info, + const struct region_device *source_rdev) +{ + struct region_device target_rdev; + + if (!cse_get_target_rdev(cse_bp_info, &target_rdev)) { + printk(BIOS_ERR, "cse_lite: Failed to get CSE RW Partition\n"); + return CSE_LITE_SKU_RW_ACCESS_ERROR; + } + + if (cse_is_update_required(cse_bp_info, source_rdev, &target_rdev)) { + printk(BIOS_DEBUG, "cse_lite: CSE RW update is initiated\n"); + return cse_trigger_fw_update(cse_bp_info, source_rdev, &target_rdev); + } + + if (!cse_is_rw_bp_status_valid(cse_bp_info)) + return CSE_LITE_SKU_RW_JUMP_ERROR; + + return 0; } void cse_fw_sync(void *unused) @@ -350,9 +630,13 @@ void cse_fw_sync(void *unused) cse_trigger_recovery(CSE_LITE_SKU_COMMUNICATION_ERROR); } - if (!cse_is_rw_info_valid(&cse_bp_info.bp_info)) { - printk(BIOS_ERR, "cse_lite: CSE RW partition is not valid\n"); - cse_trigger_recovery(CSE_LITE_SKU_RW_JUMP_ERROR); + /* If RW blob is present in CBFS, then trigger CSE firmware update */ + uint8_t rv; + struct region_device source_rdev; + if (cse_get_cbfs_rdev(&source_rdev)) { + rv = cse_fw_update(&cse_bp_info.bp_info, &source_rdev); + if (rv) + cse_trigger_recovery(rv); } if (!cse_boot_to_rw(&cse_bp_info.bp_info)) { -- cgit v1.2.3