aboutsummaryrefslogtreecommitdiff
path: root/src/soc/intel/skylake/me.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc/intel/skylake/me.c')
-rw-r--r--src/soc/intel/skylake/me.c683
1 files changed, 683 insertions, 0 deletions
diff --git a/src/soc/intel/skylake/me.c b/src/soc/intel/skylake/me.c
new file mode 100644
index 0000000000..2f94123374
--- /dev/null
+++ b/src/soc/intel/skylake/me.c
@@ -0,0 +1,683 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2016 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <arch/io.h>
+#include <commonlib/helpers.h>
+#include <console/console.h>
+#include <device/pci.h>
+#include <device/pci_def.h>
+#include <device/pci_ids.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <soc/iomap.h>
+#include <soc/pci_devs.h>
+#include <soc/me.h>
+#include <delay.h>
+#include <timer.h>
+
+static inline u32 me_read_config32(int offset)
+{
+ return pci_read_config32(PCH_DEV_ME, offset);
+}
+
+static inline void me_write_config32(int offset, u32 value)
+{
+ pci_write_config32(PCH_DEV_ME, offset, value);
+}
+
+static inline u32 me_read_mmio32(int offset)
+{
+ return read32((void *)(HECI1_BASE_ADDRESS + offset));
+}
+
+static inline void me_write_mmio32(u16 offset, u32 value)
+{
+ write32((void *)(HECI1_BASE_ADDRESS + offset), value);
+}
+
+/* HFSTS1[3:0] Current Working State Values */
+static const char *me_cws_values[] = {
+ [ME_HFS_CWS_RESET] = "Reset",
+ [ME_HFS_CWS_INIT] = "Initializing",
+ [ME_HFS_CWS_REC] = "Recovery",
+ [3] = "Unknown (3)",
+ [4] = "Unknown (4)",
+ [ME_HFS_CWS_NORMAL] = "Normal",
+ [ME_HFS_CWS_WAIT] = "Platform Disable Wait",
+ [ME_HFS_CWS_TRANS] = "OP State Transition",
+ [ME_HFS_CWS_INVALID] = "Invalid CPU Plugged In",
+ [9] = "Unknown (9)",
+ [10] = "Unknown (10)",
+ [11] = "Unknown (11)",
+ [12] = "Unknown (12)",
+ [13] = "Unknown (13)",
+ [14] = "Unknown (14)",
+ [15] = "Unknown (15)",
+};
+
+/* HFSTS1[8:6] Current Operation State Values */
+static const char *me_opstate_values[] = {
+ [ME_HFS_STATE_PREBOOT] = "Preboot",
+ [ME_HFS_STATE_M0_UMA] = "M0 with UMA",
+ [ME_HFS_STATE_M3] = "M3 without UMA",
+ [ME_HFS_STATE_M0] = "M0 without UMA",
+ [ME_HFS_STATE_BRINGUP] = "Bring up",
+ [ME_HFS_STATE_ERROR] = "M0 without UMA but with error"
+};
+
+/* HFSTS1[19:16] Current Operation Mode Values */
+static const char *me_opmode_values[] = {
+ [ME_HFS_MODE_NORMAL] = "Normal",
+ [ME_HFS_MODE_DEBUG] = "Debug",
+ [ME_HFS_MODE_DIS] = "Soft Temporary Disable",
+ [ME_HFS_MODE_OVER_JMPR] = "Security Override via Jumper",
+ [ME_HFS_MODE_OVER_MEI] = "Security Override via MEI Message"
+};
+
+/* HFSTS1[15:12] Error Code Values */
+static const char *me_error_values[] = {
+ [ME_HFS_ERROR_NONE] = "No Error",
+ [ME_HFS_ERROR_UNCAT] = "Uncategorized Failure",
+ [ME_HFS_ERROR_IMAGE] = "Image Failure",
+ [ME_HFS_ERROR_DEBUG] = "Debug Failure"
+};
+
+/* HFSTS2[31:28] ME Progress Code */
+static const char *me_progress_values[] = {
+ [ME_HFS2_PHASE_ROM] = "ROM Phase",
+ [1] = "Unknown (1)",
+ [ME_HFS2_PHASE_UKERNEL] = "uKernel Phase",
+ [ME_HFS2_PHASE_BUP] = "BUP Phase",
+ [4] = "Unknown (4)",
+ [5] = "Unknown (5)",
+ [ME_HFS2_PHASE_HOST_COMM] = "Host Communication",
+ [7] = "Unknown (7)",
+ [8] = "Unknown (8)"
+};
+
+/* HFSTS2[27:24] Power Management Event */
+static const char *me_pmevent_values[] = {
+ [ME_HFS2_PMEVENT_CLEAN_MOFF_MX_WAKE] =
+ "Clean Moff->Mx wake",
+ [ME_HFS2_PMEVENT_MOFF_MX_WAKE_ERROR] =
+ "Moff->Mx wake after an error",
+ [ME_HFS2_PMEVENT_CLEAN_GLOBAL_RESET] =
+ "Clean global reset",
+ [ME_HFS2_PMEVENT_CLEAN_GLOBAL_RESET_ERROR] =
+ "Global reset after an error",
+ [ME_HFS2_PMEVENT_CLEAN_ME_RESET] =
+ "Clean Intel ME reset",
+ [ME_HFS2_PMEVENT_ME_RESET_EXCEPTION] =
+ "Intel ME reset due to exception",
+ [ME_HFS2_PMEVENT_PSEUDO_ME_RESET] =
+ "Pseudo-global reset",
+ [ME_HFS2_PMEVENT_CM0_CM3] =
+ "CM0->CM3",
+ [ME_HFS2_PMEVENT_CM3_CM0] =
+ "CM3->CM0",
+ [ME_HFS2_PMEVENT_NON_PWR_CYCLE_RESET] =
+ "Non-power cycle reset",
+ [ME_HFS2_PMEVENT_PWR_CYCLE_RESET_M3] =
+ "Power cycle reset through M3",
+ [ME_HFS2_PMEVENT_PWR_CYCLE_RESET_MOFF] =
+ "Power cycle reset through Moff",
+ [ME_HFS2_PMEVENT_CMX_CMOFF] =
+ "Cx/Mx->Cx/Moff",
+ [ME_HFS2_PMEVENT_CM0_CM0PG] =
+ "CM0->CM0PG",
+ [ME_HFS2_PMEVENT_CM3_CM3PG] =
+ "CM3->CM3PG",
+ [ME_HFS2_PMEVENT_CM0PG_CM0] =
+ "CM0PG->CM0"
+
+};
+
+/* Progress Code 0 states */
+static const char *me_progress_rom_values[] = {
+ [ME_HFS2_STATE_ROM_BEGIN] = "BEGIN",
+ [ME_HFS2_STATE_ROM_DISABLE] = "DISABLE"
+};
+
+/* Progress Code 1 states */
+static const char *me_progress_bup_values[] = {
+ [ME_HFS2_STATE_BUP_INIT] =
+ "Initialization starts",
+ [ME_HFS2_STATE_BUP_DIS_HOST_WAKE] =
+ "Disable the host wake event",
+ [ME_HFS2_STATE_BUP_CG_ENABLE] =
+ "Enabling CG for cset",
+ [ME_HFS2_STATE_BUP_PM_HND_EN] =
+ "Enabling PM handshaking",
+ [ME_HFS2_STATE_BUP_FLOW_DET] =
+ "Flow determination start process",
+ [ME_HFS2_STATE_BUP_PMC_PATCHING] =
+ "PMC Patching process",
+ [ME_HFS2_STATE_BUP_GET_FLASH_VSCC] =
+ "Get VSCC params",
+ [ME_HFS2_STATE_BUP_SET_FLASH_VSCC] =
+ "Set VSCC params",
+ [ME_HFS2_STATE_BUP_VSCC_ERR] =
+ "Error reading/matching the VSCC table in the descriptor",
+ [ME_HFS2_STATE_BUP_EFSS_INIT] =
+ "Initialize EFFS",
+ [ME_HFS2_STATE_BUP_CHECK_STRAP] =
+ "Check to see if straps say ME DISABLED",
+ [ME_HFS2_STATE_BUP_PWR_OK_TIMEOUT] =
+ "Timeout waiting for PWROK",
+ [ME_HFS2_STATE_BUP_STRAP_DIS] =
+ "EFFS says ME disabled",
+ [ME_HFS2_STATE_BUP_MANUF_OVRD_STRAP] =
+ "Possibly handle BUP manufacturing override strap",
+ [ME_HFS2_STATE_BUP_M3] =
+ "Bringup in M3",
+ [ME_HFS2_STATE_BUP_M0] =
+ "Bringup in M0",
+ [ME_HFS2_STATE_BUP_FLOW_DET_ERR] =
+ "Flow detection error",
+ [ME_HFS2_STATE_BUP_M3_CLK_ERR] =
+ "M3 clock switching error",
+ [ME_HFS2_STATE_BUP_CPU_RESET_DID_TIMEOUT_MEM_MISSING] =
+ "Host error - CPU reset timeout, DID timeout, memory missing",
+ [ME_HFS2_STATE_BUP_M3_KERN_LOAD] =
+ "M3 kernel load",
+ [ME_HFS2_STATE_BUP_T32_MISSING] =
+ "T34 missing - cannot program ICC",
+ [ME_HFS2_STATE_BUP_WAIT_DID] =
+ "Waiting for DID BIOS message",
+ [ME_HFS2_STATE_BUP_WAIT_DID_FAIL] =
+ "Waiting for DID BIOS message failure",
+ [ME_HFS2_STATE_BUP_DID_NO_FAIL] =
+ "DID reported no error",
+ [ME_HFS2_STATE_BUP_ENABLE_UMA] =
+ "Enabling UMA",
+ [ME_HFS2_STATE_BUP_ENABLE_UMA_ERR] =
+ "Enabling UMA error",
+ [ME_HFS2_STATE_BUP_SEND_DID_ACK] =
+ "Sending DID Ack to BIOS",
+ [ME_HFS2_STATE_BUP_SEND_DID_ACK_ERR] =
+ "Sending DID Ack to BIOS error",
+ [ME_HFS2_STATE_BUP_M0_CLK] =
+ "Switching clocks in M0",
+ [ME_HFS2_STATE_BUP_M0_CLK_ERR] =
+ "Switching clocks in M0 error",
+ [ME_HFS2_STATE_BUP_TEMP_DIS] =
+ "ME in temp disable",
+ [ME_HFS2_STATE_BUP_M0_KERN_LOAD] =
+ "M0 kernel load",
+};
+
+void intel_me_status(void)
+{
+ union me_hfs hfs;
+ union me_hfs2 hfs2;
+ union me_hfs3 hfs3;
+
+ hfs.data = me_read_config32(PCI_ME_HFSTS1);
+ hfs2.data = me_read_config32(PCI_ME_HFSTS2);
+ hfs3.data = me_read_config32(PCI_ME_HFSTS3);
+
+ /* Check Current States */
+ printk(BIOS_DEBUG, "ME: FW Partition Table : %s\n",
+ hfs.fields.fpt_bad ? "BAD" : "OK");
+ printk(BIOS_DEBUG, "ME: Bringup Loader Failure : %s\n",
+ hfs.fields.ft_bup_ld_flr ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Firmware Init Complete : %s\n",
+ hfs.fields.fw_init_complete ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Manufacturing Mode : %s\n",
+ hfs.fields.mfg_mode ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Boot Options Present : %s\n",
+ hfs.fields.boot_options_present ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Update In Progress : %s\n",
+ hfs.fields.update_in_progress ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: D3 Support : %s\n",
+ hfs.fields.d3_support_valid ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: D0i3 Support : %s\n",
+ hfs.fields.d0i3_support_valid ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Low Power State Enabled : %s\n",
+ hfs2.fields.low_power_state ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Power Gated : %s\n",
+ hfs2.fields.power_gating_ind ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: CPU Replaced : %s\n",
+ hfs2.fields.cpu_replaced_sts ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: CPU Replacement Valid : %s\n",
+ hfs2.fields.cpu_replaced_valid ? "YES" : "NO");
+ printk(BIOS_DEBUG, "ME: Current Working State : %s\n",
+ me_cws_values[hfs.fields.working_state]);
+ printk(BIOS_DEBUG, "ME: Current Operation State : %s\n",
+ me_opstate_values[hfs.fields.operation_state]);
+ printk(BIOS_DEBUG, "ME: Current Operation Mode : %s\n",
+ me_opmode_values[hfs.fields.operation_mode]);
+ printk(BIOS_DEBUG, "ME: Error Code : %s\n",
+ me_error_values[hfs.fields.error_code]);
+ printk(BIOS_DEBUG, "ME: Progress Phase : %s\n",
+ me_progress_values[hfs2.fields.progress_code]);
+ printk(BIOS_DEBUG, "ME: Power Management Event : %s\n",
+ me_pmevent_values[hfs2.fields.current_pmevent]);
+
+ printk(BIOS_DEBUG, "ME: Progress Phase State : ");
+ switch (hfs2.fields.progress_code) {
+ case ME_HFS2_PHASE_ROM: /* ROM Phase */
+ printk(BIOS_DEBUG, "%s",
+ me_progress_rom_values[hfs2.fields.current_state]);
+ break;
+
+ case ME_HFS2_PHASE_UKERNEL: /* uKernel Phase */
+ printk(BIOS_DEBUG, "0x%02x", hfs2.fields.current_state);
+ break;
+
+ case ME_HFS2_PHASE_BUP: /* Bringup Phase */
+ if (hfs2.fields.current_state < ARRAY_SIZE(me_progress_bup_values)
+ && me_progress_bup_values[hfs2.fields.current_state])
+ printk(BIOS_DEBUG, "%s",
+ me_progress_bup_values[hfs2.fields.current_state]);
+ else
+ printk(BIOS_DEBUG, "0x%02x", hfs2.fields.current_state);
+ break;
+
+ case ME_HFS2_PHASE_HOST_COMM: /* Host Communication Phase */
+ if (!hfs2.fields.current_state)
+ printk(BIOS_DEBUG, "Host communication established");
+ else
+ printk(BIOS_DEBUG, "0x%02x", hfs2.fields.current_state);
+ break;
+
+ default:
+ printk(BIOS_DEBUG, "Unknown phase: 0x%02x state: 0x%02x",
+ hfs2.fields.progress_code, hfs2.fields.current_state);
+ }
+ printk(BIOS_DEBUG, "\n");
+
+ /* Power Down Mitigation Status */
+ printk(BIOS_DEBUG, "ME: Power Down Mitigation : %s\n",
+ hfs3.fields.power_down_mitigation ? "YES" : "NO");
+
+ if (hfs3.fields.power_down_mitigation) {
+ printk(BIOS_INFO, "ME: PD Mitigation State : ");
+ if (hfs3.fields.encrypt_key_override == 1 &&
+ hfs3.fields.encrypt_key_check == 0 &&
+ hfs3.fields.pch_config_change == 0)
+ printk(BIOS_INFO, "Normal Operation");
+ else if (hfs3.fields.encrypt_key_override == 1 &&
+ hfs3.fields.encrypt_key_check == 1 &&
+ hfs3.fields.pch_config_change == 0)
+ printk(BIOS_INFO, "Issue Detected and Recovered");
+ else
+ printk(BIOS_INFO, "Issue Detected but not Recovered");
+ printk(BIOS_INFO, "\n");
+
+ printk(BIOS_DEBUG, "ME: Encryption Key Override : %s\n",
+ hfs3.fields.encrypt_key_override ? "Workaround Applied" :
+ "Unable to override");
+ printk(BIOS_DEBUG, "ME: Encryption Key Check : %s\n",
+ hfs3.fields.encrypt_key_check ? "FAIL" : "PASS");
+ printk(BIOS_DEBUG, "ME: PCH Configuration Info : %s\n",
+ hfs3.fields.pch_config_change ? "Changed" : "No Change");
+
+ printk(BIOS_DEBUG, "ME: Firmware SKU : ");
+ switch (hfs3.fields.fw_sku) {
+ case ME_HFS3_FW_SKU_CONSUMER:
+ printk(BIOS_DEBUG, "Consumer\n");
+ break;
+ case ME_HFS3_FW_SKU_CORPORATE:
+ printk(BIOS_DEBUG, "Corporate\n");
+ break;
+ default:
+ printk(BIOS_DEBUG, "Unknown (0x%x)\n", hfs3.fields.fw_sku);
+ }
+ }
+}
+
+/*
+* Aligning a byte length to length in dwords.
+*/
+static u32 get_dword_length(u32 byte_length)
+{
+ return ALIGN_UP(byte_length, sizeof(uint32_t)) / sizeof(uint32_t);
+}
+
+/*
+* Get remaining message count in dword from circular buffer based on
+* write and read offset.
+*/
+static u32 get_cb_msg_count(u32 data)
+{
+ u8 read_offset = data >> 8;
+ u8 write_offset = data >> 16;
+
+ return get_dword_length(write_offset - read_offset);
+}
+
+static int wait_heci_ready(void)
+{
+ struct stopwatch sw;
+ int timeout = 0;
+ union me_csr csr;
+
+ stopwatch_init_msecs_expire(&sw, HECI_TIMEOUT);
+ while (1) {
+ do {
+ csr.data = me_read_mmio32(MMIO_ME_CSR);
+ if (csr.fields.host_ready)
+ return 0;
+ } while (!(timeout = stopwatch_expired(&sw)));
+
+ printk(BIOS_ERR, "ME_RDY bit is not set after 15 sec");
+ return -1;
+ }
+}
+
+static int wait_heci_cb_avail(u32 len)
+{
+ struct stopwatch sw;
+ union host_csr csr;
+
+ csr.data = me_read_mmio32(MMIO_HOST_CSR);
+ /*
+ * if timeout has happened, return failure as
+ * the circular buffer is not empty
+ */
+ stopwatch_init_msecs_expire(&sw, HECI_SEND_TIMEOUT);
+ /* Must have room for message and message header */
+ while (len > (get_dword_length(csr.fields.me_cir_depth) -
+ get_cb_msg_count(csr.data))) {
+ if (stopwatch_expired(&sw)) {
+ printk(BIOS_ERR,
+ "Circular Buffer never emptied within 5 sec");
+ return -1;
+ }
+ /* wait before trying again */
+ udelay(HECI_DELAY);
+ /* read HOST_CSR for next iteration */
+ csr.data = me_read_mmio32(MMIO_HOST_CSR);
+ }
+ return 0;
+}
+
+static int send_heci_packet(union mei_header *head, u32 len, u32 *payload)
+{
+ int sts;
+ int index;
+ union me_csr csr;
+ union host_csr hcsr;
+
+ /*
+ * wait until there is sufficient room in CB
+ */
+ sts = wait_heci_cb_avail(len + 1);
+ if (sts != 0)
+ return -1;
+
+ /* Write message header */
+ me_write_mmio32(MMIO_ME_CB_WW, head->data);
+
+ /* Write message body */
+ for (index = 0; index < len; index++)
+ me_write_mmio32(MMIO_ME_CB_WW, payload[index]);
+
+ /* Set Interrupt Generate bit */
+ hcsr.data = me_read_mmio32(MMIO_HOST_CSR);
+ hcsr.fields.int_gen = 1;
+ me_write_mmio32(MMIO_HOST_CSR, hcsr.data);
+
+ /* Check if ME Ready bit is set, if set to 0 then return fatal error */
+ csr.data = me_read_mmio32(MMIO_ME_CSR);
+ if (csr.fields.host_ready)
+ return 0;
+ else
+ return -1;
+}
+
+static int recv_heci_packet(union mei_header *head, u32 *packet,
+ u32 *packet_size)
+{
+ union me_csr csr;
+ union host_csr hcsr;
+ int rec_msg = 0;
+ struct stopwatch sw;
+ u32 length, index;
+
+ /* Clear Interrupt Status bit */
+ hcsr.data = me_read_mmio32(MMIO_HOST_CSR);
+ hcsr.fields.int_sts = 1;
+ me_write_mmio32(MMIO_HOST_CSR, hcsr.data);
+
+ /* Check if circular buffer overflow
+ * if yes then return fatal error
+ */
+ csr.data = me_read_mmio32(MMIO_ME_CSR);
+ if (get_cb_msg_count(csr.data) >
+ get_dword_length(csr.fields.me_cir_buff))
+ return -1;
+ /*
+ * if timeout has happened, return failure as
+ * the circular buffer is not empty
+ */
+ stopwatch_init_msecs_expire(&sw, HECI_READ_TIMEOUT);
+ /* go until we got message pkt */
+ do {
+ if (stopwatch_expired(&sw)) {
+ printk(BIOS_ERR,
+ "Circular Buffer not filled within 5 sec");
+ *packet_size = 0;
+ return -1;
+ }
+ csr.data = me_read_mmio32(MMIO_ME_CSR);
+ /* Read one message from HECI buffer */
+ if (get_cb_msg_count(csr.data) > 0) {
+ head->data = me_read_mmio32(MMIO_ME_CB_RW);
+ /* calculate the message length in dword */
+ length = get_dword_length(head->fields.length);
+ if (head->fields.length == 0) {
+ *packet_size = 0;
+ goto SET_IG;
+ }
+ /* Make sure, we have enough space to catch all */
+ if (head->fields.length <= *packet_size) {
+ csr.data = me_read_mmio32(MMIO_ME_CSR);
+ /* get complete message into circular buffer */
+ while (length > get_cb_msg_count(csr.data)) {
+ udelay(HECI_DELAY);
+ csr.data = me_read_mmio32(MMIO_ME_CSR);
+ }
+ /* here is the message */
+ for (index = 0; index < length; index++)
+ packet[index] = me_read_mmio32(MMIO_ME_CB_RW);
+
+ rec_msg = 1;
+ *packet_size = head->fields.length;
+ } else {
+ /* Too small buffer */
+ *packet_size = 0;
+ return -1;
+ }
+ }
+ } while (!rec_msg);
+
+ /*
+ * Check if ME Ready bit is set, if set to 0 then return fatal error
+ * because ME might have reset during transaction and we might have
+ * read a junk data from CB
+ */
+ csr.data = me_read_mmio32(MMIO_ME_CSR);
+ if (!(csr.fields.host_ready))
+ return -1;
+SET_IG:
+ /* Set Interrupt Generate bit */
+ hcsr.data = me_read_mmio32(MMIO_HOST_CSR);
+ hcsr.fields.int_gen = 1;
+ me_write_mmio32(MMIO_HOST_CSR, hcsr.data);
+ return 0;
+}
+
+static int
+send_heci_message(void *msg, int len, u8 hostaddress, u8 clientaddress)
+{
+ u8 retry;
+ int status = -1;
+ u32 cir_buff_depth;
+ union host_csr csr;
+ union mei_header head;
+ int cur = 0;
+ u32 slength, rlength;
+
+ for (retry = 0; retry < MAX_HECI_MESSAGE; retry++) {
+ if (wait_heci_ready() != 0)
+ continue;
+ /* HECI is ready */
+ csr.data = me_read_mmio32(MMIO_HOST_CSR);
+ cir_buff_depth = csr.fields.me_cir_depth;
+ head.fields.client_address = clientaddress;
+ head.fields.host_address = hostaddress;
+ while (len > cur) {
+ rlength = get_dword_length(len - cur);
+ /*
+ * Set the message complete bit if this is last packet
+ * in message needs to be "less than" to account for
+ * the header OR needs to be exact equal to CB depth
+ */
+ if (rlength <= cir_buff_depth)
+ head.fields.is_complete = 1;
+ else
+ head.fields.is_complete = 0;
+ /*
+ * calculate length for message header
+ * header length = smaller of CB buffer or
+ * remaining message
+ */
+ slength = ((cir_buff_depth <= rlength)
+ ? ((cir_buff_depth - 1) * 4)
+ : (len - cur));
+ head.fields.length = slength;
+ head.fields.reserved = 0;
+ /*
+ * send the current packet
+ * (cur should be treated as index for message)
+ */
+ status = send_heci_packet(&head,
+ get_dword_length(head.fields.length), msg);
+ if (status != 0)
+ break;
+ /* update the length information */
+ cur += slength;
+ msg += cur;
+ }
+ if (!status)
+ break;
+ }
+ return status;
+}
+
+static int
+recv_heci_message(void *message, u32 * message_size)
+{
+ union mei_header head;
+ int cur = 0;
+ u8 retry;
+ int status = -1;
+ int msg_complete = 0;
+ u32 pkt_buff;
+
+ for (retry = 0; retry < MAX_HECI_MESSAGE; retry++) {
+ if (wait_heci_ready() != 0)
+ continue;
+ /* HECI is ready */
+ while ((cur < *message_size) && (msg_complete == 0)) {
+ pkt_buff = *message_size - cur;
+ status = recv_heci_packet(&head, message + (cur >> 2),
+ &pkt_buff);
+ if (status == -1) {
+ *message_size = 0;
+ break;
+ }
+ msg_complete = head.fields.is_complete;
+ if (pkt_buff == 0) {
+ /* if not in middle of msg and msg complete bit
+ * is set then this is a valid zero length msg
+ */
+ if ((cur == 0) && (msg_complete == 1))
+ status = 0;
+ else
+ status = -1;
+ *message_size = 0;
+ break;
+ }
+ cur += pkt_buff;
+ }
+ if (!status) {
+ *message_size = cur;
+ break;
+ }
+ }
+ return status;
+}
+
+static int send_heci_reset_message(void)
+{
+ int status;
+ struct reset_reply {
+ u8 group_id;
+ u8 command;
+ u8 reserved;
+ u8 result;
+ } __attribute__ ((packed)) reply;
+ struct reset_message {
+ u8 group_id;
+ u8 cmd;
+ u8 reserved;
+ u8 result;
+ u8 req_origin;
+ u8 reset_type;
+ } __attribute__ ((packed));
+ struct reset_message msg = {
+ .cmd = MKHI_GLOBAL_RESET,
+ .req_origin = GR_ORIGIN_BIOS_POST,
+ .reset_type = GLOBAL_RST_TYPE
+ };
+ u32 reply_size;
+
+ status= send_heci_message(&msg, sizeof(msg),
+ BIOS_HOST_ADD, HECI_MKHI_ADD);
+ if (status != 0)
+ return -1;
+
+ reply_size = sizeof(reply);
+ if (recv_heci_message(&reply, &reply_size) == -1)
+ return -1;
+ /* get reply result from HECI MSG */
+ if (reply.result != 0) {
+ printk(BIOS_DEBUG, "%s: Exit with Failure\n", __func__);
+ return -1;
+ } else {
+ printk(BIOS_DEBUG, "%s: Exit with Success\n", __func__);
+ return 0;
+ }
+}
+
+int send_global_reset(void)
+{
+ int status = -1;
+ union me_hfs hfs;
+
+ /* Check ME operating mode */
+ hfs.data = me_read_config32(PCI_ME_HFSTS1);
+ if (hfs.fields.operation_mode)
+ goto ret;
+
+ /* ME should be in Normal Mode for this command */
+ status = send_heci_reset_message();
+ret:
+ return status;
+}