soc/intel/pantherlake: Remove soc_info.[hc] interface
[coreboot2.git] / src / soc / intel / common / block / cse / cse_eop.c
blob265fe04bbc4a19e4718234540c72d50a511f2ea7
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <acpi/acpi.h>
4 #include <bootstate.h>
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>
12 #include <types.h>
14 #define CSE_MAX_RETRY_CMD 3
16 enum cse_cmd_result {
17 CSE_CMD_RESULT_GLOBAL_RESET_REQUESTED,
18 CSE_CMD_RESULT_SUCCESS,
19 CSE_CMD_RESULT_ERROR,
20 CSE_CMD_RESULT_DISABLED,
21 CSE_CMD_RESULT_RETRY,
24 static enum cse_cmd_result decode_heci_send_receive_error(enum cse_tx_rx_status ret)
26 switch (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;
32 default:
33 return CSE_CMD_RESULT_ERROR;
37 static enum cse_cmd_result cse_disable_mei_bus(void)
39 struct bus_disable_message {
40 uint8_t command;
41 uint8_t reserved[3];
42 } __packed msg = {
43 .command = MEI_BUS_DISABLE_COMMAND,
45 struct bus_disable_resp {
46 uint8_t command;
47 uint8_t status;
48 uint8_t reserved[2];
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);
57 if (ret) {
58 printk(BIOS_ERR, "HECI: Failed to Disable MEI bus\n");
59 return decode_heci_send_receive_error(ret);
62 if (reply.status) {
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)
72 enum {
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 {
78 struct mkhi_hdr hdr;
79 uint32_t requested_actions;
80 } __packed resp = {};
81 size_t resp_size = sizeof(resp);
83 ret = heci_receive(&resp, &resp_size);
84 if (ret)
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;
109 default:
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 {
119 struct mkhi_hdr hdr;
120 } __packed msg = {
121 .hdr = {
122 .group_id = MKHI_GROUP_ID_GEN,
123 .command = MKHI_END_OF_POST,
128 * Prerequisites:
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);
149 if (ret)
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)
161 return ret;
163 return cse_receive_eop();
166 static enum cse_cmd_result cse_send_cmd_retries(enum cse_cmd_result (*cse_send_command)(void))
168 size_t retry;
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)
173 break;
175 return ret;
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)
196 switch (result) {
197 case CSE_CMD_RESULT_GLOBAL_RESET_REQUESTED:
198 printk(BIOS_INFO, "CSE requested global reset in EOP response, resetting...\n");
199 do_global_reset();
200 break;
201 case CSE_CMD_RESULT_SUCCESS:
202 printk(BIOS_INFO, "CSE EOP successful, continuing boot\n");
203 break;
204 case CSE_CMD_RESULT_DISABLED:
205 printk(BIOS_INFO, "CSE is disabled, continuing boot\n");
206 break;
207 case CSE_CMD_RESULT_ERROR: /* fallthrough */
208 default:
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();
218 break;
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;
227 if (eop_complete) {
228 printk(BIOS_WARNING, "EOP already sent\n");
229 return;
232 if (acpi_get_sleep_type() == ACPI_S3) {
233 printk(BIOS_INFO, "Skip sending EOP during S3 resume\n");
234 return;
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");
243 return;
246 if (!eop_sent) {
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)
251 eop_sent = true;
254 if (!wait_for_completion)
255 return;
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);
267 eop_complete = true;
271 * Don't send EOP if the following conditions are met:
272 * CSE Lite:
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"
277 * Other CSE Type:
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)
285 /* CSE Lite */
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");
290 return false;
292 /* Other CSE Type */
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");
296 return false;
299 return true;
302 void cse_send_end_of_post(void)
304 if (!is_cse_eop_supported())
305 return;
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");
314 return;
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))
322 cse_late_finalize();
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);
336 #else
337 BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_ENTRY, send_cse_eop_with_late_finalize, NULL);
338 #endif