Evgeny Zinoviev has uploaded this change for review.

View Change

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

To view, visit change 37115. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Change-Id: Ic01526c9731cbef4e8552bbc352133a2415787c2
Gerrit-Change-Number: 37115
Gerrit-PatchSet: 1
Gerrit-Owner: Evgeny Zinoviev <me@ch1p.io>
Gerrit-MessageType: newchange