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>
This commit is contained in:
parent
89b6d4bf12
commit
69ed3ed5d8
|
@ -11,13 +11,17 @@
|
||||||
#include <device/pci_ids.h>
|
#include <device/pci_ids.h>
|
||||||
#include <device/pci_ops.h>
|
#include <device/pci_ops.h>
|
||||||
#include <intelblocks/cse.h>
|
#include <intelblocks/cse.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <option.h>
|
||||||
#include <security/vboot/misc.h>
|
#include <security/vboot/misc.h>
|
||||||
#include <security/vboot/vboot_common.h>
|
#include <security/vboot/vboot_common.h>
|
||||||
|
#include <soc/intel/common/reset.h>
|
||||||
#include <soc/iomap.h>
|
#include <soc/iomap.h>
|
||||||
#include <soc/pci_devs.h>
|
#include <soc/pci_devs.h>
|
||||||
#include <soc/me.h>
|
#include <soc/me.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <timer.h>
|
#include <timer.h>
|
||||||
|
#include <types.h>
|
||||||
|
|
||||||
#define MAX_HECI_MESSAGE_RETRY_COUNT 5
|
#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
|
#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 = {
|
static struct device_operations cse_ops = {
|
||||||
.set_resources = pci_dev_set_resources,
|
.set_resources = pci_dev_set_resources,
|
||||||
.read_resources = pci_dev_read_resources,
|
.read_resources = pci_dev_read_resources,
|
||||||
.enable_resources = pci_dev_enable_resources,
|
.enable_resources = pci_dev_enable_resources,
|
||||||
.init = pci_dev_init,
|
.init = pci_dev_init,
|
||||||
.ops_pci = &pci_dev_ops_pci,
|
.ops_pci = &pci_dev_ops_pci,
|
||||||
|
.enable = cse_set_state,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const unsigned short pci_device_ids[] = {
|
static const unsigned short pci_device_ids[] = {
|
||||||
|
|
|
@ -11,10 +11,15 @@
|
||||||
#define MKHI_GROUP_ID_HMRFPO 0x5
|
#define MKHI_GROUP_ID_HMRFPO 0x5
|
||||||
#define MKHI_GROUP_ID_GEN 0xff
|
#define MKHI_GROUP_ID_GEN 0xff
|
||||||
#define MKHI_GROUP_ID_BUP_COMMON 0xf0
|
#define MKHI_GROUP_ID_BUP_COMMON 0xf0
|
||||||
|
#define MKHI_GROUP_ID_FWCAPS 0x3
|
||||||
|
|
||||||
/* Global Reset Command ID */
|
/* Global Reset Command ID */
|
||||||
#define MKHI_CBM_GLOBAL_RESET_REQ 0xb
|
#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 */
|
/* Origin of Global Reset command */
|
||||||
#define GR_ORIGIN_BIOS_POST 0x2
|
#define GR_ORIGIN_BIOS_POST 0x2
|
||||||
|
|
||||||
|
@ -44,6 +49,12 @@
|
||||||
#define ME_HFS1_COM_SOFT_TEMP_DISABLE 0x3
|
#define ME_HFS1_COM_SOFT_TEMP_DISABLE 0x3
|
||||||
#define ME_HFS1_COM_SECOVER_MEI_MSG 0x5
|
#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 */
|
/* ME Firmware SKU Types */
|
||||||
#define ME_HFS3_FW_SKU_CONSUMER 0x2
|
#define ME_HFS3_FW_SKU_CONSUMER 0x2
|
||||||
#define ME_HFS3_FW_SKU_CORPORATE 0x3
|
#define ME_HFS3_FW_SKU_CORPORATE 0x3
|
||||||
|
|
Loading…
Reference in New Issue