Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
| 2 | |
MAULIK V VAGHELA | a4af1b5 | 2022-02-21 19:02:19 +0530 | [diff] [blame] | 3 | #include <acpi/acpi.h> |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 4 | #include <bootstate.h> |
| 5 | #include <console/console.h> |
| 6 | #include <intelblocks/cse.h> |
Tim Wawrzynczak | 9fdd2b2 | 2021-06-18 10:34:09 -0600 | [diff] [blame] | 7 | #include <intelblocks/pmc_ipc.h> |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 8 | #include <security/vboot/vboot_common.h> |
| 9 | #include <soc/intel/common/reset.h> |
Subrata Banik | c6e2552 | 2021-09-30 18:14:09 +0530 | [diff] [blame] | 10 | #include <soc/pci_devs.h> |
Tim Wawrzynczak | f004a72 | 2021-06-22 19:15:07 -0600 | [diff] [blame] | 11 | #include <timestamp.h> |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 12 | #include <types.h> |
| 13 | |
| 14 | enum cse_eop_result { |
| 15 | CSE_EOP_RESULT_GLOBAL_RESET_REQUESTED, |
| 16 | CSE_EOP_RESULT_SUCCESS, |
| 17 | CSE_EOP_RESULT_ERROR, |
Sean Rhodes | b5b22a74 | 2021-08-25 21:47:00 +0100 | [diff] [blame] | 18 | CSE_EOP_RESULT_DISABLED, |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 19 | }; |
| 20 | |
Tim Wawrzynczak | 9fdd2b2 | 2021-06-18 10:34:09 -0600 | [diff] [blame] | 21 | static bool cse_disable_mei_bus(void) |
| 22 | { |
| 23 | struct bus_disable_message { |
| 24 | uint8_t command; |
| 25 | uint8_t reserved[3]; |
| 26 | } __packed msg = { |
| 27 | .command = MEI_BUS_DISABLE_COMMAND, |
| 28 | }; |
| 29 | struct bus_disable_resp { |
| 30 | uint8_t command; |
| 31 | uint8_t status; |
| 32 | uint8_t reserved[2]; |
| 33 | } __packed reply = {}; |
| 34 | |
Tim Wawrzynczak | 9fdd2b2 | 2021-06-18 10:34:09 -0600 | [diff] [blame] | 35 | size_t reply_sz = sizeof(reply); |
Rizwan Qureshi | 957857d | 2021-08-30 16:43:57 +0530 | [diff] [blame] | 36 | |
| 37 | if (!heci_send_receive(&msg, sizeof(msg), &reply, &reply_sz, HECI_MEI_ADDR)) { |
| 38 | printk(BIOS_ERR, "HECI: Failed to Disable MEI bus\n"); |
Tim Wawrzynczak | 9fdd2b2 | 2021-06-18 10:34:09 -0600 | [diff] [blame] | 39 | return false; |
| 40 | } |
| 41 | |
| 42 | if (reply.status) { |
| 43 | printk(BIOS_ERR, "HECI: MEI_Bus_Disable Failed (status: %d)\n", reply.status); |
| 44 | return false; |
| 45 | } |
| 46 | |
| 47 | return true; |
| 48 | } |
| 49 | |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 50 | static enum cse_eop_result cse_send_eop(void) |
| 51 | { |
| 52 | enum { |
| 53 | EOP_REQUESTED_ACTION_CONTINUE = 0, |
| 54 | EOP_REQUESTED_ACTION_GLOBAL_RESET = 1, |
| 55 | }; |
| 56 | struct end_of_post_msg { |
| 57 | struct mkhi_hdr hdr; |
| 58 | } __packed msg = { |
| 59 | .hdr = { |
| 60 | .group_id = MKHI_GROUP_ID_GEN, |
| 61 | .command = MKHI_END_OF_POST, |
| 62 | }, |
| 63 | }; |
| 64 | struct end_of_post_resp { |
| 65 | struct mkhi_hdr hdr; |
| 66 | uint32_t requested_actions; |
| 67 | } __packed resp = {}; |
| 68 | size_t resp_size = sizeof(resp); |
| 69 | |
Tim Wawrzynczak | 9fdd2b2 | 2021-06-18 10:34:09 -0600 | [diff] [blame] | 70 | /* For a CSE-Lite SKU, if the CSE is running RO FW and the board is |
| 71 | running vboot in recovery mode, the CSE is expected to be in SOFT |
| 72 | TEMP DISABLE state. */ |
| 73 | if (CONFIG(SOC_INTEL_CSE_LITE_SKU) && vboot_recovery_mode_enabled() && |
| 74 | cse_is_hfs1_com_soft_temp_disable()) { |
| 75 | printk(BIOS_INFO, "HECI: coreboot in recovery mode; found CSE in expected SOFT " |
| 76 | "TEMP DISABLE state, skipping EOP\n"); |
| 77 | return CSE_EOP_RESULT_SUCCESS; |
| 78 | } |
| 79 | |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 80 | /* |
| 81 | * Prerequisites: |
| 82 | * 1) HFSTS1 CWS is Normal |
| 83 | * 2) HFSTS1 COM is Normal |
| 84 | * 3) Only sent after DID (accomplished by compiling this into ramstage) |
| 85 | */ |
Sean Rhodes | b5b22a74 | 2021-08-25 21:47:00 +0100 | [diff] [blame] | 86 | |
| 87 | if (cse_is_hfs1_com_soft_temp_disable()) { |
| 88 | printk(BIOS_ERR, "HECI: Prerequisites not met for sending EOP\n"); |
| 89 | if (CONFIG(SOC_INTEL_CSE_LITE_SKU)) |
| 90 | return CSE_EOP_RESULT_ERROR; |
| 91 | return CSE_EOP_RESULT_DISABLED; |
| 92 | } |
| 93 | |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 94 | if (!cse_is_hfs1_cws_normal() || !cse_is_hfs1_com_normal()) { |
| 95 | printk(BIOS_ERR, "HECI: Prerequisites not met for sending EOP\n"); |
| 96 | return CSE_EOP_RESULT_ERROR; |
| 97 | } |
| 98 | |
| 99 | printk(BIOS_INFO, "HECI: Sending End-of-Post\n"); |
| 100 | |
Rizwan Qureshi | 957857d | 2021-08-30 16:43:57 +0530 | [diff] [blame] | 101 | if (!heci_send_receive(&msg, sizeof(msg), &resp, &resp_size, HECI_MKHI_ADDR)) { |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 102 | printk(BIOS_ERR, "HECI: EOP send/receive fail\n"); |
| 103 | return CSE_EOP_RESULT_ERROR; |
| 104 | } |
| 105 | |
| 106 | if (resp.hdr.result) { |
| 107 | printk(BIOS_ERR, "HECI: EOP Resp Failed: %u\n", resp.hdr.result); |
| 108 | return CSE_EOP_RESULT_ERROR; |
| 109 | } |
| 110 | |
| 111 | printk(BIOS_INFO, "CSE: EOP requested action: "); |
| 112 | |
| 113 | switch (resp.requested_actions) { |
| 114 | case EOP_REQUESTED_ACTION_GLOBAL_RESET: |
| 115 | printk(BIOS_INFO, "global reset\n"); |
| 116 | return CSE_EOP_RESULT_GLOBAL_RESET_REQUESTED; |
| 117 | case EOP_REQUESTED_ACTION_CONTINUE: |
| 118 | printk(BIOS_INFO, "continue boot\n"); |
| 119 | return CSE_EOP_RESULT_SUCCESS; |
| 120 | default: |
| 121 | printk(BIOS_INFO, "unknown %u\n", resp.requested_actions); |
| 122 | return CSE_EOP_RESULT_ERROR; |
| 123 | } |
| 124 | } |
| 125 | |
Tim Wawrzynczak | 9fdd2b2 | 2021-06-18 10:34:09 -0600 | [diff] [blame] | 126 | /* |
| 127 | * On EOP error, the BIOS is required to send an MEI bus disable message to the |
| 128 | * CSE, followed by disabling all MEI devices. After successfully completing |
| 129 | * this, it is safe to boot. |
| 130 | */ |
| 131 | static void cse_handle_eop_error(void) |
| 132 | { |
| 133 | if (!cse_disable_mei_bus()) |
| 134 | die("Failed to disable MEI bus while recovering from EOP error\n" |
| 135 | "Preventing system from booting into an insecure state.\n"); |
| 136 | |
| 137 | if (!cse_disable_mei_devices()) |
| 138 | die("Error disabling MEI devices while recovering from EOP error\n" |
| 139 | "Preventing system from booting into an insecure state.\n"); |
| 140 | } |
| 141 | |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 142 | static void handle_cse_eop_result(enum cse_eop_result result) |
| 143 | { |
| 144 | switch (result) { |
| 145 | case CSE_EOP_RESULT_GLOBAL_RESET_REQUESTED: |
| 146 | printk(BIOS_INFO, "CSE requested global reset in EOP response, resetting...\n"); |
| 147 | do_global_reset(); |
| 148 | break; |
| 149 | case CSE_EOP_RESULT_SUCCESS: |
| 150 | printk(BIOS_INFO, "CSE EOP successful, continuing boot\n"); |
| 151 | break; |
Sean Rhodes | b5b22a74 | 2021-08-25 21:47:00 +0100 | [diff] [blame] | 152 | case CSE_EOP_RESULT_DISABLED: |
| 153 | printk(BIOS_INFO, "CSE is disabled, continuing boot\n"); |
| 154 | break; |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 155 | case CSE_EOP_RESULT_ERROR: /* fallthrough */ |
| 156 | default: |
Julius Werner | e966595 | 2022-01-21 17:06:20 -0800 | [diff] [blame] | 157 | printk(BIOS_ERR, "Failed to send EOP to CSE, %d\n", result); |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 158 | /* For vboot, trigger recovery mode if applicable, as there is |
| 159 | likely something very broken in this case. */ |
| 160 | if (CONFIG(VBOOT) && !vboot_recovery_mode_enabled()) |
| 161 | cse_trigger_vboot_recovery(CSE_EOP_FAIL); |
Tim Wawrzynczak | 9fdd2b2 | 2021-06-18 10:34:09 -0600 | [diff] [blame] | 162 | |
| 163 | /* In non-vboot builds or recovery mode, follow the BWG in order |
| 164 | to continue to boot securely. */ |
| 165 | cse_handle_eop_error(); |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 166 | break; |
| 167 | } |
| 168 | } |
| 169 | |
Subrata Banik | 7c31d17 | 2022-02-01 00:11:29 +0530 | [diff] [blame] | 170 | static void do_send_end_of_post(void) |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 171 | { |
Subrata Banik | 7c31d17 | 2022-02-01 00:11:29 +0530 | [diff] [blame] | 172 | static bool eop_sent = false; |
| 173 | |
| 174 | if (eop_sent) { |
| 175 | printk(BIOS_ERR, "EOP already sent\n"); |
| 176 | return; |
| 177 | } |
| 178 | |
MAULIK V VAGHELA | a4af1b5 | 2022-02-21 19:02:19 +0530 | [diff] [blame] | 179 | if (acpi_get_sleep_type() == ACPI_S3) { |
| 180 | printk(BIOS_INFO, "Skip sending EOP during S3 resume\n"); |
| 181 | return; |
| 182 | } |
| 183 | |
Subrata Banik | ac1bba8 | 2021-10-25 11:23:54 +0530 | [diff] [blame] | 184 | /* |
| 185 | * If CSE is already hidden then accessing CSE registers would be wrong and will |
| 186 | * receive junk, hence, return as CSE is already disabled. |
| 187 | */ |
| 188 | if (!is_cse_enabled()) { |
| 189 | printk(BIOS_DEBUG, "CSE is disabled, cannot send End-of-Post (EOP) message\n"); |
| 190 | return; |
| 191 | } |
| 192 | |
Subrata Banik | c6e2552 | 2021-09-30 18:14:09 +0530 | [diff] [blame] | 193 | set_cse_device_state(PCH_DEVFN_CSE, DEV_ACTIVE); |
Subrata Banik | 37231fb | 2021-09-25 15:26:12 +0530 | [diff] [blame] | 194 | |
Tim Wawrzynczak | f004a72 | 2021-06-22 19:15:07 -0600 | [diff] [blame] | 195 | timestamp_add_now(TS_ME_BEFORE_END_OF_POST); |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 196 | handle_cse_eop_result(cse_send_eop()); |
Tim Wawrzynczak | f004a72 | 2021-06-22 19:15:07 -0600 | [diff] [blame] | 197 | timestamp_add_now(TS_ME_AFTER_END_OF_POST); |
Subrata Banik | 37231fb | 2021-09-25 15:26:12 +0530 | [diff] [blame] | 198 | |
Subrata Banik | c6e2552 | 2021-09-30 18:14:09 +0530 | [diff] [blame] | 199 | set_cse_device_state(PCH_DEVFN_CSE, DEV_IDLE); |
Subrata Banik | 7c31d17 | 2022-02-01 00:11:29 +0530 | [diff] [blame] | 200 | |
| 201 | eop_sent = true; |
| 202 | } |
| 203 | |
| 204 | void cse_send_end_of_post(void) |
| 205 | { |
| 206 | return do_send_end_of_post(); |
| 207 | } |
| 208 | |
| 209 | static void set_cse_end_of_post(void *unused) |
| 210 | { |
| 211 | return do_send_end_of_post(); |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 212 | } |
| 213 | |
| 214 | /* |
| 215 | * Ideally, to give coreboot maximum flexibility, sending EOP would be done as |
Subrata Banik | bee4bb5 | 2021-09-21 21:00:16 +0530 | [diff] [blame] | 216 | * late possible. If HECI_DISABLE_USING_SMM is selected, then sending EOP must |
| 217 | * be performed before the HECI bus is disabled, so these boards use |
| 218 | * BS_PAYLOAD_LOAD, which happens before the HECI_DISABLE_USING_SMM Kconfig takes |
| 219 | * effect (EOP is sent using the HECI bus). |
| 220 | * Otherwise, EOP can be pushed a little later, and can be performed in |
| 221 | * BS_PAYLOAD_BOOT instead. |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 222 | */ |
Subrata Banik | bee4bb5 | 2021-09-21 21:00:16 +0530 | [diff] [blame] | 223 | #if !CONFIG(HECI_DISABLE_USING_SMM) |
| 224 | BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_BOOT, BS_ON_ENTRY, set_cse_end_of_post, NULL); |
| 225 | #else |
Tim Wawrzynczak | 064ca18 | 2021-06-17 12:40:13 -0600 | [diff] [blame] | 226 | BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_ENTRY, set_cse_end_of_post, NULL); |
Subrata Banik | bee4bb5 | 2021-09-21 21:00:16 +0530 | [diff] [blame] | 227 | #endif |