Evgeny Zinoviev has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/37115 )
Change subject: [WIP] sb/intel/bd82x6x: Support ME Software Disable Mode ......................................................................
[WIP] sb/intel/bd82x6x: Support ME Software Disable Mode
AFAIK, ME is supposed to enter Software Temp Disable Mode on the next reboot after receiving the disable command.
But my tests on X230 (ME firmware 8.1.1416.40) show that CF9 reset doesn't help here and user must manually perform power off/power on cycle.
So for now working algorithm to disable ME is as follows: - run `nvramtool -w me_disable=Enable` - reboot - power off - power on - run `intelmetool -m` to verify that ME is in soft temp disable mode.
To enable ME: - run `nvramtool -m me_disable=Disable` - reboot
TODO: if it's possible, find a way to perform correct program reset so that user wouldn't have to do one more power off/power on.
Change-Id: Ic01526c9731cbef4e8552bbc352133a2415787c2 Signed-off-by: Evgeny Zinoviev me@ch1p.io --- M src/mainboard/lenovo/x230/cmos.default M src/mainboard/lenovo/x230/cmos.layout M src/southbridge/intel/bd82x6x/early_me.c M src/southbridge/intel/bd82x6x/me.h M src/southbridge/intel/bd82x6x/me_8.x.c 5 files changed, 104 insertions(+), 6 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/15/37115/1
diff --git a/src/mainboard/lenovo/x230/cmos.default b/src/mainboard/lenovo/x230/cmos.default index 979f132..75f27df 100644 --- a/src/mainboard/lenovo/x230/cmos.default +++ b/src/mainboard/lenovo/x230/cmos.default @@ -14,3 +14,4 @@ trackpoint=Enable backlight=Both usb_always_on=Disable +me_disable=Disable diff --git a/src/mainboard/lenovo/x230/cmos.layout b/src/mainboard/lenovo/x230/cmos.layout index 27197fb..755d98b 100644 --- a/src/mainboard/lenovo/x230/cmos.layout +++ b/src/mainboard/lenovo/x230/cmos.layout @@ -73,6 +73,9 @@
# coreboot config options: cpu #424 8 r 0 unused +424 1 e 1 me_disable +425 1 r 0 me_disable_prev +#426 6 r 0 unused
# coreboot config options: northbridge 432 3 e 11 gfx_uma_size diff --git a/src/southbridge/intel/bd82x6x/early_me.c b/src/southbridge/intel/bd82x6x/early_me.c index f82ed3e..41ca3f6 100644 --- a/src/southbridge/intel/bd82x6x/early_me.c +++ b/src/southbridge/intel/bd82x6x/early_me.c @@ -23,6 +23,7 @@ #include <halt.h> #include <string.h> #include <timestamp.h> +#include <option.h> #include "me.h" #include "pch.h"
@@ -236,7 +237,6 @@ printk(BIOS_NOTICE, "ME: Current PM event: 0x%x\n", (me_fws2 & 0xf000000) >> 24); printk(BIOS_NOTICE, "ME: Progress code : 0x%x\n", (me_fws2 & 0xf0000000) >> 28);
- /* Return the requested BIOS action */ printk(BIOS_NOTICE, "ME: Requested BIOS Action: %s\n", me_ack_values[(hfs & 0xe) >> 1]); diff --git a/src/southbridge/intel/bd82x6x/me.h b/src/southbridge/intel/bd82x6x/me.h index 203d0c0..e05a178 100644 --- a/src/southbridge/intel/bd82x6x/me.h +++ b/src/southbridge/intel/bd82x6x/me.h @@ -185,6 +185,11 @@ #define MKHI_GLOBAL_RESET 0x0b
#define MKHI_FWCAPS_GET_RULE 0x02 +#define MKHI_FWCAPS_SET_RULE 0x03 + +#define MKHI_HMRFPO_ENABLE 0x01 +#define MKHI_HMRFPO_LOCK 0x02 +#define MKHI_HMRFPO_DISABLE 0x04
#define MKHI_MDES_ENABLE 0x09
diff --git a/src/southbridge/intel/bd82x6x/me_8.x.c b/src/southbridge/intel/bd82x6x/me_8.x.c index f13ced9..5d57e02 100644 --- a/src/southbridge/intel/bd82x6x/me_8.x.c +++ b/src/southbridge/intel/bd82x6x/me_8.x.c @@ -23,6 +23,7 @@ */
#include <arch/acpi.h> +#include <cf9_reset.h> #include <device/mmio.h> #include <device/device.h> #include <device/pci.h> @@ -30,6 +31,7 @@ #include <console/console.h> #include <device/pci_ids.h> #include <device/pci_def.h> +#include <pc80/mc146818rtc.h> #include <string.h> #include <delay.h> #include <elog.h> @@ -189,7 +191,7 @@ }
static int mei_send_msg(struct mei_header *mei, struct mkhi_header *mkhi, - void *req_data) + void *req_data, bool wait_ready) { struct mei_csr host; unsigned int ndata, n; @@ -247,8 +249,11 @@ host.interrupt_generate = 1; write_host_csr(&host);
- /* Make sure ME is ready after sending request data */ - return mei_wait_for_me_ready(); + if (!wait_ready) + return 0; + else + /* Make sure ME is ready after sending request data */ + return mei_wait_for_me_ready(); }
static int mei_recv_msg(struct mkhi_header *mkhi, @@ -338,10 +343,12 @@ static inline int mei_sendrecv(struct mei_header *mei, struct mkhi_header *mkhi, void *req_data, void *rsp_data, int rsp_bytes) { - if (mei_send_msg(mei, mkhi, req_data) < 0) + if (mei_send_msg(mei, mkhi, req_data, true) < 0) return -1; + if (mei_recv_msg(mkhi, rsp_data, rsp_bytes) < 0) return -1; + return 0; }
@@ -513,6 +520,50 @@ }
#else /* !__SIMPLE_DEVICE__ */ +static void reset(void) { + struct device *lpc = pcidev_on_root(0x1f, 0); + u32 etr3 = pci_read_config32(lpc, ETR3); + u8 cf9; + + etr3 |= ETR3_CF9GR; + pci_write_config32(lpc, ETR3, etr3); + + cf9 = inb(0xcf9); + cf9 |= 0x0e; + outb(cf9, 0xcf9); + + halt(); +} + +static void enable_soft_temp_disable_mode(void) { + u32 message[2] = { 0x06, 0x01 }; + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_FWCAPS, + .command = MKHI_FWCAPS_SET_RULE, + }; + struct mei_header mei = { + .is_complete = 1, + .length = sizeof(mkhi) + sizeof(message), + .host_address = MEI_HOST_ADDRESS, + .client_address = MEI_ADDRESS_MKHI, + }; + + printk(BIOS_NOTICE, "ME: %s\n", __FUNCTION__); + + /* + * ME stops responding after receiving the disable command, + * therefore it makes no sence to wait for it + */ + bool wait_ready = false; + + if (mei_send_msg(&mei, &mkhi, &message, wait_ready) < 0) + printk(BIOS_DEBUG, "ME: %s: me_send_msg returned -1 (wait timeout)\n", + __FUNCTION__); +} + +static void disable_soft_temp_disable_mode(struct device *dev) { + pci_write_config32(dev, PCI_ME_H_GS, 0x20000000); +}
/* Determine the path that we should take based on ME status */ static me_bios_path intel_me_path(struct device *dev) @@ -675,10 +726,19 @@ { me_bios_path path = intel_me_path(dev); me_bios_payload mbp_data; + u8 me_disable, me_disable_prev; + bool need_reset = false; + struct me_hfs hfs;
/* Do initial setup and determine the BIOS path */ printk(BIOS_NOTICE, "ME: BIOS path: %s\n", me_bios_path_values[path]);
+ get_option(&me_disable, "me_disable"); + get_option(&me_disable_prev, "me_disable_prev"); + + printk(BIOS_DEBUG, "ME: me_disable=%u, me_disable_prev=%u\n", + me_disable, me_disable_prev); + switch (path) { case ME_S3WAKE_BIOS_PATH: intel_me_hide(dev); @@ -710,6 +770,17 @@ } #endif
+ /* Put ME in Software Temporary Disable Mode, if needed */ + if (me_disable) { + printk(BIOS_DEBUG, "ME: need to disable ME\n"); + enable_soft_temp_disable_mode(); + if (!me_disable_prev) { + printk(BIOS_DEBUG, "ME: me_disable_prev differs, need to reset\n"); + need_reset = true; + break; + } + } + if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) { me_print_fw_version(&mbp_data.fw_version_name); me_print_fwcaps(&mbp_data.fw_caps_sku); @@ -721,12 +792,30 @@ */ break;
+ case ME_DISABLE_BIOS_PATH: + /* Bring ME out of Softwate Temporary Disable mode, if needed */ + pci_read_dword_ptr(dev, &hfs, PCI_ME_HFS); + if (hfs.operation_mode == ME_HFS_MODE_DIS && !me_disable) { + printk(BIOS_DEBUG, "ME: need to undisable ME\n"); + disable_soft_temp_disable_mode(dev); + if (me_disable_prev) { + printk(BIOS_DEBUG, "ME: me_disable_prev differs, need to reset\n"); + need_reset = true; + } + } + break; + case ME_ERROR_BIOS_PATH: case ME_RECOVERY_BIOS_PATH: - case ME_DISABLE_BIOS_PATH: case ME_FIRMWARE_UPDATE_BIOS_PATH: break; } + + if (me_disable != me_disable_prev) + set_option("me_disable_prev", &me_disable); + + if (need_reset) + reset(); }
static struct pci_operations pci_ops = {