blob: e81d7ff7b3539deb45720dbf63943c89eaeb7e62 [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>
20#include <string.h>
21#include <delay.h>
Duncan Lauriec1c94352012-07-13 10:11:54 -070022#include <elog.h>
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +030023#include <halt.h>
24#include <option.h>
25#include <southbridge/intel/common/me.h>
Stefan Reinauer8e073822012-04-04 00:07:22 +020026
Stefan Reinauer8e073822012-04-04 00:07:22 +020027#include "me.h"
28#include "pch.h"
29
Angel Ponsc94bc8e2021-01-27 12:17:33 +010030#ifdef __SIMPLE_DEVICE__
31
Stefan Reinauer8e073822012-04-04 00:07:22 +020032/* Send END OF POST message to the ME */
Angel Ponsc94bc8e2021-01-27 12:17:33 +010033static int mkhi_end_of_post(void)
Stefan Reinauer8e073822012-04-04 00:07:22 +020034{
35 struct mkhi_header mkhi = {
36 .group_id = MKHI_GROUP_ID_GEN,
37 .command = MKHI_END_OF_POST,
38 };
39 struct mei_header mei = {
40 .is_complete = 1,
41 .host_address = MEI_HOST_ADDRESS,
42 .client_address = MEI_ADDRESS_MKHI,
43 .length = sizeof(mkhi),
44 };
45
46 /* Send request and wait for response */
47 if (mei_sendrecv(&mei, &mkhi, NULL, NULL, 0) < 0) {
48 printk(BIOS_ERR, "ME: END OF POST message failed\n");
49 return -1;
50 }
51
52 printk(BIOS_INFO, "ME: END OF POST message successful\n");
53 return 0;
54}
55
Stefan Reinauer998f3a22012-06-11 15:15:46 -070056static void intel_me7_finalize_smm(void)
Stefan Reinauer8e073822012-04-04 00:07:22 +020057{
58 struct me_hfs hfs;
59 u32 reg32;
60
Angel Pons2e29c3b2020-08-10 15:47:28 +020061 update_mei_base_address();
Stefan Reinauer8e073822012-04-04 00:07:22 +020062
63 /* S3 path will have hidden this device already */
Angel Pons2e29c3b2020-08-10 15:47:28 +020064 if (!is_mei_base_address_valid())
Stefan Reinauer8e073822012-04-04 00:07:22 +020065 return;
66
67 /* Make sure ME is in a mode that expects EOP */
Kyösti Mälkkifd98c652013-07-26 08:50:53 +030068 reg32 = pci_read_config32(PCH_ME_DEV, PCI_ME_HFS);
Stefan Reinauer8e073822012-04-04 00:07:22 +020069 memcpy(&hfs, &reg32, sizeof(u32));
70
71 /* Abort and leave device alone if not normal mode */
72 if (hfs.fpt_bad ||
73 hfs.working_state != ME_HFS_CWS_NORMAL ||
74 hfs.operation_mode != ME_HFS_MODE_NORMAL)
75 return;
76
77 /* Try to send EOP command so ME stops accepting other commands */
78 mkhi_end_of_post();
79
80 /* Make sure IO is disabled */
Angel Ponsc803f652020-06-07 22:09:01 +020081 pci_and_config16(PCH_ME_DEV, PCI_COMMAND,
82 ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO));
Stefan Reinauer8e073822012-04-04 00:07:22 +020083
84 /* Hide the PCI device */
85 RCBA32_OR(FD2, PCH_DISABLE_MEI1);
86}
87
Stefan Reinauer998f3a22012-06-11 15:15:46 -070088void intel_me_finalize_smm(void)
89{
Angel Ponsa7db40e2020-09-10 12:51:38 +020090 u16 did = pci_read_config16(PCH_ME_DEV, PCI_DEVICE_ID);
Stefan Reinauer998f3a22012-06-11 15:15:46 -070091 switch (did) {
Angel Ponsa7db40e2020-09-10 12:51:38 +020092 case 0x1c3a:
Stefan Reinauer998f3a22012-06-11 15:15:46 -070093 intel_me7_finalize_smm();
94 break;
Angel Ponsa7db40e2020-09-10 12:51:38 +020095 case 0x1e3a:
Stefan Reinauer998f3a22012-06-11 15:15:46 -070096 intel_me8_finalize_smm();
97 break;
98 default:
Angel Ponsa7db40e2020-09-10 12:51:38 +020099 printk(BIOS_ERR, "No finalize handler for ME %04x.\n", did);
Stefan Reinauer998f3a22012-06-11 15:15:46 -0700100 }
101}
Kyösti Mälkki21d6a272019-11-05 18:50:38 +0200102
103#else
Stefan Reinauer8e073822012-04-04 00:07:22 +0200104
105/* Determine the path that we should take based on ME status */
Elyes HAOUASdc035282018-09-18 13:28:49 +0200106static me_bios_path intel_me_path(struct device *dev)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200107{
108 me_bios_path path = ME_DISABLE_BIOS_PATH;
109 struct me_hfs hfs;
110 struct me_gmes gmes;
111
Stefan Reinauer8e073822012-04-04 00:07:22 +0200112 /* S3 wake skips all MKHI messages */
Kyösti Mälkkic3ed8862014-06-19 19:50:51 +0300113 if (acpi_is_wakeup_s3())
Stefan Reinauer8e073822012-04-04 00:07:22 +0200114 return ME_S3WAKE_BIOS_PATH;
Stefan Reinauer8e073822012-04-04 00:07:22 +0200115
116 pci_read_dword_ptr(dev, &hfs, PCI_ME_HFS);
117 pci_read_dword_ptr(dev, &gmes, PCI_ME_GMES);
118
119 /* Check and dump status */
120 intel_me_status(&hfs, &gmes);
121
Stefan Reinauer8e073822012-04-04 00:07:22 +0200122 /* Check Current Working State */
123 switch (hfs.working_state) {
124 case ME_HFS_CWS_NORMAL:
125 path = ME_NORMAL_BIOS_PATH;
126 break;
127 case ME_HFS_CWS_REC:
128 path = ME_RECOVERY_BIOS_PATH;
129 break;
130 default:
131 path = ME_DISABLE_BIOS_PATH;
132 break;
133 }
134
135 /* Check Current Operation Mode */
136 switch (hfs.operation_mode) {
137 case ME_HFS_MODE_NORMAL:
138 break;
139 case ME_HFS_MODE_DEBUG:
140 case ME_HFS_MODE_DIS:
141 case ME_HFS_MODE_OVER_JMPR:
142 case ME_HFS_MODE_OVER_MEI:
143 default:
144 path = ME_DISABLE_BIOS_PATH;
145 break;
146 }
147
Duncan Laurie5c88c6f2012-09-01 14:00:23 -0700148 /* Check for any error code and valid firmware */
149 if (hfs.error_code || hfs.fpt_bad)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200150 path = ME_ERROR_BIOS_PATH;
151
Kyösti Mälkkibe5317f2019-11-06 12:07:21 +0200152 if (CONFIG(ELOG) && path != ME_NORMAL_BIOS_PATH) {
Duncan Laurie5c88c6f2012-09-01 14:00:23 -0700153 struct elog_event_data_me_extended data = {
154 .current_working_state = hfs.working_state,
155 .operation_state = hfs.operation_state,
156 .operation_mode = hfs.operation_mode,
157 .error_code = hfs.error_code,
158 .progress_code = gmes.progress_code,
159 .current_pmevent = gmes.current_pmevent,
160 .current_state = gmes.current_state,
161 };
162 elog_add_event_byte(ELOG_TYPE_MANAGEMENT_ENGINE, path);
163 elog_add_event_raw(ELOG_TYPE_MANAGEMENT_ENGINE_EXT,
164 &data, sizeof(data));
165 }
Duncan Laurie5c88c6f2012-09-01 14:00:23 -0700166
Stefan Reinauer8e073822012-04-04 00:07:22 +0200167 return path;
168}
169
Angel Ponsc94bc8e2021-01-27 12:17:33 +0100170/* Get ME firmware version */
171static int mkhi_get_fw_version(void)
172{
173 struct me_fw_version version;
174 struct mkhi_header mkhi = {
175 .group_id = MKHI_GROUP_ID_GEN,
176 .command = MKHI_GET_FW_VERSION,
177 };
178 struct mei_header mei = {
179 .is_complete = 1,
180 .host_address = MEI_HOST_ADDRESS,
181 .client_address = MEI_ADDRESS_MKHI,
182 .length = sizeof(mkhi),
183 };
184
185 /* Send request and wait for response */
186 if (mei_sendrecv(&mei, &mkhi, NULL, &version, sizeof(version)) < 0) {
187 printk(BIOS_ERR, "ME: GET FW VERSION message failed\n");
188 return -1;
189 }
190
191 printk(BIOS_INFO, "ME: Firmware Version %u.%u.%u.%u (code) "
192 "%u.%u.%u.%u (recovery)\n",
193 version.code_major, version.code_minor,
194 version.code_build_number, version.code_hot_fix,
195 version.recovery_major, version.recovery_minor,
196 version.recovery_build_number, version.recovery_hot_fix);
197
198 return 0;
199}
200
201static inline void print_cap(const char *name, int state)
202{
203 printk(BIOS_DEBUG, "ME Capability: %-30s : %sabled\n",
204 name, state ? "en" : "dis");
205}
206
207/* Get ME Firmware Capabilities */
208static int mkhi_get_fwcaps(void)
209{
210 u32 rule_id = 0;
211 struct me_fwcaps cap;
212 struct mkhi_header mkhi = {
213 .group_id = MKHI_GROUP_ID_FWCAPS,
214 .command = MKHI_FWCAPS_GET_RULE,
215 };
216 struct mei_header mei = {
217 .is_complete = 1,
218 .host_address = MEI_HOST_ADDRESS,
219 .client_address = MEI_ADDRESS_MKHI,
220 .length = sizeof(mkhi) + sizeof(rule_id),
221 };
222
223 /* Send request and wait for response */
224 if (mei_sendrecv(&mei, &mkhi, &rule_id, &cap, sizeof(cap)) < 0) {
225 printk(BIOS_ERR, "ME: GET FWCAPS message failed\n");
226 return -1;
227 }
228
229 print_cap("Full Network manageability", cap.caps_sku.full_net);
230 print_cap("Regular Network manageability", cap.caps_sku.std_net);
231 print_cap("Manageability", cap.caps_sku.manageability);
232 print_cap("Small business technology", cap.caps_sku.small_business);
233 print_cap("Level III manageability", cap.caps_sku.l3manageability);
234 print_cap("IntelR Anti-Theft (AT)", cap.caps_sku.intel_at);
235 print_cap("IntelR Capability Licensing Service (CLS)",
236 cap.caps_sku.intel_cls);
237 print_cap("IntelR Power Sharing Technology (MPC)",
238 cap.caps_sku.intel_mpc);
239 print_cap("ICC Over Clocking", cap.caps_sku.icc_over_clocking);
240 print_cap("Protected Audio Video Path (PAVP)", cap.caps_sku.pavp);
241 print_cap("IPV6", cap.caps_sku.ipv6);
242 print_cap("KVM Remote Control (KVM)", cap.caps_sku.kvm);
243 print_cap("Outbreak Containment Heuristic (OCH)", cap.caps_sku.och);
244 print_cap("Virtual LAN (VLAN)", cap.caps_sku.vlan);
245 print_cap("TLS", cap.caps_sku.tls);
246 print_cap("Wireless LAN (WLAN)", cap.caps_sku.wlan);
247
248 return 0;
249}
250
251
Stefan Reinauer8e073822012-04-04 00:07:22 +0200252/* Check whether ME is present and do basic init */
Elyes HAOUASdc035282018-09-18 13:28:49 +0200253static void intel_me_init(struct device *dev)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200254{
255 me_bios_path path = intel_me_path(dev);
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300256 u8 me_state = 0, me_state_prev = 0;
257 bool need_reset = false;
258 struct me_hfs hfs;
Stefan Reinauer8e073822012-04-04 00:07:22 +0200259
260 /* Do initial setup and determine the BIOS path */
Angel Pons2e29c3b2020-08-10 15:47:28 +0200261 printk(BIOS_NOTICE, "ME: BIOS path: %s\n", me_get_bios_path_string(path));
Stefan Reinauer8e073822012-04-04 00:07:22 +0200262
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300263 get_option(&me_state, "me_state");
264 get_option(&me_state_prev, "me_state_prev");
265
266 printk(BIOS_DEBUG, "ME: me_state=%u, me_state_prev=%u\n", me_state, me_state_prev);
267
Stefan Reinauer8e073822012-04-04 00:07:22 +0200268 switch (path) {
269 case ME_S3WAKE_BIOS_PATH:
James Yea85d4a52020-02-22 20:30:49 +1100270#if CONFIG(HIDE_MEI_ON_ERROR)
271 case ME_ERROR_BIOS_PATH:
272#endif
Stefan Reinauer8e073822012-04-04 00:07:22 +0200273 intel_me_hide(dev);
274 break;
275
276 case ME_NORMAL_BIOS_PATH:
277 /* Validate the extend register */
278 if (intel_me_extend_valid(dev) < 0)
279 break; /* TODO: force recovery mode */
280
281 /* Prepare MEI MMIO interface */
282 if (intel_mei_setup(dev) < 0)
283 break;
284
Kyösti Mälkkic86fc8e2019-11-06 06:32:27 +0200285 if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) {
286 /* Print ME firmware version */
287 mkhi_get_fw_version();
288 /* Print ME firmware capabilities */
289 mkhi_get_fwcaps();
290 }
Stefan Reinauer8e073822012-04-04 00:07:22 +0200291
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300292 /* Put ME in Software Temporary Disable Mode, if needed */
293 if (me_state == CMOS_ME_STATE_DISABLED
294 && CMOS_ME_STATE(me_state_prev) == CMOS_ME_STATE_NORMAL) {
295 printk(BIOS_INFO, "ME: disabling ME\n");
296 if (enter_soft_temp_disable()) {
297 enter_soft_temp_disable_wait();
298 need_reset = true;
299 } else {
300 printk(BIOS_ERR, "ME: failed to enter Soft Temporary Disable mode\n");
301 }
302
303 break;
304 }
305
Stefan Reinauer8e073822012-04-04 00:07:22 +0200306 /*
307 * Leave the ME unlocked in this path.
308 * It will be locked via SMI command later.
309 */
310 break;
311
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300312 case ME_DISABLE_BIOS_PATH:
313 /* Bring ME out of Soft Temporary Disable mode, if needed */
314 pci_read_dword_ptr(dev, &hfs, PCI_ME_HFS);
315 if (hfs.operation_mode == ME_HFS_MODE_DIS
316 && me_state == CMOS_ME_STATE_NORMAL
317 && (CMOS_ME_STATE(me_state_prev) == CMOS_ME_STATE_DISABLED
318 || !CMOS_ME_CHANGED(me_state_prev))) {
319 printk(BIOS_INFO, "ME: re-enabling ME\n");
320
321 exit_soft_temp_disable(dev);
322 exit_soft_temp_disable_wait(dev);
323
324 /*
325 * ME starts loading firmware immediately after writing to H_GS,
326 * but Lenovo BIOS performs a reboot after bringing ME back to
327 * Normal mode. Assume that global reset is needed.
328 */
329 need_reset = true;
330 } else {
331 intel_me_hide(dev);
332 }
333 break;
334
James Yea85d4a52020-02-22 20:30:49 +1100335#if !CONFIG(HIDE_MEI_ON_ERROR)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200336 case ME_ERROR_BIOS_PATH:
James Yea85d4a52020-02-22 20:30:49 +1100337#endif
Stefan Reinauer8e073822012-04-04 00:07:22 +0200338 case ME_RECOVERY_BIOS_PATH:
Stefan Reinauer8e073822012-04-04 00:07:22 +0200339 case ME_FIRMWARE_UPDATE_BIOS_PATH:
Stefan Reinauer8e073822012-04-04 00:07:22 +0200340 break;
341 }
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300342
343 /* To avoid boot loops if ME fails to get back from disabled mode,
344 set the 'changed' bit here. */
345 if (me_state != CMOS_ME_STATE(me_state_prev) || need_reset) {
346 u8 new_state = me_state | CMOS_ME_STATE_CHANGED;
347 set_option("me_state_prev", &new_state);
348 }
349
350 if (need_reset) {
351 set_global_reset(true);
352 full_reset();
353 }
Stefan Reinauer8e073822012-04-04 00:07:22 +0200354}
355
Stefan Reinauer8e073822012-04-04 00:07:22 +0200356static struct device_operations device_ops = {
357 .read_resources = pci_dev_read_resources,
358 .set_resources = pci_dev_set_resources,
359 .enable_resources = pci_dev_enable_resources,
360 .init = intel_me_init,
Angel Pons1fc0edd2020-05-31 00:03:28 +0200361 .ops_pci = &pci_dev_ops_pci,
Stefan Reinauer8e073822012-04-04 00:07:22 +0200362};
363
364static const struct pci_driver intel_me __pci_driver = {
365 .ops = &device_ops,
366 .vendor = PCI_VENDOR_ID_INTEL,
367 .device = 0x1c3a,
368};
369
Kyösti Mälkki21d6a272019-11-05 18:50:38 +0200370#endif /* __SIMPLE_DEVICE__ */