/* SPDX-License-Identifier: GPL-2.0-only */ #define __SIMPLE_DEVICE__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_HECI_MESSAGE_RETRY_COUNT 5 /* Wait up to 15 sec for HECI to get ready */ #define HECI_DELAY_READY_MS (15 * 1000) /* Wait up to 100 usec between circular buffer polls */ #define HECI_DELAY_US 100 /* Wait up to 5 sec for CSE to chew something we sent */ #define HECI_SEND_TIMEOUT_MS (5 * 1000) /* Wait up to 5 sec for CSE to blurp a reply */ #define HECI_READ_TIMEOUT_MS (5 * 1000) /* Wait up to 1 ms for CSE CIP */ #define HECI_CIP_TIMEOUT_US 1000 /* Wait up to 5 seconds for CSE to boot from RO(BP1) */ #define CSE_DELAY_BOOT_TO_RO_MS (5 * 1000) #define SLOT_SIZE sizeof(uint32_t) #define MMIO_CSE_CB_WW 0x00 #define MMIO_HOST_CSR 0x04 #define MMIO_CSE_CB_RW 0x08 #define MMIO_CSE_CSR 0x0c #define MMIO_CSE_DEVIDLE 0x800 #define CSE_DEV_IDLE (1 << 2) #define CSE_DEV_CIP (1 << 0) #define CSR_IE (1 << 0) #define CSR_IS (1 << 1) #define CSR_IG (1 << 2) #define CSR_READY (1 << 3) #define CSR_RESET (1 << 4) #define CSR_RP_START 8 #define CSR_RP (((1 << 8) - 1) << CSR_RP_START) #define CSR_WP_START 16 #define CSR_WP (((1 << 8) - 1) << CSR_WP_START) #define CSR_CBD_START 24 #define CSR_CBD (((1 << 8) - 1) << CSR_CBD_START) #define MEI_HDR_IS_COMPLETE (1 << 31) #define MEI_HDR_LENGTH_START 16 #define MEI_HDR_LENGTH_SIZE 9 #define MEI_HDR_LENGTH (((1 << MEI_HDR_LENGTH_SIZE) - 1) \ << MEI_HDR_LENGTH_START) #define MEI_HDR_HOST_ADDR_START 8 #define MEI_HDR_HOST_ADDR (((1 << 8) - 1) << MEI_HDR_HOST_ADDR_START) #define MEI_HDR_CSE_ADDR_START 0 #define MEI_HDR_CSE_ADDR (((1 << 8) - 1) << MEI_HDR_CSE_ADDR_START) /* Get HECI BAR 0 from PCI configuration space */ static uintptr_t get_cse_bar(pci_devfn_t dev) { uintptr_t bar; bar = pci_read_config32(dev, PCI_BASE_ADDRESS_0); assert(bar != 0); /* * Bits 31-12 are the base address as per EDS for SPI, * Don't care about 0-11 bit */ return bar & ~PCI_BASE_ADDRESS_MEM_ATTR_MASK; } /* * Initialize the device with provided temporary BAR. If BAR is 0 use a * default. This is intended for pre-mem usage only where BARs haven't been * assigned yet and devices are not enabled. */ void heci_init(uintptr_t tempbar) { pci_devfn_t dev = PCH_DEV_CSE; u16 pcireg; /* Assume it is already initialized, nothing else to do */ if (get_cse_bar(dev)) return; /* Use default pre-ram bar */ if (!tempbar) tempbar = HECI1_BASE_ADDRESS; /* Assign Resources to HECI1 */ /* Clear BIT 1-2 of Command Register */ pcireg = pci_read_config16(dev, PCI_COMMAND); pcireg &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); pci_write_config16(dev, PCI_COMMAND, pcireg); /* Program Temporary BAR for HECI1 */ pci_write_config32(dev, PCI_BASE_ADDRESS_0, tempbar); pci_write_config32(dev, PCI_BASE_ADDRESS_1, 0x0); /* Enable Bus Master and MMIO Space */ pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); /* Trigger HECI Reset and make Host ready for communication with CSE */ heci_reset(); } static uint32_t read_bar(pci_devfn_t dev, uint32_t offset) { return read32p(get_cse_bar(dev) + offset); } static void write_bar(pci_devfn_t dev, uint32_t offset, uint32_t val) { return write32p(get_cse_bar(dev) + offset, val); } static uint32_t read_cse_csr(void) { return read_bar(PCH_DEV_CSE, MMIO_CSE_CSR); } static uint32_t read_host_csr(void) { return read_bar(PCH_DEV_CSE, MMIO_HOST_CSR); } static void write_host_csr(uint32_t data) { write_bar(PCH_DEV_CSE, MMIO_HOST_CSR, data); } static size_t filled_slots(uint32_t data) { uint8_t wp, rp; rp = data >> CSR_RP_START; wp = data >> CSR_WP_START; return (uint8_t) (wp - rp); } static size_t cse_filled_slots(void) { return filled_slots(read_cse_csr()); } static size_t host_empty_slots(void) { uint32_t csr; csr = read_host_csr(); return ((csr & CSR_CBD) >> CSR_CBD_START) - filled_slots(csr); } static void clear_int(void) { uint32_t csr; csr = read_host_csr(); csr |= CSR_IS; write_host_csr(csr); } static uint32_t read_slot(void) { return read_bar(PCH_DEV_CSE, MMIO_CSE_CB_RW); } static void write_slot(uint32_t val) { write_bar(PCH_DEV_CSE, MMIO_CSE_CB_WW, val); } static int wait_write_slots(size_t cnt) { struct stopwatch sw; stopwatch_init_msecs_expire(&sw, HECI_SEND_TIMEOUT_MS); while (host_empty_slots() < cnt) { udelay(HECI_DELAY_US); if (stopwatch_expired(&sw)) { printk(BIOS_ERR, "HECI: timeout, buffer not drained\n"); return 0; } } return 1; } static int wait_read_slots(size_t cnt) { struct stopwatch sw; stopwatch_init_msecs_expire(&sw, HECI_READ_TIMEOUT_MS); while (cse_filled_slots() < cnt) { udelay(HECI_DELAY_US); if (stopwatch_expired(&sw)) { printk(BIOS_ERR, "HECI: timed out reading answer!\n"); return 0; } } return 1; } /* get number of full 4-byte slots */ static size_t bytes_to_slots(size_t bytes) { return ALIGN_UP(bytes, SLOT_SIZE) / SLOT_SIZE; } static int cse_ready(void) { uint32_t csr; csr = read_cse_csr(); return csr & CSR_READY; } static bool cse_check_hfs1_com(int mode) { union me_hfsts1 hfs1; hfs1.data = me_read_config32(PCI_ME_HFSTS1); return hfs1.fields.operation_mode == mode; } bool cse_is_hfs1_cws_normal(void) { union me_hfsts1 hfs1; hfs1.data = me_read_config32(PCI_ME_HFSTS1); if (hfs1.fields.working_state == ME_HFS1_CWS_NORMAL) return true; return false; } bool cse_is_hfs1_com_normal(void) { return cse_check_hfs1_com(ME_HFS1_COM_NORMAL); } bool cse_is_hfs1_com_secover_mei_msg(void) { return cse_check_hfs1_com(ME_HFS1_COM_SECOVER_MEI_MSG); } bool cse_is_hfs1_com_soft_temp_disable(void) { return cse_check_hfs1_com(ME_HFS1_COM_SOFT_TEMP_DISABLE); } bool cse_is_hfs3_fw_sku_lite(void) { union me_hfsts3 hfs3; hfs3.data = me_read_config32(PCI_ME_HFSTS3); return hfs3.fields.fw_sku == ME_HFS3_FW_SKU_LITE; } /* Makes the host ready to communicate with CSE */ void cse_set_host_ready(void) { uint32_t csr; csr = read_host_csr(); csr &= ~CSR_RESET; csr |= (CSR_IG | CSR_READY); write_host_csr(csr); } /* Polls for ME mode ME_HFS1_COM_SECOVER_MEI_MSG for 15 seconds */ uint8_t cse_wait_sec_override_mode(void) { struct stopwatch sw; stopwatch_init_msecs_expire(&sw, HECI_DELAY_READY_MS); while (!cse_is_hfs1_com_secover_mei_msg()) { udelay(HECI_DELAY_US); if (stopwatch_expired(&sw)) { printk(BIOS_ERR, "HECI: Timed out waiting for SEC_OVERRIDE mode!\n"); return 0; } } printk(BIOS_DEBUG, "HECI: CSE took %lu ms to enter security override mode\n", stopwatch_duration_msecs(&sw)); return 1; } /* * Polls for CSE's current operation mode 'Soft Temporary Disable'. * The CSE enters the current operation mode when it boots from RO(BP1). */ uint8_t cse_wait_com_soft_temp_disable(void) { struct stopwatch sw; stopwatch_init_msecs_expire(&sw, CSE_DELAY_BOOT_TO_RO_MS); while (!cse_is_hfs1_com_soft_temp_disable()) { udelay(HECI_DELAY_US); if (stopwatch_expired(&sw)) { printk(BIOS_ERR, "HECI: Timed out waiting for CSE to boot from RO!\n"); return 0; } } printk(BIOS_SPEW, "HECI: CSE took %lu ms to boot from RO\n", stopwatch_duration_msecs(&sw)); return 1; } static int wait_heci_ready(void) { struct stopwatch sw; stopwatch_init_msecs_expire(&sw, HECI_DELAY_READY_MS); while (!cse_ready()) { udelay(HECI_DELAY_US); if (stopwatch_expired(&sw)) return 0; } return 1; } static void host_gen_interrupt(void) { uint32_t csr; csr = read_host_csr(); csr |= CSR_IG; write_host_csr(csr); } static size_t hdr_get_length(uint32_t hdr) { return (hdr & MEI_HDR_LENGTH) >> MEI_HDR_LENGTH_START; } static int send_one_message(uint32_t hdr, const void *buff) { size_t pend_len, pend_slots, remainder, i; uint32_t tmp; const uint32_t *p = buff; /* Get space for the header */ if (!wait_write_slots(1)) return 0; /* First, write header */ write_slot(hdr); pend_len = hdr_get_length(hdr); pend_slots = bytes_to_slots(pend_len); if (!wait_write_slots(pend_slots)) return 0; /* Write the body in whole slots */ i = 0; while (i < ALIGN_DOWN(pend_len, SLOT_SIZE)) { write_slot(*p++); i += SLOT_SIZE; } remainder = pend_len % SLOT_SIZE; /* Pad to 4 bytes not touching caller's buffer */ if (remainder) { memcpy(&tmp, p, remainder); write_slot(tmp); } host_gen_interrupt(); /* Make sure nothing bad happened during transmission */ if (!cse_ready()) return 0; return pend_len; } /* * Send message msg of size len to host from host_addr to cse_addr. * Returns 1 on success and 0 otherwise. * In case of error heci_reset() may be required. */ static int heci_send(const void *msg, size_t len, uint8_t host_addr, uint8_t client_addr) { uint8_t retry; uint32_t csr, hdr; size_t sent, remaining, cb_size, max_length; const uint8_t *p; if (!msg || !len) return 0; clear_int(); for (retry = 0; retry < MAX_HECI_MESSAGE_RETRY_COUNT; retry++) { p = msg; if (!wait_heci_ready()) { printk(BIOS_ERR, "HECI: not ready\n"); continue; } csr = read_host_csr(); cb_size = ((csr & CSR_CBD) >> CSR_CBD_START) * SLOT_SIZE; /* * Reserve one slot for the header. Limit max message * length by 9 bits that are available in the header. */ max_length = MIN(cb_size, (1 << MEI_HDR_LENGTH_SIZE) - 1) - SLOT_SIZE; remaining = len; /* * Fragment the message into smaller messages not exceeding * useful circular buffer length. Mark last message complete. */ do { hdr = MIN(max_length, remaining) << MEI_HDR_LENGTH_START; hdr |= client_addr << MEI_HDR_CSE_ADDR_START; hdr |= host_addr << MEI_HDR_HOST_ADDR_START; hdr |= (MIN(max_length, remaining) == remaining) ? MEI_HDR_IS_COMPLETE : 0; sent = send_one_message(hdr, p); p += sent; remaining -= sent; } while (remaining > 0 && sent != 0); if (!remaining) return 1; } return 0; } static size_t recv_one_message(uint32_t *hdr, void *buff, size_t maxlen) { uint32_t reg, *p = buff; size_t recv_slots, recv_len, remainder, i; /* first get the header */ if (!wait_read_slots(1)) return 0; *hdr = read_slot(); recv_len = hdr_get_length(*hdr); if (!recv_len) printk(BIOS_WARNING, "HECI: message is zero-sized\n"); recv_slots = bytes_to_slots(recv_len); i = 0; if (recv_len > maxlen) { printk(BIOS_ERR, "HECI: response is too big\n"); return 0; } /* wait for the rest of messages to arrive */ wait_read_slots(recv_slots); /* fetch whole slots first */ while (i < ALIGN_DOWN(recv_len, SLOT_SIZE)) { *p++ = read_slot(); i += SLOT_SIZE; } /* * If ME is not ready, something went wrong and * we received junk */ if (!cse_ready()) return 0; remainder = recv_len % SLOT_SIZE; if (remainder) { reg = read_slot(); memcpy(p, ®, remainder); } return recv_len; } /* * Receive message into buff not exceeding maxlen. Message is considered * successfully received if a 'complete' indication is read from ME side * and there was enough space in the buffer to fit that message. maxlen * is updated with size of message that was received. Returns 0 on failure * and 1 on success. * In case of error heci_reset() may be required. */ static int heci_receive(void *buff, size_t *maxlen) { uint8_t retry; size_t left, received; uint32_t hdr = 0; uint8_t *p; if (!buff || !maxlen || !*maxlen) return 0; clear_int(); for (retry = 0; retry < MAX_HECI_MESSAGE_RETRY_COUNT; retry++) { p = buff; left = *maxlen; if (!wait_heci_ready()) { printk(BIOS_ERR, "HECI: not ready\n"); continue; } /* * Receive multiple packets until we meet one marked * complete or we run out of space in caller-provided buffer. */ do { received = recv_one_message(&hdr, p, left); if (!received) { printk(BIOS_ERR, "HECI: Failed to receive!\n"); return 0; } left -= received; p += received; /* If we read out everything ping to send more */ if (!(hdr & MEI_HDR_IS_COMPLETE) && !cse_filled_slots()) host_gen_interrupt(); } while (received && !(hdr & MEI_HDR_IS_COMPLETE) && left > 0); if ((hdr & MEI_HDR_IS_COMPLETE) && received) { *maxlen = p - (uint8_t *) buff; return 1; } } return 0; } int heci_send_receive(const void *snd_msg, size_t snd_sz, void *rcv_msg, size_t *rcv_sz, uint8_t cse_addr) { if (!heci_send(snd_msg, snd_sz, BIOS_HOST_ADDR, cse_addr)) { printk(BIOS_ERR, "HECI: send Failed\n"); return 0; } if (rcv_msg != NULL) { if (!heci_receive(rcv_msg, rcv_sz)) { printk(BIOS_ERR, "HECI: receive Failed\n"); return 0; } } return 1; } /* * Attempt to reset the device. This is useful when host and ME are out * of sync during transmission or ME didn't understand the message. */ int heci_reset(void) { uint32_t csr; /* Clear post code to prevent eventlog entry from unknown code. */ post_code(0); /* Send reset request */ csr = read_host_csr(); csr |= (CSR_RESET | CSR_IG); write_host_csr(csr); if (wait_heci_ready()) { /* Device is back on its imaginary feet, clear reset */ cse_set_host_ready(); return 1; } printk(BIOS_CRIT, "HECI: reset failed\n"); return 0; } bool is_cse_devfn_visible(unsigned int devfn) { int slot = PCI_SLOT(devfn); int func = PCI_FUNC(devfn); if (!is_devfn_enabled(devfn)) { printk(BIOS_WARNING, "HECI: CSE device %02x.%01x is disabled\n", slot, func); return false; } if (pci_read_config16(PCI_DEV(0, slot, func), PCI_VENDOR_ID) == 0xFFFF) { printk(BIOS_WARNING, "HECI: CSE device %02x.%01x is hidden\n", slot, func); return false; } return true; } bool is_cse_enabled(void) { return is_cse_devfn_visible(PCH_DEVFN_CSE); } uint32_t me_read_config32(int offset) { return pci_read_config32(PCH_DEV_CSE, offset); } static bool cse_is_global_reset_allowed(void) { /* * Allow sending GLOBAL_RESET command only if: * - CSE's current working state is Normal and current operation mode is Normal. * - (or) CSE's current working state is normal and current operation mode can * be Soft Temp Disable or Security Override Mode if CSE's Firmware SKU is * Lite. */ if (!cse_is_hfs1_cws_normal()) return false; if (cse_is_hfs1_com_normal()) return true; if (cse_is_hfs3_fw_sku_lite()) { if (cse_is_hfs1_com_soft_temp_disable() || cse_is_hfs1_com_secover_mei_msg()) return true; } return false; } /* * Sends GLOBAL_RESET_REQ cmd to CSE with reset type GLOBAL_RESET. * Returns 0 on failure and 1 on success. */ static int cse_request_reset(enum rst_req_type rst_type) { int status; struct mkhi_hdr reply; struct reset_message { struct mkhi_hdr hdr; uint8_t req_origin; uint8_t reset_type; } __packed; struct reset_message msg = { .hdr = { .group_id = MKHI_GROUP_ID_CBM, .command = MKHI_CBM_GLOBAL_RESET_REQ, }, .req_origin = GR_ORIGIN_BIOS_POST, .reset_type = rst_type }; size_t reply_size; printk(BIOS_DEBUG, "HECI: Global Reset(Type:%d) Command\n", rst_type); if (!(rst_type == GLOBAL_RESET || rst_type == CSE_RESET_ONLY)) { printk(BIOS_ERR, "HECI: Unsupported reset type is requested\n"); return 0; } if (!cse_is_global_reset_allowed() || !is_cse_enabled()) { printk(BIOS_ERR, "HECI: CSE does not meet required prerequisites\n"); return 0; } heci_reset(); reply_size = sizeof(reply); memset(&reply, 0, reply_size); if (rst_type == CSE_RESET_ONLY) status = heci_send(&msg, sizeof(msg), BIOS_HOST_ADDR, HECI_MKHI_ADDR); else status = heci_send_receive(&msg, sizeof(msg), &reply, &reply_size, HECI_MKHI_ADDR); printk(BIOS_DEBUG, "HECI: Global Reset %s!\n", status ? "success" : "failure"); return status; } int cse_request_global_reset(void) { return cse_request_reset(GLOBAL_RESET); } static bool cse_is_hmrfpo_enable_allowed(void) { /* * Allow sending HMRFPO ENABLE command only if: * - CSE's current working state is Normal and current operation mode is Normal * - (or) cse's current working state is normal and current operation mode is * Soft Temp Disable if CSE's Firmware SKU is Lite */ if (!cse_is_hfs1_cws_normal()) return false; if (cse_is_hfs1_com_normal()) return true; if (cse_is_hfs3_fw_sku_lite() && cse_is_hfs1_com_soft_temp_disable()) return true; return false; } /* Sends HMRFPO Enable command to CSE */ int cse_hmrfpo_enable(void) { struct hmrfpo_enable_msg { struct mkhi_hdr hdr; uint32_t nonce[2]; } __packed; /* HMRFPO Enable message */ struct hmrfpo_enable_msg msg = { .hdr = { .group_id = MKHI_GROUP_ID_HMRFPO, .command = MKHI_HMRFPO_ENABLE, }, .nonce = {0}, }; /* HMRFPO Enable response */ struct hmrfpo_enable_resp { struct mkhi_hdr hdr; /* Base addr for factory data area, not relevant for client SKUs */ uint32_t fct_base; /* Length of factory data area, not relevant for client SKUs */ uint32_t fct_limit; uint8_t status; uint8_t reserved[3]; } __packed; struct hmrfpo_enable_resp resp; size_t resp_size = sizeof(struct hmrfpo_enable_resp); if (cse_is_hfs1_com_secover_mei_msg()) { printk(BIOS_DEBUG, "HECI: CSE is already in security override mode, " "skip sending HMRFPO_ENABLE command to CSE\n"); return 1; } printk(BIOS_DEBUG, "HECI: Send HMRFPO Enable Command\n"); if (!cse_is_hmrfpo_enable_allowed()) { printk(BIOS_ERR, "HECI: CSE does not meet required prerequisites\n"); return 0; } if (!heci_send_receive(&msg, sizeof(struct hmrfpo_enable_msg), &resp, &resp_size, HECI_MKHI_ADDR)) return 0; if (resp.hdr.result) { printk(BIOS_ERR, "HECI: Resp Failed:%d\n", resp.hdr.result); return 0; } if (resp.status) { printk(BIOS_ERR, "HECI: HMRFPO_Enable Failed (resp status: %d)\n", resp.status); return 0; } return 1; } /* * Sends HMRFPO Get Status command to CSE to get the HMRFPO status. * The status can be DISABLED/LOCKED/ENABLED */ int cse_hmrfpo_get_status(void) { struct hmrfpo_get_status_msg { struct mkhi_hdr hdr; } __packed; struct hmrfpo_get_status_resp { struct mkhi_hdr hdr; uint8_t status; uint8_t reserved[3]; } __packed; struct hmrfpo_get_status_msg msg = { .hdr = { .group_id = MKHI_GROUP_ID_HMRFPO, .command = MKHI_HMRFPO_GET_STATUS, }, }; struct hmrfpo_get_status_resp resp; size_t resp_size = sizeof(struct hmrfpo_get_status_resp); printk(BIOS_INFO, "HECI: Sending Get HMRFPO Status Command\n"); if (!cse_is_hfs1_cws_normal()) { printk(BIOS_ERR, "HECI: CSE's current working state is not Normal\n"); return -1; } if (!heci_send_receive(&msg, sizeof(struct hmrfpo_get_status_msg), &resp, &resp_size, HECI_MKHI_ADDR)) { printk(BIOS_ERR, "HECI: HMRFPO send/receive fail\n"); return -1; } if (resp.hdr.result) { printk(BIOS_ERR, "HECI: HMRFPO Resp Failed:%d\n", resp.hdr.result); return -1; } return resp.status; } void print_me_fw_version(void *unused) { struct me_fw_ver_resp resp = {0}; /* Ignore if UART debugging is disabled */ if (!CONFIG(CONSOLE_SERIAL)) return; if (get_me_fw_version(&resp) == CB_SUCCESS) { printk(BIOS_DEBUG, "ME: Version: %d.%d.%d.%d\n", resp.code.major, resp.code.minor, resp.code.hotfix, resp.code.build); return; } printk(BIOS_DEBUG, "ME: Version: Unavailable\n"); } enum cb_err get_me_fw_version(struct me_fw_ver_resp *resp) { const struct mkhi_hdr fw_ver_msg = { .group_id = MKHI_GROUP_ID_GEN, .command = MKHI_GEN_GET_FW_VERSION, }; if (resp == NULL) { printk(BIOS_ERR, "%s failed, null pointer parameter\n", __func__); return CB_ERR; } size_t resp_size = sizeof(*resp); /* Ignore if CSE is disabled */ if (!is_cse_enabled()) return CB_ERR; /* * Ignore if ME Firmware SKU type is Lite since * print_boot_partition_info() logs RO(BP1) and RW(BP2) versions. */ if (cse_is_hfs3_fw_sku_lite()) return CB_ERR; /* * Prerequisites: * 1) HFSTS1 Current Working State is Normal * 2) HFSTS1 Current Operation Mode is Normal * 3) It's after DRAM INIT DONE message (taken care of by calling it * during ramstage */ if (!cse_is_hfs1_cws_normal() || !cse_is_hfs1_com_normal()) return CB_ERR; heci_reset(); if (!heci_send_receive(&fw_ver_msg, sizeof(fw_ver_msg), resp, &resp_size, HECI_MKHI_ADDR)) return CB_ERR; if (resp->hdr.result) return CB_ERR; return CB_SUCCESS; } void cse_trigger_vboot_recovery(enum csme_failure_reason reason) { printk(BIOS_DEBUG, "cse: CSE status registers: HFSTS1: 0x%x, HFSTS2: 0x%x " "HFSTS3: 0x%x\n", me_read_config32(PCI_ME_HFSTS1), me_read_config32(PCI_ME_HFSTS2), me_read_config32(PCI_ME_HFSTS3)); if (CONFIG(VBOOT)) { struct vb2_context *ctx = vboot_get_context(); if (ctx == NULL) goto failure; vb2api_fail(ctx, VB2_RECOVERY_INTEL_CSE_LITE_SKU, reason); vboot_save_data(ctx); vboot_reboot(); } failure: die("cse: Failed to trigger recovery mode(recovery subcode:%d)\n", reason); } static bool disable_cse_idle(pci_devfn_t dev) { struct stopwatch sw; uint32_t dev_idle_ctrl = read_bar(dev, MMIO_CSE_DEVIDLE); dev_idle_ctrl &= ~CSE_DEV_IDLE; write_bar(dev, MMIO_CSE_DEVIDLE, dev_idle_ctrl); stopwatch_init_usecs_expire(&sw, HECI_CIP_TIMEOUT_US); do { dev_idle_ctrl = read_bar(dev, MMIO_CSE_DEVIDLE); if ((dev_idle_ctrl & CSE_DEV_CIP) == CSE_DEV_CIP) return true; udelay(HECI_DELAY_US); } while (!stopwatch_expired(&sw)); return false; } static void enable_cse_idle(pci_devfn_t dev) { uint32_t dev_idle_ctrl = read_bar(dev, MMIO_CSE_DEVIDLE); dev_idle_ctrl |= CSE_DEV_IDLE; write_bar(dev, MMIO_CSE_DEVIDLE, dev_idle_ctrl); } enum cse_device_state get_cse_device_state(unsigned int devfn) { pci_devfn_t dev = PCI_DEV(0, PCI_SLOT(devfn), PCI_FUNC(devfn)); uint32_t dev_idle_ctrl = read_bar(dev, MMIO_CSE_DEVIDLE); if ((dev_idle_ctrl & CSE_DEV_IDLE) == CSE_DEV_IDLE) return DEV_IDLE; return DEV_ACTIVE; } static enum cse_device_state ensure_cse_active(pci_devfn_t dev) { if (!disable_cse_idle(dev)) return DEV_IDLE; pci_or_config32(dev, PCI_COMMAND, PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); return DEV_ACTIVE; } static void ensure_cse_idle(pci_devfn_t dev) { enable_cse_idle(dev); pci_and_config32(dev, PCI_COMMAND, ~(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER)); } bool set_cse_device_state(unsigned int devfn, enum cse_device_state requested_state) { enum cse_device_state current_state = get_cse_device_state(devfn); pci_devfn_t dev = PCI_DEV(0, PCI_SLOT(devfn), PCI_FUNC(devfn)); if (current_state == requested_state) return true; if (requested_state == DEV_ACTIVE) return ensure_cse_active(dev) == requested_state; else ensure_cse_idle(dev); return true; } #if ENV_RAMSTAGE /* * Disable the Intel (CS)Management Engine via HECI based on a cmos value * of `me_state`. A value of `0` will result in a (CS)ME state of `0` (working) * and value of `1` will result in a (CS)ME state of `3` (disabled). * * It isn't advised to use this in combination with me_cleaner. * * It is advisable to have a second cmos option called `me_state_counter`. * Whilst not essential, it avoid reboots loops if the (CS)ME fails to * change states after 3 attempts. Some versions of the (CS)ME need to be * reset 3 times. * * Ideal cmos values would be: * * # coreboot config options: cpu * 432 1 e 5 me_state * 440 4 h 0 me_state_counter * * #ID value text * 5 0 Enable * 5 1 Disable */ static void me_reset_with_count(void) { unsigned int cmos_me_state_counter = get_uint_option("me_state_counter", UINT_MAX); if (cmos_me_state_counter != UINT_MAX) { printk(BIOS_DEBUG, "CMOS: me_state_counter = %u\n", cmos_me_state_counter); /* Avoid boot loops by only trying a state change 3 times */ if (cmos_me_state_counter < ME_DISABLE_ATTEMPTS) { cmos_me_state_counter++; set_uint_option("me_state_counter", cmos_me_state_counter); printk(BIOS_DEBUG, "ME: Reset attempt %u/%u.\n", cmos_me_state_counter, ME_DISABLE_ATTEMPTS); do_global_reset(); } else { /* * If the (CS)ME fails to change states after 3 attempts, it will * likely need a cold boot, or recovering. */ printk(BIOS_ERR, "Error: Failed to change ME state in %u attempts!\n", ME_DISABLE_ATTEMPTS); } } else { printk(BIOS_DEBUG, "ME: Resetting"); do_global_reset(); } } static void cse_set_state(struct device *dev) { /* (CS)ME Disable Command */ struct me_disable_command { struct mkhi_hdr hdr; uint32_t rule_id; uint8_t rule_len; uint32_t rule_data; } __packed me_disable = { .hdr = { .group_id = MKHI_GROUP_ID_FWCAPS, .command = MKHI_SET_ME_DISABLE, }, .rule_id = ME_DISABLE_RULE_ID, .rule_len = ME_DISABLE_RULE_LENGTH, .rule_data = ME_DISABLE_COMMAND, }; struct me_disable_reply { struct mkhi_hdr hdr; uint32_t rule_id; } __packed; struct me_disable_reply disable_reply; size_t disable_reply_size; /* (CS)ME Enable Command */ struct me_enable_command { struct mkhi_hdr hdr; } me_enable = { .hdr = { .group_id = MKHI_GROUP_ID_BUP_COMMON, .command = MKHI_SET_ME_ENABLE, }, }; struct me_enable_reply { struct mkhi_hdr hdr; } __packed; struct me_enable_reply enable_reply; size_t enable_reply_size; /* Function Start */ int send; int result; /* * Check if the CMOS value "me_state" exists, if it doesn't, then * don't do anything. */ const unsigned int cmos_me_state = get_uint_option("me_state", UINT_MAX); if (cmos_me_state == UINT_MAX) return; printk(BIOS_DEBUG, "CMOS: me_state = %u\n", cmos_me_state); /* * We only take action if the me_state doesn't match the CS(ME) working state */ const unsigned int soft_temp_disable = cse_is_hfs1_com_soft_temp_disable(); if (cmos_me_state && !soft_temp_disable) { /* me_state should be disabled, but it's enabled */ printk(BIOS_DEBUG, "ME needs to be disabled.\n"); send = heci_send_receive(&me_disable, sizeof(me_disable), &disable_reply, &disable_reply_size, HECI_MKHI_ADDR); result = disable_reply.hdr.result; } else if (!cmos_me_state && soft_temp_disable) { /* me_state should be enabled, but it's disabled */ printk(BIOS_DEBUG, "ME needs to be enabled.\n"); send = heci_send_receive(&me_enable, sizeof(me_enable), &enable_reply, &enable_reply_size, HECI_MKHI_ADDR); result = enable_reply.hdr.result; } else { printk(BIOS_DEBUG, "ME is %s.\n", cmos_me_state ? "disabled" : "enabled"); unsigned int cmos_me_state_counter = get_uint_option("me_state_counter", UINT_MAX); /* set me_state_counter to 0 */ if ((cmos_me_state_counter != UINT_MAX && cmos_me_state_counter != 0)) set_uint_option("me_state_counter", 0); return; } printk(BIOS_DEBUG, "HECI: ME state change send %s!\n", send ? "success" : "failure"); printk(BIOS_DEBUG, "HECI: ME state change result %s!\n", result ? "success" : "failure"); /* * Reset if the result was successful, or if the send failed as some older * version of the Intel (CS)ME won't successfully receive the message unless reset * twice. */ if (send || !result) me_reset_with_count(); } static struct device_operations cse_ops = { .set_resources = pci_dev_set_resources, .read_resources = pci_dev_read_resources, .enable_resources = pci_dev_enable_resources, .init = pci_dev_init, .ops_pci = &pci_dev_ops_pci, .enable = cse_set_state, }; static const unsigned short pci_device_ids[] = { PCI_DEVICE_ID_INTEL_APL_CSE0, PCI_DEVICE_ID_INTEL_GLK_CSE0, PCI_DEVICE_ID_INTEL_CNL_CSE0, PCI_DEVICE_ID_INTEL_SKL_CSE0, PCI_DEVICE_ID_INTEL_LWB_CSE0, PCI_DEVICE_ID_INTEL_LWB_CSE0_SUPER, PCI_DEVICE_ID_INTEL_CNP_H_CSE0, PCI_DEVICE_ID_INTEL_ICL_CSE0, PCI_DEVICE_ID_INTEL_CMP_CSE0, PCI_DEVICE_ID_INTEL_CMP_H_CSE0, PCI_DEVICE_ID_INTEL_TGL_CSE0, PCI_DEVICE_ID_INTEL_TGL_H_CSE0, PCI_DEVICE_ID_INTEL_MCC_CSE0, PCI_DEVICE_ID_INTEL_MCC_CSE1, PCI_DEVICE_ID_INTEL_MCC_CSE2, PCI_DEVICE_ID_INTEL_MCC_CSE3, PCI_DEVICE_ID_INTEL_JSP_CSE0, PCI_DEVICE_ID_INTEL_JSP_CSE1, PCI_DEVICE_ID_INTEL_JSP_CSE2, PCI_DEVICE_ID_INTEL_JSP_CSE3, PCI_DEVICE_ID_INTEL_ADP_P_CSE0, PCI_DEVICE_ID_INTEL_ADP_P_CSE1, PCI_DEVICE_ID_INTEL_ADP_P_CSE2, PCI_DEVICE_ID_INTEL_ADP_P_CSE3, PCI_DEVICE_ID_INTEL_ADP_S_CSE0, PCI_DEVICE_ID_INTEL_ADP_S_CSE1, PCI_DEVICE_ID_INTEL_ADP_S_CSE2, PCI_DEVICE_ID_INTEL_ADP_S_CSE3, PCI_DEVICE_ID_INTEL_ADP_M_CSE0, PCI_DEVICE_ID_INTEL_ADP_M_CSE1, PCI_DEVICE_ID_INTEL_ADP_M_CSE2, PCI_DEVICE_ID_INTEL_ADP_M_CSE3, 0, }; static const struct pci_driver cse_driver __pci_driver = { .ops = &cse_ops, .vendor = PCI_VENDOR_ID_INTEL, /* SoC/chipset needs to provide PCI device ID */ .devices = pci_device_ids }; #endif