diff options
author | Sridhar Siricilla <sridhar.siricilla@intel.com> | 2019-09-12 17:18:20 +0530 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2020-05-04 09:47:29 +0000 |
commit | f87ff33a898e93012112bf9a446182ac7e024bc8 (patch) | |
tree | 975718ba37d19aa68afd4fb4b46cce9c9a8426a1 /src/soc/intel/common | |
parent | 89ac87a976e2ace9a3637c99209adec492566d30 (diff) |
soc/intel/common/block/cse: Add boot partition related APIs
In CSE Firmware Custom SKU, CSE region is logically divided into
2 boot partitions. These boot partitions are represented by BP1(RO),
BP2(RW). With CSE Firmware Custom SKU, CSE can boot from either
RO(BP1) or RW(BP2).
The CSE Firmware Custom SKU layout appears as below:
------------- -------------------- ---------------------
|CSE REGION | => | RO | RW | DATA | => | BP1 | BP2 | DATA |
------------- -------------------- ---------------------
In order to support CSE FW update to RW region, below APIs help coreboot
to get info about the boot partitions, and allows coreboot to set CSE
to boot from required boot partition (either RO(BP1) or RW(BP2)).
GET_BOOT_PARTITION_INFO - Provides info on available partitions in the CSE
region. The API provides info on boot partitions like start/end offsets
of a partition within CSE region, and their version and partition status.
SET_BOOT_PARTITION_INFO - Sets CSE's next boot partition to boot from.
With the HECI API, firmware can notify CSE to boot from RO(BP1) or RW(BP2)
on next boot.
As system having CSE Firmware Custom SKU, boots from RO(BP1) after G3,
so coreboot sets CSE to boot from RW(BP2) in normal mode and further,
coreboot ensure CSE to boot from whichever is selected boot partition
if system is in recovery mode.
BUG=b:145809764
TEST=Verified on hatch
Change-Id: Iaa62409c0616d5913d21374a8a6804f82258eb4f
Signed-off-by: Sridhar Siricilla <sridhar.siricilla@intel.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/35402
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Furquan Shaikh <furquan@google.com>
Diffstat (limited to 'src/soc/intel/common')
-rw-r--r-- | src/soc/intel/common/block/cse/Kconfig | 7 | ||||
-rw-r--r-- | src/soc/intel/common/block/cse/Makefile.inc | 1 | ||||
-rw-r--r-- | src/soc/intel/common/block/cse/custom_bp.c | 329 | ||||
-rw-r--r-- | src/soc/intel/common/block/include/intelblocks/cse.h | 14 |
4 files changed, 351 insertions, 0 deletions
diff --git a/src/soc/intel/common/block/cse/Kconfig b/src/soc/intel/common/block/cse/Kconfig index 15de0b0536..e566dddcce 100644 --- a/src/soc/intel/common/block/cse/Kconfig +++ b/src/soc/intel/common/block/cse/Kconfig @@ -12,3 +12,10 @@ config SOC_INTEL_COMMON_BLOCK_HECI_DISABLE_IN_SMM help Use this config to include common CSE block to make HECI function disable in SMM mode + +config SOC_INTEL_CSE_CUSTOM_SKU + bool + default n + depends on CHROMEOS + help + Enables CSE Custom SKU diff --git a/src/soc/intel/common/block/cse/Makefile.inc b/src/soc/intel/common/block/cse/Makefile.inc index 90f76d59b0..418b7a2efa 100644 --- a/src/soc/intel/common/block/cse/Makefile.inc +++ b/src/soc/intel/common/block/cse/Makefile.inc @@ -1,4 +1,5 @@ bootblock-$(CONFIG_SOC_INTEL_COMMON_BLOCK_CSE) += cse.c romstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_CSE) += cse.c ramstage-$(CONFIG_SOC_INTEL_COMMON_BLOCK_CSE) += cse.c +ramstage-$(CONFIG_SOC_INTEL_CSE_CUSTOM_SKU) += custom_bp.c smm-$(CONFIG_SOC_INTEL_COMMON_BLOCK_HECI_DISABLE_IN_SMM) += disable_heci.c diff --git a/src/soc/intel/common/block/cse/custom_bp.c b/src/soc/intel/common/block/cse/custom_bp.c new file mode 100644 index 0000000000..653684a7bd --- /dev/null +++ b/src/soc/intel/common/block/cse/custom_bp.c @@ -0,0 +1,329 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* This file is part of the coreboot project. */ +#include <bootstate.h> +#include <console/console.h> +#include <soc/intel/common/reset.h> +#include <intelblocks/cse.h> +#include <security/vboot/vboot_common.h> +#include <string.h> + +/* Converts bp index to boot partition string */ +#define GET_BP_STR(bp_index) (bp_index ? "RW" : "RO") + +/* + * CSE Firmware supports 3 boot partitions. For CSE Custom SKU, only 2 boot partitions are + * used and 3rd boot partition is set to BP_STATUS_PARTITION_NOT_PRESENT. + * CSE Custom SKU Image Layout: + * ------------- ------------------- --------------------- + * |CSE REGION | => | RO | RW | DATA | => | BP1 | BP2 | DATA | + * ------------- ------------------- --------------------- + */ +#define CSE_MAX_BOOT_PARTITIONS 3 + +/* CSE Custom SKU's valid bootable partition identifiers */ +enum boot_partition_id { + /* RO(BP1) contains recovery/minimal boot FW */ + RO = 0, + + /* RW(BP2) contains fully functional CSE Firmware */ + RW = 1 +}; + +/* + * Boot partition status. + * The status is returned in response to MKHI_BUP_COMMON_GET_BOOT_PARTITION_INFO cmd. + */ +enum bp_status { + /* This value is returned when a partition has no errors */ + BP_STATUS_SUCCESS = 0, + + /* + * This value is returned when a partition should be present based on layout, but it is + * not valid. + */ + BP_STATUS_GENERAL_FAILURE = 1, + + /* This value is returned when a partition is not present per initial image layout */ + BP_STATUS_PARTITION_NOT_PRESENT = 2, + +}; + +/* + * Boot Partition Info Flags + * The flags are returned in response to MKHI_BUP_COMMON_GET_BOOT_PARTITION_INFO cmd. + */ +enum bp_info_flags { + + /* Redundancy Enabled: It indicates CSE supports RO(BP1) and RW(BP2) regions */ + BP_INFO_REDUNDANCY_EN = 1 << 0, + + /* It indicates RO(BP1) supports Minimal Recovery Mode */ + BP_INFO_MIN_RECOV_MODE_EN = 1 << 1, + + /* + * Read-only Config Enabled: It indicates HW protection to CSE RO region is enabled. + * The option is relevant only if the BP_INFO_MIN_RECOV_MODE_EN flag is enabled. + */ + BP_INFO_READ_ONLY_CFG = 1 << 2, +}; + +/* Boot Partition FW Version */ +struct fw_version { + uint16_t major; + uint16_t minor; + uint16_t hotfix; + uint16_t build; +} __packed; + +/* CSE boot partition entry info */ +struct cse_bp_entry { + /* Boot partition version */ + struct fw_version fw_ver; + + /* Boot partition status */ + uint32_t status; + + /* Starting offset of the partition within CSE region */ + uint32_t start_offset; + + /* Ending offset of the partition within CSE region */ + uint32_t end_offset; + uint8_t reserved[12]; +} __packed; + +/* CSE boot partition info */ +struct cse_bp_info { + /* Number of boot partitions */ + uint8_t total_number_of_bp; + + /* Current boot partition */ + uint8_t current_bp; + + /* Next boot partition */ + uint8_t next_bp; + + /* Boot Partition Info Flags */ + uint8_t flags; + + /* Boot Partition Entry Info */ + struct cse_bp_entry bp_entries[CSE_MAX_BOOT_PARTITIONS]; +} __packed; + +struct get_bp_info_rsp { + struct mkhi_hdr hdr; + struct cse_bp_info bp_info; +} __packed; + +static uint8_t cse_get_current_bp(const struct cse_bp_info *cse_bp_info) +{ + return cse_bp_info->current_bp; +} + +static const struct cse_bp_entry *cse_get_bp_entry(enum boot_partition_id bp, + const struct cse_bp_info *cse_bp_info) +{ + return &cse_bp_info->bp_entries[bp]; +} + +static void cse_print_boot_partition_info(const struct cse_bp_info *cse_bp_info) +{ + const struct cse_bp_entry *cse_bp; + + printk(BIOS_DEBUG, "ME: Number of partitions = %d\n", cse_bp_info->total_number_of_bp); + printk(BIOS_DEBUG, "ME: Current partition = %s\n", GET_BP_STR(cse_bp_info->current_bp)); + printk(BIOS_DEBUG, "ME: Next partition = %s\n", GET_BP_STR(cse_bp_info->next_bp)); + printk(BIOS_DEBUG, "ME: Flags = 0x%x\n", cse_bp_info->flags); + + /* Log version info of RO & RW partitions */ + cse_bp = cse_get_bp_entry(RO, cse_bp_info); + printk(BIOS_DEBUG, "ME: %s version = %d.%d.%d.%d (Status=0x%x, Start=0x%x, End=0x%x)\n", + GET_BP_STR(RO), cse_bp->fw_ver.major, cse_bp->fw_ver.minor, + cse_bp->fw_ver.hotfix, cse_bp->fw_ver.build, + cse_bp->status, cse_bp->start_offset, + cse_bp->end_offset); + + cse_bp = cse_get_bp_entry(RW, cse_bp_info); + printk(BIOS_DEBUG, "ME: %s version = %d.%d.%d.%d (Status=0x%x, Start=0x%x, End=0x%x)\n", + GET_BP_STR(RW), cse_bp->fw_ver.major, cse_bp->fw_ver.minor, + cse_bp->fw_ver.hotfix, cse_bp->fw_ver.build, + cse_bp->status, cse_bp->start_offset, + cse_bp->end_offset); +} + +/* + * Checks prerequisites for MKHI_BUP_COMMON_GET_BOOT_PARTITION_INFO and + * MKHI_BUP_COMMON_SET_BOOT_PARTITION_INFO HECI commands. + * It allows execution of the Boot Partition commands in below scenarios: + * - 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) + */ +static bool cse_is_bp_cmd_info_possible(void) +{ + if (cse_is_hfs1_cws_normal()) { + if (cse_is_hfs1_com_normal()) + return true; + if (cse_is_hfs1_com_secover_mei_msg()) + return true; + if (cse_is_hfs1_com_soft_temp_disable()) + return true; + } + return false; +} + +static bool cse_get_bp_info(struct get_bp_info_rsp *bp_info_rsp) +{ + struct get_bp_info_req { + struct mkhi_hdr hdr; + uint8_t reserved[4]; + } __packed; + + struct get_bp_info_req info_req = { + .hdr.group_id = MKHI_GROUP_ID_BUP_COMMON, + .hdr.command = MKHI_BUP_COMMON_GET_BOOT_PARTITION_INFO, + .reserved = {0}, + }; + + if (!cse_is_bp_cmd_info_possible()) { + printk(BIOS_ERR, "cse_bp: CSE does not meet prerequisites\n"); + return false; + } + + size_t resp_size = sizeof(struct get_bp_info_rsp); + + if (!heci_send_receive(&info_req, sizeof(info_req), bp_info_rsp, &resp_size)) { + printk(BIOS_ERR, "cse_bp: Could not get partition info\n"); + return false; + } + + if (bp_info_rsp->hdr.result) { + printk(BIOS_ERR, "cse_bp: Get partition info resp failed: %d\n", + bp_info_rsp->hdr.result); + return false; + } + + cse_print_boot_partition_info(&bp_info_rsp->bp_info); + + return true; +} +/* + * 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. + * The CSE's valid bootable partitions are BP1(RO) and BP2(RW). + * This function must be used before EOP. + * Returns false on failure and true on success. + */ +static bool cse_set_next_boot_partition(enum boot_partition_id bp) +{ + struct set_boot_partition_info_req { + struct mkhi_hdr hdr; + uint8_t next_bp; + uint8_t reserved[3]; + } __packed; + + struct set_boot_partition_info_req switch_req = { + .hdr.group_id = MKHI_GROUP_ID_BUP_COMMON, + .hdr.command = MKHI_BUP_COMMON_SET_BOOT_PARTITION_INFO, + .next_bp = bp, + .reserved = {0}, + }; + + if (bp != RO && bp != RW) { + printk(BIOS_ERR, "cse_bp: Incorrect partition id(%d) is provided", bp); + return false; + } + + printk(BIOS_INFO, "cse_bp: Set Boot Partition Info Command (%s)\n", GET_BP_STR(bp)); + + if (!cse_is_bp_cmd_info_possible()) { + printk(BIOS_ERR, "cse_bp: CSE does not meet prerequisites\n"); + return false; + } + + struct mkhi_hdr switch_resp; + size_t sw_resp_sz = sizeof(struct mkhi_hdr); + + if (!heci_send_receive(&switch_req, sizeof(switch_req), &switch_resp, &sw_resp_sz)) + return false; + + if (switch_resp.result) { + printk(BIOS_ERR, "cse_bp: Set Boot Partition Info Response Failed: %d\n", + switch_resp.result); + return false; + } + + return true; +} + +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 false; + + do_global_reset(); + + die("cse_bp: Failed to reset system\n"); + + /* Control never reaches here */ + return false; +} + +static bool cse_is_rw_status_valid(const struct cse_bp_info *cse_bp_info) +{ + const struct cse_bp_entry *rw_bp; + + /* RW(BP2) alone represents RW partition */ + 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_bp: RW BP (status:%u) is not valid\n", rw_bp->status); + return false; + } + return true; +} + +static bool cse_is_rw_info_valid(struct cse_bp_info *cse_bp_info) +{ + return cse_is_rw_status_valid(cse_bp_info); +} + +void cse_fw_sync(void *unused) +{ + static struct get_bp_info_rsp cse_bp_info; + + if (vboot_recovery_mode_enabled()) { + printk(BIOS_DEBUG, "cse_bp: Skip switching to RW in the recovery path\n"); + return; + } + + /* If CSE SKU type is not Custom, skip enabling CSE Custom SKU */ + if (!cse_is_hfs3_fw_sku_custom()) { + printk(BIOS_ERR, "cse_bp: Not a CSE Custom SKU\n"); + return; + } + + if (!cse_get_bp_info(&cse_bp_info)) { + printk(BIOS_ERR, "cse_bp: Failed to get CSE boot partition info\n"); + goto failed; + } + + + if (!cse_is_rw_info_valid(&cse_bp_info.bp_info)) { + printk(BIOS_ERR, "cse_bp: CSE RW partition is not valid\n"); + goto failed; + } + + if (!cse_boot_to_rw(&cse_bp_info.bp_info)) { + printk(BIOS_ERR, "cse_bp: Failed to switch to RW\n"); + goto failed; + } + return; +failed: + do_global_reset(); +} + +BOOT_STATE_INIT_ENTRY(BS_PRE_DEVICE, BS_ON_ENTRY, cse_fw_sync, NULL); diff --git a/src/soc/intel/common/block/include/intelblocks/cse.h b/src/soc/intel/common/block/include/intelblocks/cse.h index ead5d41e8c..5cad63c47f 100644 --- a/src/soc/intel/common/block/include/intelblocks/cse.h +++ b/src/soc/intel/common/block/include/intelblocks/cse.h @@ -23,6 +23,7 @@ #define MKHI_GROUP_ID_CBM 0x0 #define MKHI_GROUP_ID_HMRFPO 0x5 #define MKHI_GROUP_ID_GEN 0xff +#define MKHI_GROUP_ID_BUP_COMMON 0xf0 /* Global Reset Command ID */ #define MKHI_CBM_GLOBAL_RESET_REQ 0xb @@ -37,6 +38,10 @@ /* Get Firmware Version Command Id */ #define MKHI_GEN_GET_FW_VERSION 0x2 +/* Boot partition info and set boot partition info command ids */ +#define MKHI_BUP_COMMON_GET_BOOT_PARTITION_INFO 0x1c +#define MKHI_BUP_COMMON_SET_BOOT_PARTITION_INFO 0x1d + /* ME Current Working States */ #define ME_HFS1_CWS_NORMAL 0x5 @@ -219,4 +224,13 @@ bool cse_is_hfs3_fw_sku_custom(void); * Returns 0 on failure and 1 on success. */ uint8_t cse_wait_com_soft_temp_disable(void); + +/* + * The CSE Custom SKU supports notion of RO and RW boot partitions. The function will set + * CSE's boot partition as per Chrome OS boot modes. In normal mode, the function allows CSE to + * boot from RW and triggers recovery mode if CSE fails to jump to RW. + * In software triggered recovery mode, the function allows CSE to boot from whatever is + * currently selected partition. + */ +void cse_fw_sync(void *unused); #endif // SOC_INTEL_COMMON_CSE_H |