soc/intel/common/cse: Add support for sending CSE End-of-Post message

The CSE expects the boot firmware to send it an End-of-Post message
before loading the OS. This is a security feature, and is done to ensure
that the CSE will no longer perform certain sensitive commands that are
not intended to be exposed to the OS.

If processing the EOP message fails in any way on a ChromeOS build, (and
not already in recovery mode), recovery mode will be triggered,
otherwise the CSME BWG will be followed, which is in the following
commit.

BUG=b:191362590

Signed-off-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Change-Id: I6f667905f759cc2337daca4cc6e09694e68ab7e8
Reviewed-on: https://review.coreboot.org/c/coreboot/+/55631
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Furquan Shaikh <furquan@google.com>
Reviewed-by: Subrata Banik <subrata.banik@intel.com>
Reviewed-by: Karthik Ramasubramanian <kramasub@google.com>
diff --git a/src/soc/intel/common/block/cse/Kconfig b/src/soc/intel/common/block/cse/Kconfig
index 044c60b..492213e 100644
--- a/src/soc/intel/common/block/cse/Kconfig
+++ b/src/soc/intel/common/block/cse/Kconfig
@@ -70,3 +70,11 @@
 	 This config contains the Intel CSE RW version of the blob that is provided by
 	 SOC_INTEL_CSE_RW_FILE config and the version must be set in the format
 	 major.minor.hotfix.build (ex: 14.0.40.1209).
+
+config SOC_INTEL_CSE_SET_EOP
+	bool
+	default n
+	help
+	  This config ensures coreboot will send the CSE the End-of-POST message
+	  just prior to loading the payload. This is a security feature so the
+	  CSE will no longer respond to Pre-Boot commands.
diff --git a/src/soc/intel/common/block/cse/Makefile.inc b/src/soc/intel/common/block/cse/Makefile.inc
index 30eb78e..e21efc6 100644
--- a/src/soc/intel/common/block/cse/Makefile.inc
+++ b/src/soc/intel/common/block/cse/Makefile.inc
@@ -4,6 +4,8 @@
 romstage-$(CONFIG_SOC_INTEL_CSE_LITE_SKU) += cse_lite.c
 smm-$(CONFIG_SOC_INTEL_COMMON_BLOCK_HECI_DISABLE_IN_SMM) += disable_heci.c
 
+ramstage-$(CONFIG_SOC_INTEL_CSE_SET_EOP) += cse_eop.c
+
 ifeq ($(CONFIG_SOC_INTEL_CSE_RW_UPDATE),y)
 ifneq ($(CONFIG_SOC_INTEL_CSE_RW_FILE),"")
 CSE_LITE_ME_RW = $(call strip_quotes,$(CONFIG_SOC_INTEL_CSE_RW_CBFS_NAME))
diff --git a/src/soc/intel/common/block/cse/cse_eop.c b/src/soc/intel/common/block/cse/cse_eop.c
new file mode 100644
index 0000000..98e36f0
--- /dev/null
+++ b/src/soc/intel/common/block/cse/cse_eop.c
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <bootstate.h>
+#include <console/console.h>
+#include <intelblocks/cse.h>
+#include <security/vboot/vboot_common.h>
+#include <soc/intel/common/reset.h>
+#include <types.h>
+
+enum cse_eop_result {
+	CSE_EOP_RESULT_GLOBAL_RESET_REQUESTED,
+	CSE_EOP_RESULT_SUCCESS,
+	CSE_EOP_RESULT_ERROR,
+};
+
+static enum cse_eop_result cse_send_eop(void)
+{
+	enum {
+		EOP_REQUESTED_ACTION_CONTINUE = 0,
+		EOP_REQUESTED_ACTION_GLOBAL_RESET = 1,
+	};
+	struct end_of_post_msg {
+		struct mkhi_hdr hdr;
+	} __packed msg = {
+		.hdr = {
+			.group_id = MKHI_GROUP_ID_GEN,
+			.command = MKHI_END_OF_POST,
+		},
+	};
+	struct end_of_post_resp {
+		struct mkhi_hdr hdr;
+		uint32_t requested_actions;
+	} __packed resp = {};
+	size_t resp_size = sizeof(resp);
+
+	/*
+	 * Prerequisites:
+	 * 1) HFSTS1 CWS is Normal
+	 * 2) HFSTS1 COM is Normal
+	 * 3) Only sent after DID (accomplished by compiling this into ramstage)
+	 */
+	if (!cse_is_hfs1_cws_normal() || !cse_is_hfs1_com_normal()) {
+		printk(BIOS_ERR, "HECI: Prerequisites not met for sending EOP\n");
+		return CSE_EOP_RESULT_ERROR;
+	}
+
+	printk(BIOS_INFO, "HECI: Sending End-of-Post\n");
+
+	if (!heci_send_receive(&msg, sizeof(msg), &resp, &resp_size)) {
+		printk(BIOS_ERR, "HECI: EOP send/receive fail\n");
+		return CSE_EOP_RESULT_ERROR;
+	}
+
+	if (resp.hdr.result) {
+		printk(BIOS_ERR, "HECI: EOP Resp Failed: %u\n", resp.hdr.result);
+		return CSE_EOP_RESULT_ERROR;
+	}
+
+	printk(BIOS_INFO, "CSE: EOP requested action: ");
+
+	switch (resp.requested_actions) {
+	case EOP_REQUESTED_ACTION_GLOBAL_RESET:
+		printk(BIOS_INFO, "global reset\n");
+		return CSE_EOP_RESULT_GLOBAL_RESET_REQUESTED;
+	case EOP_REQUESTED_ACTION_CONTINUE:
+		printk(BIOS_INFO, "continue boot\n");
+		return CSE_EOP_RESULT_SUCCESS;
+	default:
+		printk(BIOS_INFO, "unknown %u\n", resp.requested_actions);
+		return CSE_EOP_RESULT_ERROR;
+	}
+}
+
+static void handle_cse_eop_result(enum cse_eop_result result)
+{
+	switch (result) {
+	case CSE_EOP_RESULT_GLOBAL_RESET_REQUESTED:
+		printk(BIOS_INFO, "CSE requested global reset in EOP response, resetting...\n");
+		do_global_reset();
+		break;
+	case CSE_EOP_RESULT_SUCCESS:
+		printk(BIOS_INFO, "CSE EOP successful, continuing boot\n");
+		break;
+	case CSE_EOP_RESULT_ERROR: /* fallthrough */
+	default:
+		printk(BIOS_ERR, "ERROR: Failed to send EOP to CSE, %d\n", result);
+		/* For vboot, trigger recovery mode if applicable, as there is
+		   likely something very broken in this case. */
+		if (CONFIG(VBOOT) && !vboot_recovery_mode_enabled())
+			cse_trigger_vboot_recovery(CSE_EOP_FAIL);
+		break;
+	}
+}
+
+static void set_cse_end_of_post(void *unused)
+{
+	handle_cse_eop_result(cse_send_eop());
+}
+
+/*
+ * Ideally, to give coreboot maximum flexibility, sending EOP would be done as
+ * late possible, just before loading the payload, which would be BS_ON_EXIT
+ * here, but the platforms this is currently supported for all select
+ * HECI_DISABLE_USING_SMM, which runs in BS_ON_EXIT. Because sending EOP
+ * requires HECI to be up, and it is not trivial to control the order in which
+ * these callbacks are issued, it is called on BS_ON_ENTRY.
+ */
+BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_ENTRY, set_cse_end_of_post, NULL);
diff --git a/src/soc/intel/common/block/include/intelblocks/cse.h b/src/soc/intel/common/block/include/intelblocks/cse.h
index eac57d9..43f3137 100644
--- a/src/soc/intel/common/block/include/intelblocks/cse.h
+++ b/src/soc/intel/common/block/include/intelblocks/cse.h
@@ -25,6 +25,9 @@
 /* Get Firmware Version Command Id */
 #define MKHI_GEN_GET_FW_VERSION	0x2
 
+/* Set End-of-POST in CSE */
+#define MKHI_END_OF_POST	0xc
+
 /* 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
@@ -117,6 +120,9 @@
 
 	/* CSE CBFS RW blob layout is not correct */
 	CSE_LITE_SKU_LAYOUT_MISMATCH_ERROR = 11,
+
+	/* Error sending EOP to CSE */
+	CSE_EOP_FAIL = 12,
 };
 
 /* set up device for use in early boot enviroument with temp bar */