diff options
-rw-r--r-- | src/soc/intel/common/block/cse/Kconfig | 1 | ||||
-rw-r--r-- | src/soc/intel/common/block/cse/cse_eop.c | 85 | ||||
-rw-r--r-- | src/soc/intel/common/block/include/intelblocks/cse.h | 6 |
3 files changed, 92 insertions, 0 deletions
diff --git a/src/soc/intel/common/block/cse/Kconfig b/src/soc/intel/common/block/cse/Kconfig index 492213e732..94f8cd5ebc 100644 --- a/src/soc/intel/common/block/cse/Kconfig +++ b/src/soc/intel/common/block/cse/Kconfig @@ -74,6 +74,7 @@ config SOC_INTEL_CSE_RW_VERSION config SOC_INTEL_CSE_SET_EOP bool default n + select PMC_IPC_ACPI_INTERFACE 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 diff --git a/src/soc/intel/common/block/cse/cse_eop.c b/src/soc/intel/common/block/cse/cse_eop.c index 98e36f027c..330aa68260 100644 --- a/src/soc/intel/common/block/cse/cse_eop.c +++ b/src/soc/intel/common/block/cse/cse_eop.c @@ -3,16 +3,71 @@ #include <bootstate.h> #include <console/console.h> #include <intelblocks/cse.h> +#include <intelblocks/pmc_ipc.h> #include <security/vboot/vboot_common.h> #include <soc/intel/common/reset.h> #include <types.h> +#define PMC_IPC_MEI_DISABLE_ID 0xa9 +#define PMC_IPC_MEI_DISABLE_SUBID_ENABLE 0 +#define PMC_IPC_MEI_DISABLE_SUBID_DISABLE 1 + enum cse_eop_result { CSE_EOP_RESULT_GLOBAL_RESET_REQUESTED, CSE_EOP_RESULT_SUCCESS, CSE_EOP_RESULT_ERROR, }; +static bool cse_disable_mei_bus(void) +{ + struct bus_disable_message { + uint8_t command; + uint8_t reserved[3]; + } __packed msg = { + .command = MEI_BUS_DISABLE_COMMAND, + }; + struct bus_disable_resp { + uint8_t command; + uint8_t status; + uint8_t reserved[2]; + } __packed reply = {}; + + /* This is sent to the MEI client endpoint, not the MKHI endpoint */ + int ret = heci_send(&msg, sizeof(msg), BIOS_HOST_ADDR, HECI_MEI_ADDR); + if (!ret) { + printk(BIOS_ERR, "HECI: Failed to send MEI bus disable command!\n"); + return false; + } + + size_t reply_sz = sizeof(reply); + if (!heci_receive(&reply, &reply_sz)) { + printk(BIOS_ERR, "HECI: Failed to receive a reply from CSE\n"); + return false; + } + + if (reply.status) { + printk(BIOS_ERR, "HECI: MEI_Bus_Disable Failed (status: %d)\n", reply.status); + return false; + } + + return true; +} + +static bool cse_disable_mei_devices(void) +{ + struct pmc_ipc_buffer req = { 0 }; + struct pmc_ipc_buffer rsp; + uint32_t cmd; + + cmd = pmc_make_ipc_cmd(PMC_IPC_MEI_DISABLE_ID, PMC_IPC_MEI_DISABLE_SUBID_DISABLE, 0); + if (pmc_send_ipc_cmd(cmd, &req, &rsp) != CB_SUCCESS) { + printk(BIOS_ERR, "CSE: Failed to disable MEI devices\n"); + return false; + } + + return true; +} + static enum cse_eop_result cse_send_eop(void) { enum { @@ -33,6 +88,16 @@ static enum cse_eop_result cse_send_eop(void) } __packed resp = {}; size_t resp_size = sizeof(resp); + /* For a CSE-Lite SKU, if the CSE is running RO FW and the board is + running vboot in recovery mode, the CSE is expected to be in SOFT + TEMP DISABLE state. */ + if (CONFIG(SOC_INTEL_CSE_LITE_SKU) && vboot_recovery_mode_enabled() && + cse_is_hfs1_com_soft_temp_disable()) { + printk(BIOS_INFO, "HECI: coreboot in recovery mode; found CSE in expected SOFT " + "TEMP DISABLE state, skipping EOP\n"); + return CSE_EOP_RESULT_SUCCESS; + } + /* * Prerequisites: * 1) HFSTS1 CWS is Normal @@ -71,6 +136,22 @@ static enum cse_eop_result cse_send_eop(void) } } +/* + * On EOP error, the BIOS is required to send an MEI bus disable message to the + * CSE, followed by disabling all MEI devices. After successfully completing + * this, it is safe to boot. + */ +static void cse_handle_eop_error(void) +{ + if (!cse_disable_mei_bus()) + die("Failed to disable MEI bus while recovering from EOP error\n" + "Preventing system from booting into an insecure state.\n"); + + if (!cse_disable_mei_devices()) + die("Error disabling MEI devices while recovering from EOP error\n" + "Preventing system from booting into an insecure state.\n"); +} + static void handle_cse_eop_result(enum cse_eop_result result) { switch (result) { @@ -88,6 +169,10 @@ static void handle_cse_eop_result(enum cse_eop_result result) likely something very broken in this case. */ if (CONFIG(VBOOT) && !vboot_recovery_mode_enabled()) cse_trigger_vboot_recovery(CSE_EOP_FAIL); + + /* In non-vboot builds or recovery mode, follow the BWG in order + to continue to boot securely. */ + cse_handle_eop_error(); break; } } diff --git a/src/soc/intel/common/block/include/intelblocks/cse.h b/src/soc/intel/common/block/include/intelblocks/cse.h index 43f3137d6a..153cb228da 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 +/* MEI bus disable command. Must be sent to MEI client endpoint, not MKHI */ +#define MEI_BUS_DISABLE_COMMAND 0xc + /* Set End-of-POST in CSE */ #define MKHI_END_OF_POST 0xc @@ -220,6 +223,9 @@ int cse_hmrfpo_get_status(void); /* Fixed Address MEI Header's ME Address field value */ #define HECI_MKHI_ADDR 0x07 +/* Fixed Address MEI Header's ME Address for MEI bus messages */ +#define HECI_MEI_ADDR 0x00 + /* HMRFPO Status types */ /* Host can't access ME region */ #define MKHI_HMRFPO_DISABLED 0 |