diff --git a/src/soc/intel/common/block/cse/Kconfig b/src/soc/intel/common/block/cse/Kconfig index d1f4d33342..420b511a3b 100644 --- a/src/soc/intel/common/block/cse/Kconfig +++ b/src/soc/intel/common/block/cse/Kconfig @@ -86,6 +86,38 @@ config SOC_INTEL_CSE_SET_EOP just prior to loading the payload. This is a security feature so the CSE will no longer respond to Pre-Boot commands. +config SOC_INTEL_CSE_SUB_PART_UPDATE + bool "Enable the CSE sub-partition update Feature" + default n + depends on SOC_INTEL_CSE_LITE_SKU + help + This config will enable CSE sub-partition firmware update feature and also will be used ensure + all the required configs are provided by mainboard. + +config SOC_INTEL_CSE_IOM_CBFS_NAME + string "CBFS name for CSE sub-partition IOM binary" if SOC_INTEL_CSE_SUB_PART_UPDATE + default "cse_iom" + help + CBFS entry name for Intel CSE sub-partition IOM binary + +config SOC_INTEL_CSE_IOM_CBFS_FILE + string "Intel CBFS path and file name for CSE sub-partition IOM binary" if SOC_INTEL_CSE_SUB_PART_UPDATE + default "" + help + CBFS path and file name for Intel CSE sub-partition IOM binary + +config SOC_INTEL_CSE_NPHY_CBFS_NAME + string "CBFS name for CSE sub-partition NPHY binary" if SOC_INTEL_CSE_SUB_PART_UPDATE + default "cse_nphy" + help + CBFS entry name for Intel CSE sub-partition NPHY binary + +config SOC_INTEL_CSE_NPHY_CBFS_FILE + string "Intel CBFS path and file name for CSE sub-partition NPHY binary" if SOC_INTEL_CSE_SUB_PART_UPDATE + default "" + help + CBFS path and file name for Intel CSE sub-partition NPHY binary + if STITCH_ME_BIN config CSE_COMPONENTS_PATH diff --git a/src/soc/intel/common/block/cse/Makefile.inc b/src/soc/intel/common/block/cse/Makefile.inc index eac7d90424..a0dc780cc3 100644 --- a/src/soc/intel/common/block/cse/Makefile.inc +++ b/src/soc/intel/common/block/cse/Makefile.inc @@ -104,5 +104,23 @@ cbfs-files-y += $(CSE_RW_HASH) $(CSE_RW_HASH)-file := $(obj)/cse_rw.hash $(CSE_RW_HASH)-name := $(CSE_RW_HASH) $(CSE_RW_HASH)-type := raw - +endif + +ifeq ($(CONFIG_SOC_INTEL_CSE_SUB_PART_UPDATE),y) + +CSE_IOM_FILE = $(call strip_quotes,$(CONFIG_SOC_INTEL_CSE_IOM_CBFS_FILE)) +CSE_IOM = $(call strip_quotes,$(CONFIG_SOC_INTEL_CSE_IOM_CBFS_NAME)) +regions-for-file-$(CSE_IOM) = FW_MAIN_A,FW_MAIN_B,COREBOOT +cbfs-files-y += $(CSE_IOM) +$(CSE_IOM)-file := $(CSE_IOM_FILE) +$(CSE_IOM)-name := $(CSE_IOM) +$(CSE_IOM)-type := raw + +CSE_NPHY_FILE = $(call strip_quotes,$(CONFIG_SOC_INTEL_CSE_NPHY_CBFS_FILE)) +CSE_NPHY = $(call strip_quotes,$(CONFIG_SOC_INTEL_CSE_NPHY_CBFS_NAME)) +regions-for-file-$(CSE_NPHY) = FW_MAIN_A,FW_MAIN_B,COREBOOT +cbfs-files-y += $(CSE_NPHY) +$(CSE_NPHY)-file := $(CSE_NPHY_FILE) +$(CSE_NPHY)-name := $(CSE_NPHY) +$(CSE_NPHY)-type := raw endif diff --git a/src/soc/intel/common/block/cse/cse_lite.c b/src/soc/intel/common/block/cse/cse_lite.c index 051172fc40..6edbd843a4 100644 --- a/src/soc/intel/common/block/cse/cse_lite.c +++ b/src/soc/intel/common/block/cse/cse_lite.c @@ -5,9 +5,17 @@ #include #include #include +#include #include #include #include +#include + +#define BPDT_HEADER_SZ sizeof(struct bpdt_header) +#define BPDT_ENTRY_SZ sizeof(struct bpdt_entry) +#define SUBPART_HEADER_SZ sizeof(struct subpart_hdr) +#define SUBPART_ENTRY_SZ sizeof(struct subpart_entry) +#define SUBPART_MANIFEST_HDR_SZ sizeof(struct subpart_entry_manifest_header) /* Converts bp index to boot partition string */ #define GET_BP_STR(bp_index) (bp_index ? "RW" : "RO") @@ -627,8 +635,8 @@ static enum csme_failure_reason cse_update_rw(const struct cse_bp_info *cse_bp_i struct region_device *target_rdev) { if (region_device_sz(target_rdev) < cse_blob_sz) { - printk(BIOS_ERR, "RW update does not fit. CSE RW flash region size: %zx, Update blob size:%zx\n", - region_device_sz(target_rdev), cse_blob_sz); + printk(BIOS_ERR, "RW update does not fit. CSE RW flash region size: %zx," + "Update blob size:%zx\n", region_device_sz(target_rdev), cse_blob_sz); return CSE_LITE_SKU_LAYOUT_MISMATCH_ERROR; } @@ -727,11 +735,248 @@ static uint8_t cse_fw_update(const struct cse_bp_info *cse_bp_info) return cse_trigger_fw_update(cse_bp_info, status, &target_rdev); } +static const char *cse_sub_part_str(enum bpdt_entry_type type) +{ + switch (type) { + case IOM_FW: + return "IOM"; + case NPHY_FW: + return "NPHY"; + default: + return "Unknown"; + } +} + +static bool cse_sub_part_get_target_rdev(struct region_device *target_rdev, + const char *region_name, enum bpdt_entry_type type) +{ + struct bpdt_header bpdt_hdr; + struct region_device cse_rdev; + struct bpdt_entry bpdt_entries[MAX_SUBPARTS]; + uint8_t i; + + if (fmap_locate_area_as_rdev_rw(region_name, &cse_rdev) < 0) { + printk(BIOS_ERR, "cse_lite: Failed to locate %s in the FMAP\n", region_name); + return false; + } + + if ((rdev_readat(&cse_rdev, &bpdt_hdr, 0, BPDT_HEADER_SZ)) != BPDT_HEADER_SZ) { + printk(BIOS_ERR, "cse_lite: Failed to read BPDT header from CSE region\n"); + return false; + } + + if ((rdev_readat(&cse_rdev, bpdt_entries, BPDT_HEADER_SZ, + (bpdt_hdr.descriptor_count * BPDT_ENTRY_SZ))) != + (bpdt_hdr.descriptor_count * BPDT_ENTRY_SZ)) { + printk(BIOS_ERR, "cse_lite: Failed to read BPDT entries from CSE region\n"); + return false; + } + + /* walk through BPDT entries to identify sub-partition's payload offset and size */ + for (i = 0; i < bpdt_hdr.descriptor_count; i++) { + if (bpdt_entries[i].type == type) { + printk(BIOS_INFO, "cse_lite: Sub-partition %s- offset = 0x%x," + "size = 0x%x\n", cse_sub_part_str(type), bpdt_entries[i].offset, + bpdt_entries[i].size); + + if (rdev_chain(target_rdev, &cse_rdev, bpdt_entries[i].offset, + bpdt_entries[i].size)) + return false; + else + return true; + } + } + + printk(BIOS_ERR, "cse_lite: Sub-partition %s is not found\n", cse_sub_part_str(type)); + return false; +} + +static bool cse_get_sub_part_fw_version(enum bpdt_entry_type type, + const struct region_device *rdev, + struct fw_version *fw_ver) +{ + struct subpart_entry subpart_entry; + struct subpart_entry_manifest_header man_hdr; + + if ((rdev_readat(rdev, &subpart_entry, SUBPART_HEADER_SZ, SUBPART_ENTRY_SZ)) + != SUBPART_ENTRY_SZ) { + printk(BIOS_ERR, "cse_lite: Failed to read %s sub partition entry\n", + cse_sub_part_str(type)); + return false; + } + + if ((rdev_readat(rdev, &man_hdr, subpart_entry.offset_bytes, SUBPART_MANIFEST_HDR_SZ)) + != SUBPART_MANIFEST_HDR_SZ) { + printk(BIOS_ERR, "cse_lite: Failed to read %s Sub part entry #0 manifest\n", + cse_sub_part_str(type)); + return false; + } + + fw_ver->major = man_hdr.binary_version.major; + fw_ver->minor = man_hdr.binary_version.minor; + fw_ver->hotfix = man_hdr.binary_version.hotfix; + fw_ver->build = man_hdr.binary_version.build; + + return true; +} + +static void cse_sub_part_get_source_fw_version(void *subpart_cbfs_rw, struct fw_version *fw_ver) +{ + uint8_t *ptr = (uint8_t *)subpart_cbfs_rw; + struct subpart_entry *subpart_entry; + struct subpart_entry_manifest_header *man_hdr; + + subpart_entry = (struct subpart_entry *) (ptr + SUBPART_HEADER_SZ); + man_hdr = (struct subpart_entry_manifest_header *) (ptr + subpart_entry->offset_bytes); + + fw_ver->major = man_hdr->binary_version.major; + fw_ver->minor = man_hdr->binary_version.minor; + fw_ver->hotfix = man_hdr->binary_version.hotfix; + fw_ver->build = man_hdr->binary_version.build; +} + +static bool cse_prep_for_component_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_sub_part_trigger_update(enum bpdt_entry_type type, uint8_t bp, + const void *subpart_cbfs_rw, const size_t blob_sz, + struct region_device *target_rdev) +{ + if (region_device_sz(target_rdev) < blob_sz) { + printk(BIOS_ERR, "cse_lite: %s Target sub-partition size: %zx, " + "smaller than blob size:%zx, abort update\n", + cse_sub_part_str(type), region_device_sz(target_rdev), blob_sz); + return CSE_LITE_SKU_SUB_PART_LAYOUT_MISMATCH_ERROR; + } + + /* Erase CSE Lite sub-partition */ + if (!cse_erase_rw_region(target_rdev)) + return CSE_LITE_SKU_SUB_PART_UPDATE_FAIL; + + /* Update CSE Lite sub-partition */ + if (!cse_copy_rw(target_rdev, (void *)subpart_cbfs_rw, 0, blob_sz)) + return CSE_LITE_SKU_SUB_PART_UPDATE_FAIL; + + printk(BIOS_INFO, "cse_lite: CSE %s %s Update successful\n", GET_BP_STR(bp), + cse_sub_part_str(type)); + + return CSE_LITE_SKU_PART_UPDATE_SUCCESS; +} + +static uint8_t handle_cse_sub_part_fw_update_rv(uint8_t rv) +{ + switch (rv) { + case CSE_LITE_SKU_PART_UPDATE_SUCCESS: + case CSE_LITE_SKU_SUB_PART_UPDATE_NOT_REQ: + return rv; + default: + cse_trigger_vboot_recovery(rv); + } + /* Control never reaches here */ + return rv; +} + +static enum csme_failure_reason cse_sub_part_fw_component_update(enum bpdt_entry_type type, + const struct cse_bp_info *cse_bp_info, const char *name) +{ + struct region_device target_rdev; + struct fw_version target_fw_ver, source_fw_ver; + enum csme_failure_reason rv; + size_t size; + static const char * const cse_regions[] = {"CSE_RO", "CSE_RW"}; + + void *subpart_cbfs_rw = cbfs_map(name, &size); + if (!subpart_cbfs_rw) { + printk(BIOS_ERR, "cse_lite: Not able to map %s CBFS file\n", + cse_sub_part_str(type)); + return CSE_LITE_SKU_SUB_PART_BLOB_ACCESS_ERR; + } + + cse_sub_part_get_source_fw_version(subpart_cbfs_rw, &source_fw_ver); + printk(BIOS_INFO, "cse_lite: CBFS %s FW Version: %x.%x.%x.%x\n", cse_sub_part_str(type), + source_fw_ver.major, source_fw_ver.minor, source_fw_ver.hotfix, + source_fw_ver.build); + + /* Trigger sub-partition update in CSE RO and CSE RW */ + for (size_t bp = 0; bp < ARRAY_SIZE(cse_regions); bp++) { + if (!cse_sub_part_get_target_rdev(&target_rdev, cse_regions[bp], type)) { + rv = CSE_LITE_SKU_SUB_PART_ACCESS_ERR; + goto error_exit; + } + + if (!cse_get_sub_part_fw_version(type, &target_rdev, &target_fw_ver)) { + rv = CSE_LITE_SKU_SUB_PART_ACCESS_ERR; + goto error_exit; + } + + printk(BIOS_INFO, "cse_lite: %s %s FW Version: %x.%x.%x.%x\n", cse_regions[bp], + cse_sub_part_str(type), target_fw_ver.major, + target_fw_ver.minor, target_fw_ver.hotfix, target_fw_ver.build); + + if (!cse_compare_sub_part_version(&target_fw_ver, &source_fw_ver)) { + printk(BIOS_INFO, "cse_lite: %s %s update is not required\n", + cse_regions[bp], cse_sub_part_str(type)); + rv = CSE_LITE_SKU_SUB_PART_UPDATE_NOT_REQ; + continue; + } + + printk(BIOS_INFO, "CSE %s %s Update initiated\n", GET_BP_STR(bp), + cse_sub_part_str(type)); + + if (!cse_prep_for_component_update(cse_bp_info)) { + rv = CSE_LITE_SKU_SUB_PART_ACCESS_ERR; + goto error_exit; + } + + rv = cse_sub_part_trigger_update(type, bp, subpart_cbfs_rw, + size, &target_rdev); + + if (rv != CSE_LITE_SKU_PART_UPDATE_SUCCESS) + goto error_exit; + } +error_exit: + cbfs_unmap(subpart_cbfs_rw); + return rv; +} + +static uint8_t cse_sub_part_fw_update(const struct cse_bp_info *cse_bp_info) +{ + if (skip_cse_sub_part_update()) { + printk(BIOS_INFO, "CSE Sub-partition update not required\n"); + return CSE_LITE_SKU_SUB_PART_UPDATE_NOT_REQ; + } + + int rv; + rv = cse_sub_part_fw_component_update(IOM_FW, cse_bp_info, + CONFIG_SOC_INTEL_CSE_IOM_CBFS_NAME); + + handle_cse_sub_part_fw_update_rv(rv); + + rv = cse_sub_part_fw_component_update(NPHY_FW, cse_bp_info, + CONFIG_SOC_INTEL_CSE_NPHY_CBFS_NAME); + + return handle_cse_sub_part_fw_update_rv(rv); +} + void cse_fw_sync(void) { static struct get_bp_info_rsp cse_bp_info; - if (vboot_recovery_mode_enabled()) { + /* + * If system is in recovery mode, skip CSE Lite update if CSE sub-partition update + * is not enabled and continue to update CSE sub-partitions. + */ + if (vboot_recovery_mode_enabled() && !CONFIG(SOC_INTEL_CSE_SUB_PART_UPDATE)) { printk(BIOS_DEBUG, "cse_lite: Skip switching to RW in the recovery path\n"); return; } @@ -744,7 +989,31 @@ void cse_fw_sync(void) if (!cse_get_bp_info(&cse_bp_info)) { printk(BIOS_ERR, "cse_lite: Failed to get CSE boot partition info\n"); - cse_trigger_vboot_recovery(CSE_COMMUNICATION_ERROR); + + /* If system is in recovery mode, don't trigger recovery again */ + if (!vboot_recovery_mode_enabled()) { + cse_trigger_vboot_recovery(CSE_COMMUNICATION_ERROR); + } else { + printk(BIOS_ERR, "cse_lite: System is already in Recovery Mode, " + "so no action\n"); + return; + } + } + + /* + * If system is in recovery mode, CSE Lite update has to be skipped but CSE + * sub-partitions like NPHY and IOM have to to be updated. If CSE sub-parition update + * fails during recovery, just continue to boot. + */ + if (CONFIG(SOC_INTEL_CSE_SUB_PART_UPDATE) && vboot_recovery_mode_enabled()) { + if (cse_sub_part_fw_update(&cse_bp_info.bp_info) == + CSE_LITE_SKU_PART_UPDATE_SUCCESS) { + cse_board_reset(); + do_global_reset(); + die("ERROR: GLOBAL RESET Failed to reset the system\n"); + } + + return; } if (!cse_fix_data_failure_err(&cse_bp_info.bp_info)) @@ -761,6 +1030,9 @@ void cse_fw_sync(void) cse_trigger_vboot_recovery(rv); } + if (CONFIG(SOC_INTEL_CSE_SUB_PART_UPDATE)) + cse_sub_part_fw_update(&cse_bp_info.bp_info); + if (!cse_is_rw_bp_status_valid(&cse_bp_info.bp_info)) cse_trigger_vboot_recovery(CSE_LITE_SKU_RW_JUMP_ERROR); diff --git a/src/soc/intel/common/block/include/intelblocks/cse.h b/src/soc/intel/common/block/include/intelblocks/cse.h index 4184b17c52..68f1d3c2a8 100644 --- a/src/soc/intel/common/block/include/intelblocks/cse.h +++ b/src/soc/intel/common/block/include/intelblocks/cse.h @@ -143,6 +143,24 @@ enum csme_failure_reason { /* Error sending EOP to CSE */ CSE_EOP_FAIL = 12, + + /* CSE Sub-partition update fail */ + CSE_LITE_SKU_SUB_PART_UPDATE_FAIL = 13, + + /* CSE sub-partition access failure */ + CSE_LITE_SKU_SUB_PART_ACCESS_ERR = 14, + + /* CSE CBFS sub-partition access error */ + CSE_LITE_SKU_SUB_PART_BLOB_ACCESS_ERR = 15, + + /* CSE Lite sub-partition update is not required */ + CSE_LITE_SKU_SUB_PART_UPDATE_NOT_REQ = 16, + + /* CSE Lite sub-partition layout mismatch error */ + CSE_LITE_SKU_SUB_PART_LAYOUT_MISMATCH_ERROR = 17, + + /* CSE Lite sub-partition update success */ + CSE_LITE_SKU_PART_UPDATE_SUCCESS = 18, }; /* set up device for use in early boot enviroument with temp bar */ @@ -320,4 +338,9 @@ enum cse_device_state get_cse_device_state(unsigned int devfn); /* Function that put the CSE into desired state based on `requested_state` */ bool set_cse_device_state(unsigned int devfn, enum cse_device_state requested_state); +/* + * Check if cse sub-parition update is required or not. + * Returns true if cse sub-parition update is required otherwise false. + */ +bool skip_cse_sub_part_update(void); #endif // SOC_INTEL_COMMON_CSE_H diff --git a/src/soc/intel/common/block/include/intelblocks/cse_layout.h b/src/soc/intel/common/block/include/intelblocks/cse_layout.h new file mode 100644 index 0000000000..4c88cc52d3 --- /dev/null +++ b/src/soc/intel/common/block/include/intelblocks/cse_layout.h @@ -0,0 +1,105 @@ +/* BPDT version 1.7 support */ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include + +enum bpdt_entry_type { + SMIP = 0, + CSE_RBE = 1, + CSE_BUP = 2, + UCODE = 3, + IBB = 4, + S_BPDT = 5, + OBB = 6, + CSE_MAIN = 7, + ISH = 8, + CSE_IDLM = 9, + IFP_OVERRIDE = 10, + UTOK = 11, + UFS_PHY = 12, + UFS_GPP = 13, + PMC = 14, + IUNIT = 15, + NVM_CFG = 16, + UEP = 17, + OEM_KM = 20, + PAVP = 22, + IOM_FW = 23, + NPHY_FW = 24, + TBT_FW = 25, + ICC = 32, + + MAX_SUBPARTS, +}; + +struct bpdt_header { + uint32_t signature; /* BPDT_SIGNATURE */ + uint16_t descriptor_count; + uint8_t version; /* Layout 1.7 = 2 */ + uint8_t flags; + uint32_t checksum; + uint32_t ifwi_version; + struct { + uint16_t major; + uint16_t minor; + uint16_t build; + uint16_t hotfix; + } fit_tool_version; +} __packed; + +struct cse_layout { + uint8_t rom_bypass[16]; + uint16_t size; + uint16_t redundancy; + uint32_t checksum; + uint32_t data_offset; + uint32_t data_size; + uint32_t bp1_offset; + uint32_t bp1_size; + uint32_t bp2_offset; + uint32_t bp2_size; + uint32_t bp3_offset; + uint32_t bp3_size; + uint32_t bp4_offset; + uint32_t bp4_size; + uint32_t bp5_offset; + uint32_t bp5_size; + uint32_t temp_base_addr; + uint32_t temp_base_size; + uint32_t flog_offset; + uint32_t flog_size; +} __packed; + +struct bpdt_entry { + uint32_t type; + uint32_t offset; + uint32_t size; +} __packed; + +struct subpart_hdr { + uint32_t signature; /* SUBPART_SIGNATURE */ + uint32_t count; + uint8_t hdr_version; /* Header version = 2 */ + uint8_t entry_version; /* Entry version = 1 */ + uint8_t length; + uint8_t reserved; + uint8_t name[4]; + uint32_t checksum; +} __packed; + +struct subpart_entry { + uint8_t name[12]; + uint32_t offset_bytes; + uint32_t length; + uint32_t rsvd2; +} __packed; + +struct subpart_entry_manifest_header { + uint8_t reserved[36]; + struct { + uint16_t major; + uint16_t minor; + uint16_t build; + uint16_t hotfix; + } binary_version; +} __packed;