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:
Sean Rhodes 2021-04-30 16:38:17 +01:00 committed by Felix Held
parent 89b6d4bf12
commit 69ed3ed5d8
2 changed files with 170 additions and 0 deletions

View File

@ -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[] = {

View File

@ -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