summaryrefslogtreecommitdiff
path: root/src/soc/intel
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc/intel')
-rw-r--r--src/soc/intel/common/block/cse/Kconfig7
-rw-r--r--src/soc/intel/common/block/cse/Makefile.inc1
-rw-r--r--src/soc/intel/common/block/cse/custom_bp.c329
-rw-r--r--src/soc/intel/common/block/include/intelblocks/cse.h14
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