diff options
author | Subrata Banik <subrata.banik@intel.com> | 2016-08-19 12:33:42 +0530 |
---|---|---|
committer | Aaron Durbin <adurbin@chromium.org> | 2016-10-16 02:50:26 +0200 |
commit | ff8bf410d9596ac82b183cb62e2d8990c550ee4a (patch) | |
tree | 30ff1c100dcb1a4e0a91db6eb3e3a5aa6e185ba1 /src/soc/intel/skylake | |
parent | 29f8708fca820797a088c7e81eb830fc61b21d28 (diff) |
soc/intel/skylake: Implement Global Reset MEI message
As per ME BWG, there are two mechanism to generate a Global
Reset (resets both host and Intel ME), one is through CF9h
IO write of 6h or Eh with "CF9h Global Reset" (CF9GR) bit set,
PMC PCI offset ACh[20]. Another is to issue the Global Reset
MEI message. Because any attempts to cause global reset without
synchronizing the two sides might cause unwanted side effects,
such as unwritten flash data that will get destroyed if the
host were to cause a global reset without informing Intel ME
firmware, the recommended method is to send a Global Reset MEI
message when the following conditions are met:
The PCH chipset firmware just needs to complete the Intel ME
Interface #1 initialization and check the Intel ME HFSTS state
if Intel ME is not in ERROR state and is accepting MEI commands
then firmware should be able to use Global Reset MEI message to
trigger global reset.
Furthermore, if Intel ME is in ERROR state, BIOS can use I/O 0xCF9
write of 0x06 or 0x0E command with PCH ETR3 register bit [20]
to perform the global reset.
BUG=none
BRANCH=none
TEST=Verified Global Reset MEI message is able to perform platform
global issue in ME good state.
Change-Id: If326a137eeadaa695668b76b84c510e12c546024
Signed-off-by: Subrata Banik <subrata.banik@intel.com>
Signed-off-by: Rizwan Qureshi <rizwan.qureshi@intel.com>
Reviewed-on: https://review.coreboot.org/16902
Tested-by: build bot (Jenkins)
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Diffstat (limited to 'src/soc/intel/skylake')
-rw-r--r-- | src/soc/intel/skylake/Makefile.inc | 3 | ||||
-rw-r--r-- | src/soc/intel/skylake/include/soc/me.h | 92 | ||||
-rw-r--r-- | src/soc/intel/skylake/me.c | 683 | ||||
-rw-r--r-- | src/soc/intel/skylake/me_status.c | 324 |
4 files changed, 771 insertions, 331 deletions
diff --git a/src/soc/intel/skylake/Makefile.inc b/src/soc/intel/skylake/Makefile.inc index ccd38abf0d..bf429ef902 100644 --- a/src/soc/intel/skylake/Makefile.inc +++ b/src/soc/intel/skylake/Makefile.inc @@ -36,6 +36,7 @@ romstage-y += flash_controller.c romstage-y += gpio.c romstage-y += memmap.c romstage-y += monotonic_timer.c +romstage-y += me.c romstage-y += pch.c romstage-y += pcr.c romstage-y += pei_data.c @@ -59,7 +60,7 @@ ramstage-y += i2c.c ramstage-y += igd.c ramstage-y += irq.c ramstage-y += lpc.c -ramstage-y += me_status.c +ramstage-y += me.c ramstage-y += memmap.c ramstage-y += monotonic_timer.c ramstage-$(CONFIG_PLATFORM_USES_FSP1_1) += opregion.c diff --git a/src/soc/intel/skylake/include/soc/me.h b/src/soc/intel/skylake/include/soc/me.h index 423e9d10cc..8dc21c381f 100644 --- a/src/soc/intel/skylake/include/soc/me.h +++ b/src/soc/intel/skylake/include/soc/me.h @@ -48,7 +48,9 @@ #define ME_HFS_POWER_SOURCE_AC 1 #define ME_HFS_POWER_SOURCE_DC 2 -struct me_hfs { +union me_hfs { + u32 data; + struct { u32 working_state: 4; u32 mfg_mode: 1; u32 fpt_bad: 1; @@ -66,7 +68,8 @@ struct me_hfs { u32 current_power_source: 2; u32 d3_support_valid: 1; u32 d0i3_support_valid: 1; -} __attribute__ ((packed)); + } __attribute__ ((packed)) fields; +}; #define PCI_ME_HFSTS2 0x48 /* Infrastructure Progress Values */ @@ -145,7 +148,9 @@ struct me_hfs { #define ME_HFS2_PMEVENT_CM3_CM3PG 0xe #define ME_HFS2_PMEVENT_CM0PG_CM0 0xf -struct me_hfs2 { +union me_hfs2 { + u32 data; + struct { u32 reserved1: 3; u32 invoke_mebx: 1; u32 cpu_replaced_sts: 1; @@ -161,13 +166,16 @@ struct me_hfs2 { u32 current_state: 8; u32 current_pmevent: 4; u32 progress_code: 4; -} __attribute__ ((packed)); + } __attribute__ ((packed)) fields; +}; #define PCI_ME_HFSTS3 0x60 #define ME_HFS3_FW_SKU_CONSUMER 0x2 #define ME_HFS3_FW_SKU_CORPORATE 0x3 -struct me_hfs3 { +union me_hfs3 { + u32 data; + struct { u32 reserved1: 4; u32 fw_sku: 3; u32 encrypt_key_check: 1; @@ -175,8 +183,80 @@ struct me_hfs3 { u32 reserved2: 21; u32 encrypt_key_override: 1; u32 power_down_mitigation: 1; -} __attribute__ ((packed)); + } __attribute__ ((packed)) fields; +}; + +/* + * Management Engine MMIO registers + */ +#define MMIO_ME_CB_WW 0x00 +#define MMIO_HOST_CSR 0x04 + +union host_csr { + u32 data; + struct { + u32 int_en: 1; + u32 int_sts: 1; + u32 int_gen: 1; + u32 host_ready: 1; + u32 host_reset: 1; + u32 rsv: 3; + u32 host_read_offset: 8; + u32 host_write_offset: 8; + u32 me_cir_depth: 8; + } __attribute__ ((packed)) fields; +}; + +#define MMIO_ME_CB_RW 0x08 +#define MMIO_ME_CSR 0x0C + +union me_csr { + u32 data; + struct { + u32 int_en: 1; + u32 int_sts: 1; + u32 int_gen: 1; + u32 host_ready: 1; + u32 host_reset: 1; + u32 rsv: 3; + u32 me_read_offset: 8; + u32 me_write_offset: 8; + u32 me_cir_buff: 8; + } __attribute__ ((packed)) fields; +}; + +#define MMIO_ME_D0I3 0x800 + +/* Reset Request */ +#define MKHI_GLOBAL_RESET 0x0b + +#define GR_ORIGIN_BIOS_MEM_INIT 0x01 +#define GR_ORIGIN_BIOS_POST 0x02 +#define GR_ORIGIN_MEBX 0x03 + +#define GLOBAL_RST_TYPE 0x01 + +#define BIOS_HOST_ADD 0x00 +#define HECI_MKHI_ADD 0x07 + +#define MAX_HECI_MESSAGE 5 +#define HECI_TIMEOUT 15000000 /* 15sec */ +#define HECI_SEND_TIMEOUT 5000000 /* 5sec */ +#define HECI_READ_TIMEOUT 5000000 /* 5sec */ +#define HECI_DELAY 1000 /* 1ms */ + +union mei_header { + u32 data; + struct { + u32 client_address: 8; + u32 host_address: 8; + u32 length: 9; + u32 reserved: 6; + u32 is_complete: 1; + } __attribute__ ((packed)) fields; +}; void intel_me_status(void); +int send_global_reset(void); #endif 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; +} diff --git a/src/soc/intel/skylake/me_status.c b/src/soc/intel/skylake/me_status.c deleted file mode 100644 index 79d4dfb7d6..0000000000 --- a/src/soc/intel/skylake/me_status.c +++ /dev/null @@ -1,324 +0,0 @@ -/* - * This file is part of the coreboot project. - * - * Copyright (C) 2008-2009 coresystems GmbH - * Copyright (C) 2014 Google Inc. - * 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 <console/console.h> -#include <device/pci.h> -#include <device/pci_ids.h> -#include <stdlib.h> -#include <string.h> -#include <soc/pci_devs.h> -#include <soc/me.h> -#include <delay.h> - -static inline void me_read_dword_ptr(void *ptr, int offset) -{ - u32 dword = pci_read_config32(PCH_DEV_ME, offset); - memcpy(ptr, &dword, sizeof(dword)); -} - -/* 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) -{ - struct me_hfs _hfs, *hfs = &_hfs; - struct me_hfs2 _hfs2, *hfs2 = &_hfs2; - struct me_hfs3 _hfs3, *hfs3 = &_hfs3; - - me_read_dword_ptr(hfs, PCI_ME_HFSTS1); - me_read_dword_ptr(hfs2, PCI_ME_HFSTS2); - me_read_dword_ptr(hfs3, PCI_ME_HFSTS3); - - /* Check Current States */ - printk(BIOS_DEBUG, "ME: FW Partition Table : %s\n", - hfs->fpt_bad ? "BAD" : "OK"); - printk(BIOS_DEBUG, "ME: Bringup Loader Failure : %s\n", - hfs->ft_bup_ld_flr ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: Firmware Init Complete : %s\n", - hfs->fw_init_complete ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: Manufacturing Mode : %s\n", - hfs->mfg_mode ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: Boot Options Present : %s\n", - hfs->boot_options_present ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: Update In Progress : %s\n", - hfs->update_in_progress ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: D3 Support : %s\n", - hfs->d3_support_valid ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: D0i3 Support : %s\n", - hfs->d0i3_support_valid ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: Low Power State Enabled : %s\n", - hfs2->low_power_state ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: Power Gated : %s\n", - hfs2->power_gating_ind ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: CPU Replaced : %s\n", - hfs2->cpu_replaced_sts ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: CPU Replacement Valid : %s\n", - hfs2->cpu_replaced_valid ? "YES" : "NO"); - printk(BIOS_DEBUG, "ME: Current Working State : %s\n", - me_cws_values[hfs->working_state]); - printk(BIOS_DEBUG, "ME: Current Operation State : %s\n", - me_opstate_values[hfs->operation_state]); - printk(BIOS_DEBUG, "ME: Current Operation Mode : %s\n", - me_opmode_values[hfs->operation_mode]); - printk(BIOS_DEBUG, "ME: Error Code : %s\n", - me_error_values[hfs->error_code]); - printk(BIOS_DEBUG, "ME: Progress Phase : %s\n", - me_progress_values[hfs2->progress_code]); - printk(BIOS_DEBUG, "ME: Power Management Event : %s\n", - me_pmevent_values[hfs2->current_pmevent]); - - printk(BIOS_DEBUG, "ME: Progress Phase State : "); - switch (hfs2->progress_code) { - case ME_HFS2_PHASE_ROM: /* ROM Phase */ - printk(BIOS_DEBUG, "%s", - me_progress_rom_values[hfs2->current_state]); - break; - - case ME_HFS2_PHASE_UKERNEL: /* uKernel Phase */ - printk(BIOS_DEBUG, "0x%02x", hfs2->current_state); - break; - - case ME_HFS2_PHASE_BUP: /* Bringup Phase */ - if (hfs2->current_state < ARRAY_SIZE(me_progress_bup_values) - && me_progress_bup_values[hfs2->current_state]) - printk(BIOS_DEBUG, "%s", - me_progress_bup_values[hfs2->current_state]); - else - printk(BIOS_DEBUG, "0x%02x", hfs2->current_state); - break; - - case ME_HFS2_PHASE_HOST_COMM: /* Host Communication Phase */ - if (!hfs2->current_state) - printk(BIOS_DEBUG, "Host communication established"); - else - printk(BIOS_DEBUG, "0x%02x", hfs2->current_state); - break; - - default: - printk(BIOS_DEBUG, "Unknown phase: 0x%02x state: 0x%02x", - hfs2->progress_code, hfs2->current_state); - } - printk(BIOS_DEBUG, "\n"); - - /* Power Down Mitigation Status */ - printk(BIOS_DEBUG, "ME: Power Down Mitigation : %s\n", - hfs3->power_down_mitigation ? "YES" : "NO"); - - if (hfs3->power_down_mitigation) { - printk(BIOS_INFO, "ME: PD Mitigation State : "); - if (hfs3->encrypt_key_override == 1 && - hfs3->encrypt_key_check == 0 && - hfs3->pch_config_change == 0) - printk(BIOS_INFO, "Normal Operation"); - else if (hfs3->encrypt_key_override == 1 && - hfs3->encrypt_key_check == 1 && - hfs3->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->encrypt_key_override ? "Workaround Applied" : - "Unable to override"); - printk(BIOS_DEBUG, "ME: Encryption Key Check : %s\n", - hfs3->encrypt_key_check ? "FAIL" : "PASS"); - printk(BIOS_DEBUG, "ME: PCH Configuration Info : %s\n", - hfs3->pch_config_change ? "Changed" : "No Change"); - - printk(BIOS_DEBUG, "ME: Firmware SKU : "); - switch (hfs3->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->fw_sku); - } - } -} |