1 /* SPDX-License-Identifier: GPL-2.0-only */
5 #include <console/console.h>
6 #include <intelblocks/cse.h>
7 #include <intelblocks/pmc_ipc.h>
8 #include <security/vboot/vboot_common.h>
9 #include <soc/intel/common/reset.h>
10 #include <soc/pci_devs.h>
11 #include <timestamp.h>
14 #define CSE_MAX_RETRY_CMD 3
17 CSE_CMD_RESULT_GLOBAL_RESET_REQUESTED
,
18 CSE_CMD_RESULT_SUCCESS
,
20 CSE_CMD_RESULT_DISABLED
,
24 static enum cse_cmd_result
decode_heci_send_receive_error(enum cse_tx_rx_status ret
)
27 case CSE_TX_ERR_CSE_NOT_READY
:
28 case CSE_RX_ERR_CSE_NOT_READY
:
29 case CSE_RX_ERR_RESP_LEN_MISMATCH
:
30 case CSE_RX_ERR_TIMEOUT
:
31 return CSE_CMD_RESULT_RETRY
;
33 return CSE_CMD_RESULT_ERROR
;
37 static enum cse_cmd_result
cse_disable_mei_bus(void)
39 struct bus_disable_message
{
43 .command
= MEI_BUS_DISABLE_COMMAND
,
45 struct bus_disable_resp
{
49 } __packed reply
= {};
51 size_t reply_sz
= sizeof(reply
);
52 enum cse_tx_rx_status ret
;
54 printk(BIOS_DEBUG
, "HECI, Sending MEI BIOS DISABLE command\n");
55 ret
= heci_send_receive(&msg
, sizeof(msg
), &reply
, &reply_sz
, HECI_MEI_ADDR
);
58 printk(BIOS_ERR
, "HECI: Failed to Disable MEI bus\n");
59 return decode_heci_send_receive_error(ret
);
63 printk(BIOS_ERR
, "HECI: MEI_Bus_Disable Failed (status: %d)\n", reply
.status
);
64 return CSE_CMD_RESULT_ERROR
;
67 return CSE_CMD_RESULT_SUCCESS
;
70 static enum cse_cmd_result
cse_receive_eop(void)
73 EOP_REQUESTED_ACTION_CONTINUE
= 0,
74 EOP_REQUESTED_ACTION_GLOBAL_RESET
= 1,
76 enum cse_tx_rx_status ret
;
77 struct end_of_post_resp
{
79 uint32_t requested_actions
;
81 size_t resp_size
= sizeof(resp
);
83 ret
= heci_receive(&resp
, &resp_size
);
85 return decode_heci_send_receive_error(ret
);
87 if (resp
.hdr
.group_id
!= MKHI_GROUP_ID_GEN
||
88 resp
.hdr
.command
!= MKHI_END_OF_POST
) {
89 printk(BIOS_ERR
, "HECI: EOP Unexpected response group or command.\n");
90 if (CONFIG(SOC_INTEL_CSE_SEND_EOP_ASYNC
))
91 printk(BIOS_ERR
, "HECI: It could be a HECI command conflict.\n");
92 return CSE_CMD_RESULT_ERROR
;
95 if (resp
.hdr
.result
) {
96 printk(BIOS_ERR
, "HECI: EOP Resp Failed: %u\n", resp
.hdr
.result
);
97 return CSE_CMD_RESULT_ERROR
;
100 printk(BIOS_INFO
, "CSE: EOP requested action: ");
102 switch (resp
.requested_actions
) {
103 case EOP_REQUESTED_ACTION_GLOBAL_RESET
:
104 printk(BIOS_INFO
, "global reset\n");
105 return CSE_CMD_RESULT_GLOBAL_RESET_REQUESTED
;
106 case EOP_REQUESTED_ACTION_CONTINUE
:
107 printk(BIOS_INFO
, "continue boot\n");
108 return CSE_CMD_RESULT_SUCCESS
;
110 printk(BIOS_INFO
, "unknown %u\n", resp
.requested_actions
);
111 return CSE_CMD_RESULT_ERROR
;
115 static enum cse_cmd_result
cse_send_eop(void)
117 enum cse_tx_rx_status ret
;
118 struct end_of_post_msg
{
122 .group_id
= MKHI_GROUP_ID_GEN
,
123 .command
= MKHI_END_OF_POST
,
129 * 1) HFSTS1 CWS is Normal
130 * 2) HFSTS1 COM is Normal
131 * 3) Only sent after DID (accomplished by compiling this into ramstage)
134 if (cse_is_hfs1_com_soft_temp_disable()) {
135 printk(BIOS_ERR
, "HECI: Prerequisites not met for sending EOP\n");
136 if (CONFIG(SOC_INTEL_CSE_LITE_SKU
))
137 return CSE_CMD_RESULT_ERROR
;
138 return CSE_CMD_RESULT_DISABLED
;
141 if (!cse_is_hfs1_cws_normal() || !cse_is_hfs1_com_normal()) {
142 printk(BIOS_ERR
, "HECI: Prerequisites not met for sending EOP\n");
143 return CSE_CMD_RESULT_ERROR
;
146 printk(BIOS_INFO
, "HECI: Sending End-of-Post\n");
148 ret
= heci_send(&msg
, sizeof(msg
), BIOS_HOST_ADDR
, HECI_MKHI_ADDR
);
150 return decode_heci_send_receive_error(ret
);
152 return CSE_CMD_RESULT_SUCCESS
;
155 static enum cse_cmd_result
cse_send_and_receive_eop(void)
157 enum cse_cmd_result ret
;
159 ret
= cse_send_eop();
160 if (ret
!= CSE_CMD_RESULT_SUCCESS
)
163 return cse_receive_eop();
166 static enum cse_cmd_result
cse_send_cmd_retries(enum cse_cmd_result (*cse_send_command
)(void))
169 enum cse_cmd_result ret
;
170 for (retry
= 0; retry
< CSE_MAX_RETRY_CMD
; retry
++) {
171 ret
= cse_send_command();
172 if (ret
!= CSE_CMD_RESULT_RETRY
)
179 * On EOP error, the BIOS is required to send an MEI bus disable message to the
180 * CSE, followed by disabling all MEI devices. After successfully completing
181 * this, it is safe to boot.
183 static void cse_handle_eop_error(void)
185 if (cse_send_cmd_retries(cse_disable_mei_bus
))
186 die("Failed to disable MEI bus while recovering from EOP error\n"
187 "Preventing system from booting into an insecure state.\n");
189 if (!cse_disable_mei_devices())
190 die("Error disabling MEI devices while recovering from EOP error\n"
191 "Preventing system from booting into an insecure state.\n");
194 static void handle_cse_eop_result(enum cse_cmd_result result
)
197 case CSE_CMD_RESULT_GLOBAL_RESET_REQUESTED
:
198 printk(BIOS_INFO
, "CSE requested global reset in EOP response, resetting...\n");
201 case CSE_CMD_RESULT_SUCCESS
:
202 printk(BIOS_INFO
, "CSE EOP successful, continuing boot\n");
204 case CSE_CMD_RESULT_DISABLED
:
205 printk(BIOS_INFO
, "CSE is disabled, continuing boot\n");
207 case CSE_CMD_RESULT_ERROR
: /* fallthrough */
209 printk(BIOS_ERR
, "Failed to send EOP to CSE, %d\n", result
);
210 /* For vboot, trigger recovery mode if applicable, as there is
211 likely something very broken in this case. */
212 if (CONFIG(VBOOT
) && !vboot_recovery_mode_enabled())
213 cse_trigger_vboot_recovery(CSE_EOP_FAIL
);
215 /* In non-vboot builds or recovery mode, follow the BWG in order
216 to continue to boot securely. */
217 cse_handle_eop_error();
222 static void do_send_end_of_post(bool wait_for_completion
)
224 static bool eop_sent
= false, eop_complete
= false;
225 enum cse_cmd_result ret
;
228 printk(BIOS_WARNING
, "EOP already sent\n");
232 if (acpi_get_sleep_type() == ACPI_S3
) {
233 printk(BIOS_INFO
, "Skip sending EOP during S3 resume\n");
238 * If CSE is already hidden then accessing CSE registers would be wrong and will
239 * receive junk, hence, return as CSE is already disabled.
241 if (!is_cse_enabled()) {
242 printk(BIOS_DEBUG
, "CSE is disabled, cannot send End-of-Post (EOP) message\n");
247 set_cse_device_state(PCH_DEVFN_CSE
, DEV_ACTIVE
);
248 timestamp_add_now(TS_ME_END_OF_POST_START
);
249 ret
= cse_send_cmd_retries(cse_send_eop
);
250 if (ret
== CSE_CMD_RESULT_SUCCESS
)
254 if (!wait_for_completion
)
257 set_cse_device_state(PCH_DEVFN_CSE
, DEV_ACTIVE
);
258 ret
= cse_receive_eop();
259 if (ret
!= CSE_CMD_RESULT_SUCCESS
) {
260 ret
= cse_send_cmd_retries(cse_send_and_receive_eop
);
261 handle_cse_eop_result(ret
);
263 timestamp_add_now(TS_ME_END_OF_POST_END
);
265 set_cse_device_state(PCH_DEVFN_CSE
, DEV_IDLE
);
271 * Don't send EOP if the following conditions are met:
273 * 1. "The platform is running CSE-Lite SKU" AND
274 * 2. 'The CSE is running the RO FW" AND
275 * 3. "The board is in recovery mode"
278 * 1. "The board is in recovery mode"
280 * The above conditions summarize that the CSE is in "SOFT TEMP DISABLE" state,
281 * hence, don't send the EOP command to CSE.
283 static bool is_cse_eop_supported(void)
286 if ((CONFIG(SOC_INTEL_CSE_LITE_SKU
) && vboot_recovery_mode_enabled()) &&
287 cse_is_hfs1_com_soft_temp_disable()) {
288 printk(BIOS_INFO
, "HECI: coreboot in recovery mode; found CSE Lite in expected "
289 "SOFT TEMP DISABLE state, skipping EOP\n");
293 if (cse_is_hfs1_com_soft_temp_disable()) {
294 printk(BIOS_INFO
, "HECI: coreboot in recovery mode; found CSE in expected "
295 "SOFT TEMP DISABLE state, skipping EOP\n");
302 void cse_send_end_of_post(void)
304 if (!is_cse_eop_supported())
307 return do_send_end_of_post(!CONFIG(SOC_INTEL_CSE_SEND_EOP_ASYNC
));
310 static void send_cse_eop_with_late_finalize(void *unused
)
312 if (CONFIG(SOC_INTEL_CSE_SEND_EOP_BY_PAYLOAD
)) {
313 printk(BIOS_INFO
, "Deferring CSE EOP to payload\n");
317 if (is_cse_eop_supported())
318 do_send_end_of_post(true);
320 if (CONFIG(SOC_INTEL_CSE_SEND_EOP_LATE
) ||
321 CONFIG(SOC_INTEL_CSE_SEND_EOP_ASYNC
))
326 * Ideally, to give coreboot maximum flexibility, sending EOP would be done as
327 * late possible. If HECI_DISABLE_USING_SMM is selected, then sending EOP must
328 * be performed before the HECI bus is disabled, so these boards use
329 * BS_PAYLOAD_LOAD, which happens before the HECI_DISABLE_USING_SMM Kconfig takes
330 * effect (EOP is sent using the HECI bus).
331 * Otherwise, EOP can be pushed a little later, and can be performed in
332 * BS_PAYLOAD_BOOT instead.
334 #if !CONFIG(HECI_DISABLE_USING_SMM)
335 BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_BOOT
, BS_ON_ENTRY
, send_cse_eop_with_late_finalize
, NULL
);
337 BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD
, BS_ON_ENTRY
, send_cse_eop_with_late_finalize
, NULL
);