diff options
author | Sean Rhodes <sean@starlabs.systems> | 2021-04-30 16:38:17 +0100 |
---|---|---|
committer | Felix Held <felix-coreboot@felixheld.de> | 2021-11-22 14:47:05 +0000 |
commit | 69ed3ed5d8c944a90873c10e8ca3bc15042dda22 (patch) | |
tree | 07c00fa9f8aaf75e4b09911c421036435465b839 | |
parent | 89b6d4bf12aed24f7988009f414435f76d098810 (diff) |
soc/intel: Allow enable/disable ME via CMOS
Add .enable method that will set the CSME state. The state is based on
the new CMOS option me_state, with values of 0 and 1. The method is very
stable when switching between different firmware platforms.
This method should not be used in combination with USE_ME_CLEANER.
State 1 will result in:
ME: Current Working State : 4
ME: Current Operation State : 1
ME: Current Operation Mode : 3
ME: Error Code : 2
State 0 will result in:
ME: Current Working State : 5
ME: Current Operation State : 1
ME: Current Operation Mode : 0
ME: Error Code : 0
Tested on:
KBL-R: i7-8550u
CML: i3-10110u, i7-10710u
TGL: i3-1110G4, i7-1165G7
Signed-off-by: Sean Rhodes <sean@starlabs.systems>
Change-Id: I374db3b7c0ded71cdc18f27970252fec7220cc20
Reviewed-on: https://review.coreboot.org/c/coreboot/+/52800
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Tim Crawford <tcrawford@system76.com>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
-rw-r--r-- | src/soc/intel/common/block/cse/cse.c | 159 | ||||
-rw-r--r-- | src/soc/intel/common/block/include/intelblocks/cse.h | 11 |
2 files changed, 170 insertions, 0 deletions
diff --git a/src/soc/intel/common/block/cse/cse.c b/src/soc/intel/common/block/cse/cse.c index f37ff9589e..b46d3e4a1b 100644 --- a/src/soc/intel/common/block/cse/cse.c +++ b/src/soc/intel/common/block/cse/cse.c @@ -11,13 +11,17 @@ #include <device/pci_ids.h> #include <device/pci_ops.h> #include <intelblocks/cse.h> +#include <limits.h> +#include <option.h> #include <security/vboot/misc.h> #include <security/vboot/vboot_common.h> +#include <soc/intel/common/reset.h> #include <soc/iomap.h> #include <soc/pci_devs.h> #include <soc/me.h> #include <string.h> #include <timer.h> +#include <types.h> #define MAX_HECI_MESSAGE_RETRY_COUNT 5 @@ -961,12 +965,167 @@ bool set_cse_device_state(unsigned int devfn, enum cse_device_state requested_st #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[] = { diff --git a/src/soc/intel/common/block/include/intelblocks/cse.h b/src/soc/intel/common/block/include/intelblocks/cse.h index 7f455708e3..4184b17c52 100644 --- a/src/soc/intel/common/block/include/intelblocks/cse.h +++ b/src/soc/intel/common/block/include/intelblocks/cse.h @@ -11,10 +11,15 @@ #define MKHI_GROUP_ID_HMRFPO 0x5 #define MKHI_GROUP_ID_GEN 0xff #define MKHI_GROUP_ID_BUP_COMMON 0xf0 +#define MKHI_GROUP_ID_FWCAPS 0x3 /* Global Reset Command ID */ #define MKHI_CBM_GLOBAL_RESET_REQ 0xb +/* Set State Command ID */ +#define MKHI_SET_ME_DISABLE 0x3 +#define MKHI_SET_ME_ENABLE 0x3 + /* Origin of Global Reset command */ #define GR_ORIGIN_BIOS_POST 0x2 @@ -44,6 +49,12 @@ #define ME_HFS1_COM_SOFT_TEMP_DISABLE 0x3 #define ME_HFS1_COM_SECOVER_MEI_MSG 0x5 +/* ME Disable Rule */ +#define ME_DISABLE_RULE_ID 6 +#define ME_DISABLE_RULE_LENGTH 4 +#define ME_DISABLE_COMMAND 0 +#define ME_DISABLE_ATTEMPTS 3 + /* ME Firmware SKU Types */ #define ME_HFS3_FW_SKU_CONSUMER 0x2 #define ME_HFS3_FW_SKU_CORPORATE 0x3 |