coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
cse_eop.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
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>
10 #include <soc/pci_devs.h>
11 #include <timestamp.h>
12 #include <types.h>
13 
14 #define CSE_MAX_RETRY_CMD 3
15 
22 };
23 
25 {
26  switch (ret) {
30  case CSE_RX_ERR_TIMEOUT:
31  return CSE_CMD_RESULT_RETRY;
32  default:
33  return CSE_CMD_RESULT_ERROR;
34  }
35 }
36 
37 static enum cse_cmd_result cse_disable_mei_bus(void)
38 {
39  struct bus_disable_message {
40  uint8_t command;
41  uint8_t reserved[3];
42  } __packed msg = {
44  };
45  struct bus_disable_resp {
46  uint8_t command;
47  uint8_t status;
48  uint8_t reserved[2];
49  } __packed reply = {};
50 
51  size_t reply_sz = sizeof(reply);
52  enum cse_tx_rx_status ret;
53 
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);
56 
57  if (ret) {
58  printk(BIOS_ERR, "HECI: Failed to Disable MEI bus\n");
60  }
61 
62  if (reply.status) {
63  printk(BIOS_ERR, "HECI: MEI_Bus_Disable Failed (status: %d)\n", reply.status);
64  return CSE_CMD_RESULT_ERROR;
65  }
66 
68 }
69 
70 static enum cse_cmd_result cse_send_eop(void)
71 {
72  enum cse_tx_rx_status ret;
73  enum {
74  EOP_REQUESTED_ACTION_CONTINUE = 0,
75  EOP_REQUESTED_ACTION_GLOBAL_RESET = 1,
76  };
77  struct end_of_post_msg {
78  struct mkhi_hdr hdr;
79  } __packed msg = {
80  .hdr = {
81  .group_id = MKHI_GROUP_ID_GEN,
82  .command = MKHI_END_OF_POST,
83  },
84  };
85  struct end_of_post_resp {
86  struct mkhi_hdr hdr;
87  uint32_t requested_actions;
88  } __packed resp = {};
89  size_t resp_size = sizeof(resp);
90 
91  /* For a CSE-Lite SKU, if the CSE is running RO FW and the board is
92  running vboot in recovery mode, the CSE is expected to be in SOFT
93  TEMP DISABLE state. */
94  if (CONFIG(SOC_INTEL_CSE_LITE_SKU) && vboot_recovery_mode_enabled() &&
96  printk(BIOS_INFO, "HECI: coreboot in recovery mode; found CSE in expected SOFT "
97  "TEMP DISABLE state, skipping EOP\n");
99  }
100 
101  /*
102  * Prerequisites:
103  * 1) HFSTS1 CWS is Normal
104  * 2) HFSTS1 COM is Normal
105  * 3) Only sent after DID (accomplished by compiling this into ramstage)
106  */
107 
109  printk(BIOS_ERR, "HECI: Prerequisites not met for sending EOP\n");
110  if (CONFIG(SOC_INTEL_CSE_LITE_SKU))
111  return CSE_CMD_RESULT_ERROR;
113  }
114 
116  printk(BIOS_ERR, "HECI: Prerequisites not met for sending EOP\n");
117  return CSE_CMD_RESULT_ERROR;
118  }
119 
120  printk(BIOS_INFO, "HECI: Sending End-of-Post\n");
121 
122  ret = heci_send_receive(&msg, sizeof(msg), &resp, &resp_size, HECI_MKHI_ADDR);
123  if (ret)
124  return decode_heci_send_receive_error(ret);
125 
126  if (resp.hdr.result) {
127  printk(BIOS_ERR, "HECI: EOP Resp Failed: %u\n", resp.hdr.result);
128  return CSE_CMD_RESULT_ERROR;
129  }
130 
131  printk(BIOS_INFO, "CSE: EOP requested action: ");
132 
133  switch (resp.requested_actions) {
134  case EOP_REQUESTED_ACTION_GLOBAL_RESET:
135  printk(BIOS_INFO, "global reset\n");
137  case EOP_REQUESTED_ACTION_CONTINUE:
138  printk(BIOS_INFO, "continue boot\n");
139  return CSE_CMD_RESULT_SUCCESS;
140  default:
141  printk(BIOS_INFO, "unknown %u\n", resp.requested_actions);
142  return CSE_CMD_RESULT_ERROR;
143  }
144 }
145 
146 static enum cse_cmd_result cse_send_cmd_retries(enum cse_cmd_result (*cse_send_command)(void))
147 {
148  size_t retry;
149  enum cse_cmd_result ret;
150  for (retry = 0; retry < CSE_MAX_RETRY_CMD; retry++) {
151  ret = cse_send_command();
152  if (ret != CSE_CMD_RESULT_RETRY)
153  break;
154  }
155  return ret;
156 }
157 
158 /*
159  * On EOP error, the BIOS is required to send an MEI bus disable message to the
160  * CSE, followed by disabling all MEI devices. After successfully completing
161  * this, it is safe to boot.
162  */
163 static void cse_handle_eop_error(void)
164 {
166  die("Failed to disable MEI bus while recovering from EOP error\n"
167  "Preventing system from booting into an insecure state.\n");
168 
170  die("Error disabling MEI devices while recovering from EOP error\n"
171  "Preventing system from booting into an insecure state.\n");
172 }
173 
175 {
176  switch (result) {
178  printk(BIOS_INFO, "CSE requested global reset in EOP response, resetting...\n");
179  do_global_reset();
180  break;
182  printk(BIOS_INFO, "CSE EOP successful, continuing boot\n");
183  break;
185  printk(BIOS_INFO, "CSE is disabled, continuing boot\n");
186  break;
187  case CSE_CMD_RESULT_ERROR: /* fallthrough */
188  default:
189  printk(BIOS_ERR, "Failed to send EOP to CSE, %d\n", result);
190  /* For vboot, trigger recovery mode if applicable, as there is
191  likely something very broken in this case. */
192  if (CONFIG(VBOOT) && !vboot_recovery_mode_enabled())
194 
195  /* In non-vboot builds or recovery mode, follow the BWG in order
196  to continue to boot securely. */
198  break;
199  }
200 }
201 
202 static void do_send_end_of_post(void)
203 {
204  static bool eop_sent = false;
205 
206  if (eop_sent) {
207  printk(BIOS_WARNING, "EOP already sent\n");
208  return;
209  }
210 
211  if (acpi_get_sleep_type() == ACPI_S3) {
212  printk(BIOS_INFO, "Skip sending EOP during S3 resume\n");
213  return;
214  }
215 
216  /*
217  * If CSE is already hidden then accessing CSE registers would be wrong and will
218  * receive junk, hence, return as CSE is already disabled.
219  */
220  if (!is_cse_enabled()) {
221  printk(BIOS_DEBUG, "CSE is disabled, cannot send End-of-Post (EOP) message\n");
222  return;
223  }
224 
226 
230 
232 
233  eop_sent = true;
234 }
235 
237 {
238  return do_send_end_of_post();
239 }
240 
241 static void set_cse_end_of_post(void *unused)
242 {
243  return do_send_end_of_post();
244 }
245 
246 /*
247  * Ideally, to give coreboot maximum flexibility, sending EOP would be done as
248  * late possible. If HECI_DISABLE_USING_SMM is selected, then sending EOP must
249  * be performed before the HECI bus is disabled, so these boards use
250  * BS_PAYLOAD_LOAD, which happens before the HECI_DISABLE_USING_SMM Kconfig takes
251  * effect (EOP is sent using the HECI bus).
252  * Otherwise, EOP can be pushed a little later, and can be performed in
253  * BS_PAYLOAD_BOOT instead.
254  */
255 #if !CONFIG(HECI_DISABLE_USING_SMM)
257 #else
259 #endif
@ BS_PAYLOAD_LOAD
Definition: bootstate.h:88
@ BS_PAYLOAD_BOOT
Definition: bootstate.h:89
@ BS_ON_ENTRY
Definition: bootstate.h:95
#define retry(attempts, condition,...)
Definition: helpers.h:126
bool cse_is_hfs1_com_soft_temp_disable(void)
Definition: cse.c:260
bool cse_is_hfs1_cws_normal(void)
Definition: cse.c:241
bool cse_is_hfs1_com_normal(void)
Definition: cse.c:250
enum cse_tx_rx_status heci_send_receive(const void *snd_msg, size_t snd_sz, void *rcv_msg, size_t *rcv_sz, uint8_t cse_addr)
Definition: cse.c:574
bool is_cse_enabled(void)
Definition: cse.c:640
bool set_cse_device_state(unsigned int devfn, enum cse_device_state requested_state)
Definition: cse.c:987
void cse_trigger_vboot_recovery(enum csme_failure_reason reason)
Definition: cse.c:918
#define printk(level,...)
Definition: stdlib.h:16
void __noreturn die(const char *fmt,...)
Definition: die.c:17
@ CSE_EOP_FAIL
Definition: cse.h:184
cse_tx_rx_status
Definition: cse.h:113
@ CSE_RX_ERR_TIMEOUT
Definition: cse.h:124
@ CSE_TX_ERR_CSE_NOT_READY
Definition: cse.h:133
@ CSE_RX_ERR_RESP_LEN_MISMATCH
Definition: cse.h:130
@ CSE_RX_ERR_CSE_NOT_READY
Definition: cse.h:136
#define HECI_MKHI_ADDR
Definition: cse.h:414
@ DEV_ACTIVE
Definition: cse.h:501
@ DEV_IDLE
Definition: cse.h:500
#define HECI_MEI_ADDR
Definition: cse.h:417
#define MEI_BUS_DISABLE_COMMAND
Definition: cse.h:34
static void cse_handle_eop_error(void)
Definition: cse_eop.c:163
static void set_cse_end_of_post(void *unused)
Definition: cse_eop.c:241
static enum cse_cmd_result cse_send_cmd_retries(enum cse_cmd_result(*cse_send_command)(void))
Definition: cse_eop.c:146
#define CSE_MAX_RETRY_CMD
Definition: cse_eop.c:14
static void handle_cse_eop_result(enum cse_cmd_result result)
Definition: cse_eop.c:174
static enum cse_cmd_result cse_send_eop(void)
Definition: cse_eop.c:70
static enum cse_cmd_result decode_heci_send_receive_error(enum cse_tx_rx_status ret)
Definition: cse_eop.c:24
void cse_send_end_of_post(void)
Definition: cse_eop.c:236
BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_BOOT, BS_ON_ENTRY, set_cse_end_of_post, NULL)
static enum cse_cmd_result cse_disable_mei_bus(void)
Definition: cse_eop.c:37
cse_cmd_result
Definition: cse_eop.c:16
@ CSE_CMD_RESULT_DISABLED
Definition: cse_eop.c:20
@ CSE_CMD_RESULT_GLOBAL_RESET_REQUESTED
Definition: cse_eop.c:17
@ CSE_CMD_RESULT_RETRY
Definition: cse_eop.c:21
@ CSE_CMD_RESULT_SUCCESS
Definition: cse_eop.c:18
@ CSE_CMD_RESULT_ERROR
Definition: cse_eop.c:19
static void do_send_end_of_post(void)
Definition: cse_eop.c:202
bool cse_disable_mei_devices(void)
Definition: disable_heci.c:31
@ CONFIG
Definition: dsi_common.h:201
@ ACPI_S3
Definition: acpi.h:1383
int acpi_get_sleep_type(void)
Definition: acpi.c:96
void timestamp_add_now(enum timestamp_id id)
Definition: timestamp.c:141
#define BIOS_INFO
BIOS_INFO - Expected events.
Definition: loglevel.h:113
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
#define BIOS_ERR
BIOS_ERR - System in incomplete state.
Definition: loglevel.h:72
#define BIOS_WARNING
BIOS_WARNING - Bad configuration.
Definition: loglevel.h:86
result
Definition: mrc_cache.c:35
int vboot_recovery_mode_enabled(void)
Definition: bootmode.c:21
#define PCH_DEVFN_CSE
Definition: pci_devs.h:144
void do_global_reset(void)
Definition: reset.c:8
#define MKHI_GROUP_ID_GEN
Definition: me.h:252
#define MKHI_END_OF_POST
Definition: me.h:254
#define NULL
Definition: stddef.h:19
unsigned int uint32_t
Definition: stdint.h:14
unsigned char uint8_t
Definition: stdint.h:8
Definition: x86.c:23
u32 command
Definition: crashlog.h:113
uint32_t status
Definition: lcc.c:29
Definition: cse.h:80
@ TS_ME_END_OF_POST_END
@ TS_ME_END_OF_POST_START