blob: 73948824f14696ce08956e737a7eb14d0c45a992 [file] [log] [blame]
Angel Pons182dbde2020-04-02 23:49:05 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Stefan Reinauer8e073822012-04-04 00:07:22 +02002
3/*
4 * This is a ramstage driver for the Intel Management Engine found in the
5 * 6-series chipset. It handles the required boot-time messages over the
6 * MMIO-based Management Engine Interface to tell the ME that the BIOS is
7 * finished with POST. Additional messages are defined for debug but are
8 * not used unless the console loglevel is high enough.
9 */
10
Furquan Shaikh76cedd22020-05-02 10:24:23 -070011#include <acpi/acpi.h>
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +030012#include <cf9_reset.h>
Kyösti Mälkki13f66502019-03-03 08:01:05 +020013#include <device/mmio.h>
Kyösti Mälkki21d6a272019-11-05 18:50:38 +020014#include <device/device.h>
15#include <device/pci.h>
Angel Pons7f32df32020-06-02 13:36:57 +020016#include <device/pci_ops.h>
17#include <console/console.h>
Stefan Reinauer8e073822012-04-04 00:07:22 +020018#include <device/pci_ids.h>
Duncan Lauriec1c94352012-07-13 10:11:54 -070019#include <elog.h>
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +030020#include <halt.h>
21#include <option.h>
22#include <southbridge/intel/common/me.h>
Stefan Reinauer8e073822012-04-04 00:07:22 +020023
Stefan Reinauer8e073822012-04-04 00:07:22 +020024#include "me.h"
25#include "pch.h"
26
Stefan Reinauer8e073822012-04-04 00:07:22 +020027/* Determine the path that we should take based on ME status */
Elyes HAOUASdc035282018-09-18 13:28:49 +020028static me_bios_path intel_me_path(struct device *dev)
Stefan Reinauer8e073822012-04-04 00:07:22 +020029{
30 me_bios_path path = ME_DISABLE_BIOS_PATH;
Angel Pons3f7bb7d2021-01-27 13:03:20 +010031 union me_hfs hfs;
32 union me_gmes gmes;
Stefan Reinauer8e073822012-04-04 00:07:22 +020033
Stefan Reinauer8e073822012-04-04 00:07:22 +020034 /* S3 wake skips all MKHI messages */
Kyösti Mälkkic3ed8862014-06-19 19:50:51 +030035 if (acpi_is_wakeup_s3())
Stefan Reinauer8e073822012-04-04 00:07:22 +020036 return ME_S3WAKE_BIOS_PATH;
Stefan Reinauer8e073822012-04-04 00:07:22 +020037
Angel Pons3f7bb7d2021-01-27 13:03:20 +010038 hfs.raw = pci_read_config32(dev, PCI_ME_HFS);
39 gmes.raw = pci_read_config32(dev, PCI_ME_GMES);
Stefan Reinauer8e073822012-04-04 00:07:22 +020040
41 /* Check and dump status */
42 intel_me_status(&hfs, &gmes);
43
Stefan Reinauer8e073822012-04-04 00:07:22 +020044 /* Check Current Working State */
45 switch (hfs.working_state) {
46 case ME_HFS_CWS_NORMAL:
47 path = ME_NORMAL_BIOS_PATH;
48 break;
49 case ME_HFS_CWS_REC:
50 path = ME_RECOVERY_BIOS_PATH;
51 break;
52 default:
53 path = ME_DISABLE_BIOS_PATH;
54 break;
55 }
56
57 /* Check Current Operation Mode */
58 switch (hfs.operation_mode) {
59 case ME_HFS_MODE_NORMAL:
60 break;
61 case ME_HFS_MODE_DEBUG:
62 case ME_HFS_MODE_DIS:
63 case ME_HFS_MODE_OVER_JMPR:
64 case ME_HFS_MODE_OVER_MEI:
65 default:
66 path = ME_DISABLE_BIOS_PATH;
67 break;
68 }
69
Duncan Laurie5c88c6f2012-09-01 14:00:23 -070070 /* Check for any error code and valid firmware */
71 if (hfs.error_code || hfs.fpt_bad)
Stefan Reinauer8e073822012-04-04 00:07:22 +020072 path = ME_ERROR_BIOS_PATH;
73
Kyösti Mälkkibe5317f2019-11-06 12:07:21 +020074 if (CONFIG(ELOG) && path != ME_NORMAL_BIOS_PATH) {
Duncan Laurie5c88c6f2012-09-01 14:00:23 -070075 struct elog_event_data_me_extended data = {
76 .current_working_state = hfs.working_state,
77 .operation_state = hfs.operation_state,
78 .operation_mode = hfs.operation_mode,
79 .error_code = hfs.error_code,
80 .progress_code = gmes.progress_code,
81 .current_pmevent = gmes.current_pmevent,
82 .current_state = gmes.current_state,
83 };
84 elog_add_event_byte(ELOG_TYPE_MANAGEMENT_ENGINE, path);
85 elog_add_event_raw(ELOG_TYPE_MANAGEMENT_ENGINE_EXT,
86 &data, sizeof(data));
87 }
Duncan Laurie5c88c6f2012-09-01 14:00:23 -070088
Stefan Reinauer8e073822012-04-04 00:07:22 +020089 return path;
90}
91
Angel Ponsc94bc8e2021-01-27 12:17:33 +010092/* Get ME firmware version */
93static int mkhi_get_fw_version(void)
94{
95 struct me_fw_version version;
96 struct mkhi_header mkhi = {
97 .group_id = MKHI_GROUP_ID_GEN,
98 .command = MKHI_GET_FW_VERSION,
99 };
100 struct mei_header mei = {
101 .is_complete = 1,
102 .host_address = MEI_HOST_ADDRESS,
103 .client_address = MEI_ADDRESS_MKHI,
104 .length = sizeof(mkhi),
105 };
106
107 /* Send request and wait for response */
108 if (mei_sendrecv(&mei, &mkhi, NULL, &version, sizeof(version)) < 0) {
109 printk(BIOS_ERR, "ME: GET FW VERSION message failed\n");
110 return -1;
111 }
112
113 printk(BIOS_INFO, "ME: Firmware Version %u.%u.%u.%u (code) "
114 "%u.%u.%u.%u (recovery)\n",
115 version.code_major, version.code_minor,
116 version.code_build_number, version.code_hot_fix,
117 version.recovery_major, version.recovery_minor,
118 version.recovery_build_number, version.recovery_hot_fix);
119
120 return 0;
121}
122
123static inline void print_cap(const char *name, int state)
124{
125 printk(BIOS_DEBUG, "ME Capability: %-30s : %sabled\n",
126 name, state ? "en" : "dis");
127}
128
129/* Get ME Firmware Capabilities */
130static int mkhi_get_fwcaps(void)
131{
132 u32 rule_id = 0;
133 struct me_fwcaps cap;
134 struct mkhi_header mkhi = {
135 .group_id = MKHI_GROUP_ID_FWCAPS,
136 .command = MKHI_FWCAPS_GET_RULE,
137 };
138 struct mei_header mei = {
139 .is_complete = 1,
140 .host_address = MEI_HOST_ADDRESS,
141 .client_address = MEI_ADDRESS_MKHI,
142 .length = sizeof(mkhi) + sizeof(rule_id),
143 };
144
145 /* Send request and wait for response */
146 if (mei_sendrecv(&mei, &mkhi, &rule_id, &cap, sizeof(cap)) < 0) {
147 printk(BIOS_ERR, "ME: GET FWCAPS message failed\n");
148 return -1;
149 }
150
151 print_cap("Full Network manageability", cap.caps_sku.full_net);
152 print_cap("Regular Network manageability", cap.caps_sku.std_net);
153 print_cap("Manageability", cap.caps_sku.manageability);
154 print_cap("Small business technology", cap.caps_sku.small_business);
155 print_cap("Level III manageability", cap.caps_sku.l3manageability);
156 print_cap("IntelR Anti-Theft (AT)", cap.caps_sku.intel_at);
157 print_cap("IntelR Capability Licensing Service (CLS)",
158 cap.caps_sku.intel_cls);
159 print_cap("IntelR Power Sharing Technology (MPC)",
160 cap.caps_sku.intel_mpc);
161 print_cap("ICC Over Clocking", cap.caps_sku.icc_over_clocking);
162 print_cap("Protected Audio Video Path (PAVP)", cap.caps_sku.pavp);
163 print_cap("IPV6", cap.caps_sku.ipv6);
164 print_cap("KVM Remote Control (KVM)", cap.caps_sku.kvm);
165 print_cap("Outbreak Containment Heuristic (OCH)", cap.caps_sku.och);
166 print_cap("Virtual LAN (VLAN)", cap.caps_sku.vlan);
167 print_cap("TLS", cap.caps_sku.tls);
168 print_cap("Wireless LAN (WLAN)", cap.caps_sku.wlan);
169
170 return 0;
171}
172
173
Stefan Reinauer8e073822012-04-04 00:07:22 +0200174/* Check whether ME is present and do basic init */
Elyes HAOUASdc035282018-09-18 13:28:49 +0200175static void intel_me_init(struct device *dev)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200176{
177 me_bios_path path = intel_me_path(dev);
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300178 bool need_reset = false;
Angel Pons3f7bb7d2021-01-27 13:03:20 +0100179 union me_hfs hfs;
Stefan Reinauer8e073822012-04-04 00:07:22 +0200180
181 /* Do initial setup and determine the BIOS path */
Angel Pons2e29c3b2020-08-10 15:47:28 +0200182 printk(BIOS_NOTICE, "ME: BIOS path: %s\n", me_get_bios_path_string(path));
Stefan Reinauer8e073822012-04-04 00:07:22 +0200183
Angel Pons88dcb312021-04-26 17:10:28 +0200184 u8 me_state = get_uint_option("me_state", 0);
185 u8 me_state_prev = get_uint_option("me_state_prev", 0);
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300186
187 printk(BIOS_DEBUG, "ME: me_state=%u, me_state_prev=%u\n", me_state, me_state_prev);
188
Stefan Reinauer8e073822012-04-04 00:07:22 +0200189 switch (path) {
190 case ME_S3WAKE_BIOS_PATH:
James Yea85d4a52020-02-22 20:30:49 +1100191#if CONFIG(HIDE_MEI_ON_ERROR)
192 case ME_ERROR_BIOS_PATH:
193#endif
Stefan Reinauer8e073822012-04-04 00:07:22 +0200194 intel_me_hide(dev);
195 break;
196
197 case ME_NORMAL_BIOS_PATH:
198 /* Validate the extend register */
199 if (intel_me_extend_valid(dev) < 0)
200 break; /* TODO: force recovery mode */
201
202 /* Prepare MEI MMIO interface */
203 if (intel_mei_setup(dev) < 0)
204 break;
205
Kyösti Mälkkic86fc8e2019-11-06 06:32:27 +0200206 if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) {
207 /* Print ME firmware version */
208 mkhi_get_fw_version();
209 /* Print ME firmware capabilities */
210 mkhi_get_fwcaps();
211 }
Stefan Reinauer8e073822012-04-04 00:07:22 +0200212
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300213 /* Put ME in Software Temporary Disable Mode, if needed */
214 if (me_state == CMOS_ME_STATE_DISABLED
215 && CMOS_ME_STATE(me_state_prev) == CMOS_ME_STATE_NORMAL) {
216 printk(BIOS_INFO, "ME: disabling ME\n");
217 if (enter_soft_temp_disable()) {
218 enter_soft_temp_disable_wait();
219 need_reset = true;
220 } else {
221 printk(BIOS_ERR, "ME: failed to enter Soft Temporary Disable mode\n");
222 }
223
224 break;
225 }
226
Stefan Reinauer8e073822012-04-04 00:07:22 +0200227 /*
228 * Leave the ME unlocked in this path.
229 * It will be locked via SMI command later.
230 */
231 break;
232
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300233 case ME_DISABLE_BIOS_PATH:
234 /* Bring ME out of Soft Temporary Disable mode, if needed */
Angel Pons3f7bb7d2021-01-27 13:03:20 +0100235 hfs.raw = pci_read_config32(dev, PCI_ME_HFS);
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300236 if (hfs.operation_mode == ME_HFS_MODE_DIS
237 && me_state == CMOS_ME_STATE_NORMAL
238 && (CMOS_ME_STATE(me_state_prev) == CMOS_ME_STATE_DISABLED
239 || !CMOS_ME_CHANGED(me_state_prev))) {
240 printk(BIOS_INFO, "ME: re-enabling ME\n");
241
242 exit_soft_temp_disable(dev);
243 exit_soft_temp_disable_wait(dev);
244
245 /*
246 * ME starts loading firmware immediately after writing to H_GS,
247 * but Lenovo BIOS performs a reboot after bringing ME back to
248 * Normal mode. Assume that global reset is needed.
249 */
250 need_reset = true;
251 } else {
252 intel_me_hide(dev);
253 }
254 break;
255
James Yea85d4a52020-02-22 20:30:49 +1100256#if !CONFIG(HIDE_MEI_ON_ERROR)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200257 case ME_ERROR_BIOS_PATH:
James Yea85d4a52020-02-22 20:30:49 +1100258#endif
Stefan Reinauer8e073822012-04-04 00:07:22 +0200259 case ME_RECOVERY_BIOS_PATH:
Stefan Reinauer8e073822012-04-04 00:07:22 +0200260 case ME_FIRMWARE_UPDATE_BIOS_PATH:
Stefan Reinauer8e073822012-04-04 00:07:22 +0200261 break;
262 }
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300263
264 /* To avoid boot loops if ME fails to get back from disabled mode,
265 set the 'changed' bit here. */
266 if (me_state != CMOS_ME_STATE(me_state_prev) || need_reset) {
267 u8 new_state = me_state | CMOS_ME_STATE_CHANGED;
Angel Pons88dcb312021-04-26 17:10:28 +0200268 set_uint_option("me_state_prev", new_state);
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300269 }
270
271 if (need_reset) {
272 set_global_reset(true);
273 full_reset();
274 }
Stefan Reinauer8e073822012-04-04 00:07:22 +0200275}
276
Stefan Reinauer8e073822012-04-04 00:07:22 +0200277static struct device_operations device_ops = {
278 .read_resources = pci_dev_read_resources,
279 .set_resources = pci_dev_set_resources,
280 .enable_resources = pci_dev_enable_resources,
281 .init = intel_me_init,
Angel Pons1fc0edd2020-05-31 00:03:28 +0200282 .ops_pci = &pci_dev_ops_pci,
Stefan Reinauer8e073822012-04-04 00:07:22 +0200283};
284
285static const struct pci_driver intel_me __pci_driver = {
286 .ops = &device_ops,
Felix Singer43b7f412022-03-07 04:34:52 +0100287 .vendor = PCI_VID_INTEL,
Stefan Reinauer8e073822012-04-04 00:07:22 +0200288 .device = 0x1c3a,
289};