blob: 66c7dd2997bf26655e085aa9383ec77020d712b3 [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>
19#include <device/pci_def.h>
Duncan Lauriec1c94352012-07-13 10:11:54 -070020#include <elog.h>
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +030021#include <halt.h>
22#include <option.h>
23#include <southbridge/intel/common/me.h>
Stefan Reinauer8e073822012-04-04 00:07:22 +020024
Stefan Reinauer8e073822012-04-04 00:07:22 +020025#include "me.h"
26#include "pch.h"
27
Stefan Reinauer8e073822012-04-04 00:07:22 +020028/* Determine the path that we should take based on ME status */
Elyes HAOUASdc035282018-09-18 13:28:49 +020029static me_bios_path intel_me_path(struct device *dev)
Stefan Reinauer8e073822012-04-04 00:07:22 +020030{
31 me_bios_path path = ME_DISABLE_BIOS_PATH;
Angel Pons3f7bb7d2021-01-27 13:03:20 +010032 union me_hfs hfs;
33 union me_gmes gmes;
Stefan Reinauer8e073822012-04-04 00:07:22 +020034
Stefan Reinauer8e073822012-04-04 00:07:22 +020035 /* S3 wake skips all MKHI messages */
Kyösti Mälkkic3ed8862014-06-19 19:50:51 +030036 if (acpi_is_wakeup_s3())
Stefan Reinauer8e073822012-04-04 00:07:22 +020037 return ME_S3WAKE_BIOS_PATH;
Stefan Reinauer8e073822012-04-04 00:07:22 +020038
Angel Pons3f7bb7d2021-01-27 13:03:20 +010039 hfs.raw = pci_read_config32(dev, PCI_ME_HFS);
40 gmes.raw = pci_read_config32(dev, PCI_ME_GMES);
Stefan Reinauer8e073822012-04-04 00:07:22 +020041
42 /* Check and dump status */
43 intel_me_status(&hfs, &gmes);
44
Stefan Reinauer8e073822012-04-04 00:07:22 +020045 /* Check Current Working State */
46 switch (hfs.working_state) {
47 case ME_HFS_CWS_NORMAL:
48 path = ME_NORMAL_BIOS_PATH;
49 break;
50 case ME_HFS_CWS_REC:
51 path = ME_RECOVERY_BIOS_PATH;
52 break;
53 default:
54 path = ME_DISABLE_BIOS_PATH;
55 break;
56 }
57
58 /* Check Current Operation Mode */
59 switch (hfs.operation_mode) {
60 case ME_HFS_MODE_NORMAL:
61 break;
62 case ME_HFS_MODE_DEBUG:
63 case ME_HFS_MODE_DIS:
64 case ME_HFS_MODE_OVER_JMPR:
65 case ME_HFS_MODE_OVER_MEI:
66 default:
67 path = ME_DISABLE_BIOS_PATH;
68 break;
69 }
70
Duncan Laurie5c88c6f2012-09-01 14:00:23 -070071 /* Check for any error code and valid firmware */
72 if (hfs.error_code || hfs.fpt_bad)
Stefan Reinauer8e073822012-04-04 00:07:22 +020073 path = ME_ERROR_BIOS_PATH;
74
Kyösti Mälkkibe5317f2019-11-06 12:07:21 +020075 if (CONFIG(ELOG) && path != ME_NORMAL_BIOS_PATH) {
Duncan Laurie5c88c6f2012-09-01 14:00:23 -070076 struct elog_event_data_me_extended data = {
77 .current_working_state = hfs.working_state,
78 .operation_state = hfs.operation_state,
79 .operation_mode = hfs.operation_mode,
80 .error_code = hfs.error_code,
81 .progress_code = gmes.progress_code,
82 .current_pmevent = gmes.current_pmevent,
83 .current_state = gmes.current_state,
84 };
85 elog_add_event_byte(ELOG_TYPE_MANAGEMENT_ENGINE, path);
86 elog_add_event_raw(ELOG_TYPE_MANAGEMENT_ENGINE_EXT,
87 &data, sizeof(data));
88 }
Duncan Laurie5c88c6f2012-09-01 14:00:23 -070089
Stefan Reinauer8e073822012-04-04 00:07:22 +020090 return path;
91}
92
Angel Ponsc94bc8e2021-01-27 12:17:33 +010093/* Get ME firmware version */
94static int mkhi_get_fw_version(void)
95{
96 struct me_fw_version version;
97 struct mkhi_header mkhi = {
98 .group_id = MKHI_GROUP_ID_GEN,
99 .command = MKHI_GET_FW_VERSION,
100 };
101 struct mei_header mei = {
102 .is_complete = 1,
103 .host_address = MEI_HOST_ADDRESS,
104 .client_address = MEI_ADDRESS_MKHI,
105 .length = sizeof(mkhi),
106 };
107
108 /* Send request and wait for response */
109 if (mei_sendrecv(&mei, &mkhi, NULL, &version, sizeof(version)) < 0) {
110 printk(BIOS_ERR, "ME: GET FW VERSION message failed\n");
111 return -1;
112 }
113
114 printk(BIOS_INFO, "ME: Firmware Version %u.%u.%u.%u (code) "
115 "%u.%u.%u.%u (recovery)\n",
116 version.code_major, version.code_minor,
117 version.code_build_number, version.code_hot_fix,
118 version.recovery_major, version.recovery_minor,
119 version.recovery_build_number, version.recovery_hot_fix);
120
121 return 0;
122}
123
124static inline void print_cap(const char *name, int state)
125{
126 printk(BIOS_DEBUG, "ME Capability: %-30s : %sabled\n",
127 name, state ? "en" : "dis");
128}
129
130/* Get ME Firmware Capabilities */
131static int mkhi_get_fwcaps(void)
132{
133 u32 rule_id = 0;
134 struct me_fwcaps cap;
135 struct mkhi_header mkhi = {
136 .group_id = MKHI_GROUP_ID_FWCAPS,
137 .command = MKHI_FWCAPS_GET_RULE,
138 };
139 struct mei_header mei = {
140 .is_complete = 1,
141 .host_address = MEI_HOST_ADDRESS,
142 .client_address = MEI_ADDRESS_MKHI,
143 .length = sizeof(mkhi) + sizeof(rule_id),
144 };
145
146 /* Send request and wait for response */
147 if (mei_sendrecv(&mei, &mkhi, &rule_id, &cap, sizeof(cap)) < 0) {
148 printk(BIOS_ERR, "ME: GET FWCAPS message failed\n");
149 return -1;
150 }
151
152 print_cap("Full Network manageability", cap.caps_sku.full_net);
153 print_cap("Regular Network manageability", cap.caps_sku.std_net);
154 print_cap("Manageability", cap.caps_sku.manageability);
155 print_cap("Small business technology", cap.caps_sku.small_business);
156 print_cap("Level III manageability", cap.caps_sku.l3manageability);
157 print_cap("IntelR Anti-Theft (AT)", cap.caps_sku.intel_at);
158 print_cap("IntelR Capability Licensing Service (CLS)",
159 cap.caps_sku.intel_cls);
160 print_cap("IntelR Power Sharing Technology (MPC)",
161 cap.caps_sku.intel_mpc);
162 print_cap("ICC Over Clocking", cap.caps_sku.icc_over_clocking);
163 print_cap("Protected Audio Video Path (PAVP)", cap.caps_sku.pavp);
164 print_cap("IPV6", cap.caps_sku.ipv6);
165 print_cap("KVM Remote Control (KVM)", cap.caps_sku.kvm);
166 print_cap("Outbreak Containment Heuristic (OCH)", cap.caps_sku.och);
167 print_cap("Virtual LAN (VLAN)", cap.caps_sku.vlan);
168 print_cap("TLS", cap.caps_sku.tls);
169 print_cap("Wireless LAN (WLAN)", cap.caps_sku.wlan);
170
171 return 0;
172}
173
174
Stefan Reinauer8e073822012-04-04 00:07:22 +0200175/* Check whether ME is present and do basic init */
Elyes HAOUASdc035282018-09-18 13:28:49 +0200176static void intel_me_init(struct device *dev)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200177{
178 me_bios_path path = intel_me_path(dev);
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300179 bool need_reset = false;
Angel Pons3f7bb7d2021-01-27 13:03:20 +0100180 union me_hfs hfs;
Stefan Reinauer8e073822012-04-04 00:07:22 +0200181
182 /* Do initial setup and determine the BIOS path */
Angel Pons2e29c3b2020-08-10 15:47:28 +0200183 printk(BIOS_NOTICE, "ME: BIOS path: %s\n", me_get_bios_path_string(path));
Stefan Reinauer8e073822012-04-04 00:07:22 +0200184
Angel Pons88dcb312021-04-26 17:10:28 +0200185 u8 me_state = get_uint_option("me_state", 0);
186 u8 me_state_prev = get_uint_option("me_state_prev", 0);
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300187
188 printk(BIOS_DEBUG, "ME: me_state=%u, me_state_prev=%u\n", me_state, me_state_prev);
189
Stefan Reinauer8e073822012-04-04 00:07:22 +0200190 switch (path) {
191 case ME_S3WAKE_BIOS_PATH:
James Yea85d4a52020-02-22 20:30:49 +1100192#if CONFIG(HIDE_MEI_ON_ERROR)
193 case ME_ERROR_BIOS_PATH:
194#endif
Stefan Reinauer8e073822012-04-04 00:07:22 +0200195 intel_me_hide(dev);
196 break;
197
198 case ME_NORMAL_BIOS_PATH:
199 /* Validate the extend register */
200 if (intel_me_extend_valid(dev) < 0)
201 break; /* TODO: force recovery mode */
202
203 /* Prepare MEI MMIO interface */
204 if (intel_mei_setup(dev) < 0)
205 break;
206
Kyösti Mälkkic86fc8e2019-11-06 06:32:27 +0200207 if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) {
208 /* Print ME firmware version */
209 mkhi_get_fw_version();
210 /* Print ME firmware capabilities */
211 mkhi_get_fwcaps();
212 }
Stefan Reinauer8e073822012-04-04 00:07:22 +0200213
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300214 /* Put ME in Software Temporary Disable Mode, if needed */
215 if (me_state == CMOS_ME_STATE_DISABLED
216 && CMOS_ME_STATE(me_state_prev) == CMOS_ME_STATE_NORMAL) {
217 printk(BIOS_INFO, "ME: disabling ME\n");
218 if (enter_soft_temp_disable()) {
219 enter_soft_temp_disable_wait();
220 need_reset = true;
221 } else {
222 printk(BIOS_ERR, "ME: failed to enter Soft Temporary Disable mode\n");
223 }
224
225 break;
226 }
227
Stefan Reinauer8e073822012-04-04 00:07:22 +0200228 /*
229 * Leave the ME unlocked in this path.
230 * It will be locked via SMI command later.
231 */
232 break;
233
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300234 case ME_DISABLE_BIOS_PATH:
235 /* Bring ME out of Soft Temporary Disable mode, if needed */
Angel Pons3f7bb7d2021-01-27 13:03:20 +0100236 hfs.raw = pci_read_config32(dev, PCI_ME_HFS);
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300237 if (hfs.operation_mode == ME_HFS_MODE_DIS
238 && me_state == CMOS_ME_STATE_NORMAL
239 && (CMOS_ME_STATE(me_state_prev) == CMOS_ME_STATE_DISABLED
240 || !CMOS_ME_CHANGED(me_state_prev))) {
241 printk(BIOS_INFO, "ME: re-enabling ME\n");
242
243 exit_soft_temp_disable(dev);
244 exit_soft_temp_disable_wait(dev);
245
246 /*
247 * ME starts loading firmware immediately after writing to H_GS,
248 * but Lenovo BIOS performs a reboot after bringing ME back to
249 * Normal mode. Assume that global reset is needed.
250 */
251 need_reset = true;
252 } else {
253 intel_me_hide(dev);
254 }
255 break;
256
James Yea85d4a52020-02-22 20:30:49 +1100257#if !CONFIG(HIDE_MEI_ON_ERROR)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200258 case ME_ERROR_BIOS_PATH:
James Yea85d4a52020-02-22 20:30:49 +1100259#endif
Stefan Reinauer8e073822012-04-04 00:07:22 +0200260 case ME_RECOVERY_BIOS_PATH:
Stefan Reinauer8e073822012-04-04 00:07:22 +0200261 case ME_FIRMWARE_UPDATE_BIOS_PATH:
Stefan Reinauer8e073822012-04-04 00:07:22 +0200262 break;
263 }
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300264
265 /* To avoid boot loops if ME fails to get back from disabled mode,
266 set the 'changed' bit here. */
267 if (me_state != CMOS_ME_STATE(me_state_prev) || need_reset) {
268 u8 new_state = me_state | CMOS_ME_STATE_CHANGED;
Angel Pons88dcb312021-04-26 17:10:28 +0200269 set_uint_option("me_state_prev", new_state);
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300270 }
271
272 if (need_reset) {
273 set_global_reset(true);
274 full_reset();
275 }
Stefan Reinauer8e073822012-04-04 00:07:22 +0200276}
277
Stefan Reinauer8e073822012-04-04 00:07:22 +0200278static struct device_operations device_ops = {
279 .read_resources = pci_dev_read_resources,
280 .set_resources = pci_dev_set_resources,
281 .enable_resources = pci_dev_enable_resources,
282 .init = intel_me_init,
Angel Pons1fc0edd2020-05-31 00:03:28 +0200283 .ops_pci = &pci_dev_ops_pci,
Stefan Reinauer8e073822012-04-04 00:07:22 +0200284};
285
286static const struct pci_driver intel_me __pci_driver = {
287 .ops = &device_ops,
Felix Singer43b7f412022-03-07 04:34:52 +0100288 .vendor = PCI_VID_INTEL,
Stefan Reinauer8e073822012-04-04 00:07:22 +0200289 .device = 0x1c3a,
290};