blob: 0148905fd9eb4660abb06e4c5c61251621e20b88 [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
Stefan Reinauer8e073822012-04-04 00:07:22 +020030/* Determine the path that we should take based on ME status */
Elyes HAOUASdc035282018-09-18 13:28:49 +020031static me_bios_path intel_me_path(struct device *dev)
Stefan Reinauer8e073822012-04-04 00:07:22 +020032{
33 me_bios_path path = ME_DISABLE_BIOS_PATH;
Angel Pons3f7bb7d2021-01-27 13:03:20 +010034 union me_hfs hfs;
35 union me_gmes gmes;
Stefan Reinauer8e073822012-04-04 00:07:22 +020036
Stefan Reinauer8e073822012-04-04 00:07:22 +020037 /* S3 wake skips all MKHI messages */
Kyösti Mälkkic3ed8862014-06-19 19:50:51 +030038 if (acpi_is_wakeup_s3())
Stefan Reinauer8e073822012-04-04 00:07:22 +020039 return ME_S3WAKE_BIOS_PATH;
Stefan Reinauer8e073822012-04-04 00:07:22 +020040
Angel Pons3f7bb7d2021-01-27 13:03:20 +010041 hfs.raw = pci_read_config32(dev, PCI_ME_HFS);
42 gmes.raw = pci_read_config32(dev, PCI_ME_GMES);
Stefan Reinauer8e073822012-04-04 00:07:22 +020043
44 /* Check and dump status */
45 intel_me_status(&hfs, &gmes);
46
Stefan Reinauer8e073822012-04-04 00:07:22 +020047 /* Check Current Working State */
48 switch (hfs.working_state) {
49 case ME_HFS_CWS_NORMAL:
50 path = ME_NORMAL_BIOS_PATH;
51 break;
52 case ME_HFS_CWS_REC:
53 path = ME_RECOVERY_BIOS_PATH;
54 break;
55 default:
56 path = ME_DISABLE_BIOS_PATH;
57 break;
58 }
59
60 /* Check Current Operation Mode */
61 switch (hfs.operation_mode) {
62 case ME_HFS_MODE_NORMAL:
63 break;
64 case ME_HFS_MODE_DEBUG:
65 case ME_HFS_MODE_DIS:
66 case ME_HFS_MODE_OVER_JMPR:
67 case ME_HFS_MODE_OVER_MEI:
68 default:
69 path = ME_DISABLE_BIOS_PATH;
70 break;
71 }
72
Duncan Laurie5c88c6f2012-09-01 14:00:23 -070073 /* Check for any error code and valid firmware */
74 if (hfs.error_code || hfs.fpt_bad)
Stefan Reinauer8e073822012-04-04 00:07:22 +020075 path = ME_ERROR_BIOS_PATH;
76
Kyösti Mälkkibe5317f2019-11-06 12:07:21 +020077 if (CONFIG(ELOG) && path != ME_NORMAL_BIOS_PATH) {
Duncan Laurie5c88c6f2012-09-01 14:00:23 -070078 struct elog_event_data_me_extended data = {
79 .current_working_state = hfs.working_state,
80 .operation_state = hfs.operation_state,
81 .operation_mode = hfs.operation_mode,
82 .error_code = hfs.error_code,
83 .progress_code = gmes.progress_code,
84 .current_pmevent = gmes.current_pmevent,
85 .current_state = gmes.current_state,
86 };
87 elog_add_event_byte(ELOG_TYPE_MANAGEMENT_ENGINE, path);
88 elog_add_event_raw(ELOG_TYPE_MANAGEMENT_ENGINE_EXT,
89 &data, sizeof(data));
90 }
Duncan Laurie5c88c6f2012-09-01 14:00:23 -070091
Stefan Reinauer8e073822012-04-04 00:07:22 +020092 return path;
93}
94
Angel Ponsc94bc8e2021-01-27 12:17:33 +010095/* Get ME firmware version */
96static int mkhi_get_fw_version(void)
97{
98 struct me_fw_version version;
99 struct mkhi_header mkhi = {
100 .group_id = MKHI_GROUP_ID_GEN,
101 .command = MKHI_GET_FW_VERSION,
102 };
103 struct mei_header mei = {
104 .is_complete = 1,
105 .host_address = MEI_HOST_ADDRESS,
106 .client_address = MEI_ADDRESS_MKHI,
107 .length = sizeof(mkhi),
108 };
109
110 /* Send request and wait for response */
111 if (mei_sendrecv(&mei, &mkhi, NULL, &version, sizeof(version)) < 0) {
112 printk(BIOS_ERR, "ME: GET FW VERSION message failed\n");
113 return -1;
114 }
115
116 printk(BIOS_INFO, "ME: Firmware Version %u.%u.%u.%u (code) "
117 "%u.%u.%u.%u (recovery)\n",
118 version.code_major, version.code_minor,
119 version.code_build_number, version.code_hot_fix,
120 version.recovery_major, version.recovery_minor,
121 version.recovery_build_number, version.recovery_hot_fix);
122
123 return 0;
124}
125
126static inline void print_cap(const char *name, int state)
127{
128 printk(BIOS_DEBUG, "ME Capability: %-30s : %sabled\n",
129 name, state ? "en" : "dis");
130}
131
132/* Get ME Firmware Capabilities */
133static int mkhi_get_fwcaps(void)
134{
135 u32 rule_id = 0;
136 struct me_fwcaps cap;
137 struct mkhi_header mkhi = {
138 .group_id = MKHI_GROUP_ID_FWCAPS,
139 .command = MKHI_FWCAPS_GET_RULE,
140 };
141 struct mei_header mei = {
142 .is_complete = 1,
143 .host_address = MEI_HOST_ADDRESS,
144 .client_address = MEI_ADDRESS_MKHI,
145 .length = sizeof(mkhi) + sizeof(rule_id),
146 };
147
148 /* Send request and wait for response */
149 if (mei_sendrecv(&mei, &mkhi, &rule_id, &cap, sizeof(cap)) < 0) {
150 printk(BIOS_ERR, "ME: GET FWCAPS message failed\n");
151 return -1;
152 }
153
154 print_cap("Full Network manageability", cap.caps_sku.full_net);
155 print_cap("Regular Network manageability", cap.caps_sku.std_net);
156 print_cap("Manageability", cap.caps_sku.manageability);
157 print_cap("Small business technology", cap.caps_sku.small_business);
158 print_cap("Level III manageability", cap.caps_sku.l3manageability);
159 print_cap("IntelR Anti-Theft (AT)", cap.caps_sku.intel_at);
160 print_cap("IntelR Capability Licensing Service (CLS)",
161 cap.caps_sku.intel_cls);
162 print_cap("IntelR Power Sharing Technology (MPC)",
163 cap.caps_sku.intel_mpc);
164 print_cap("ICC Over Clocking", cap.caps_sku.icc_over_clocking);
165 print_cap("Protected Audio Video Path (PAVP)", cap.caps_sku.pavp);
166 print_cap("IPV6", cap.caps_sku.ipv6);
167 print_cap("KVM Remote Control (KVM)", cap.caps_sku.kvm);
168 print_cap("Outbreak Containment Heuristic (OCH)", cap.caps_sku.och);
169 print_cap("Virtual LAN (VLAN)", cap.caps_sku.vlan);
170 print_cap("TLS", cap.caps_sku.tls);
171 print_cap("Wireless LAN (WLAN)", cap.caps_sku.wlan);
172
173 return 0;
174}
175
176
Stefan Reinauer8e073822012-04-04 00:07:22 +0200177/* Check whether ME is present and do basic init */
Elyes HAOUASdc035282018-09-18 13:28:49 +0200178static void intel_me_init(struct device *dev)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200179{
180 me_bios_path path = intel_me_path(dev);
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300181 bool need_reset = false;
Angel Pons3f7bb7d2021-01-27 13:03:20 +0100182 union me_hfs hfs;
Stefan Reinauer8e073822012-04-04 00:07:22 +0200183
184 /* Do initial setup and determine the BIOS path */
Angel Pons2e29c3b2020-08-10 15:47:28 +0200185 printk(BIOS_NOTICE, "ME: BIOS path: %s\n", me_get_bios_path_string(path));
Stefan Reinauer8e073822012-04-04 00:07:22 +0200186
Angel Pons88dcb312021-04-26 17:10:28 +0200187 u8 me_state = get_uint_option("me_state", 0);
188 u8 me_state_prev = get_uint_option("me_state_prev", 0);
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300189
190 printk(BIOS_DEBUG, "ME: me_state=%u, me_state_prev=%u\n", me_state, me_state_prev);
191
Stefan Reinauer8e073822012-04-04 00:07:22 +0200192 switch (path) {
193 case ME_S3WAKE_BIOS_PATH:
James Yea85d4a52020-02-22 20:30:49 +1100194#if CONFIG(HIDE_MEI_ON_ERROR)
195 case ME_ERROR_BIOS_PATH:
196#endif
Stefan Reinauer8e073822012-04-04 00:07:22 +0200197 intel_me_hide(dev);
198 break;
199
200 case ME_NORMAL_BIOS_PATH:
201 /* Validate the extend register */
202 if (intel_me_extend_valid(dev) < 0)
203 break; /* TODO: force recovery mode */
204
205 /* Prepare MEI MMIO interface */
206 if (intel_mei_setup(dev) < 0)
207 break;
208
Kyösti Mälkkic86fc8e2019-11-06 06:32:27 +0200209 if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL >= BIOS_DEBUG) {
210 /* Print ME firmware version */
211 mkhi_get_fw_version();
212 /* Print ME firmware capabilities */
213 mkhi_get_fwcaps();
214 }
Stefan Reinauer8e073822012-04-04 00:07:22 +0200215
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300216 /* Put ME in Software Temporary Disable Mode, if needed */
217 if (me_state == CMOS_ME_STATE_DISABLED
218 && CMOS_ME_STATE(me_state_prev) == CMOS_ME_STATE_NORMAL) {
219 printk(BIOS_INFO, "ME: disabling ME\n");
220 if (enter_soft_temp_disable()) {
221 enter_soft_temp_disable_wait();
222 need_reset = true;
223 } else {
224 printk(BIOS_ERR, "ME: failed to enter Soft Temporary Disable mode\n");
225 }
226
227 break;
228 }
229
Stefan Reinauer8e073822012-04-04 00:07:22 +0200230 /*
231 * Leave the ME unlocked in this path.
232 * It will be locked via SMI command later.
233 */
234 break;
235
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300236 case ME_DISABLE_BIOS_PATH:
237 /* Bring ME out of Soft Temporary Disable mode, if needed */
Angel Pons3f7bb7d2021-01-27 13:03:20 +0100238 hfs.raw = pci_read_config32(dev, PCI_ME_HFS);
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300239 if (hfs.operation_mode == ME_HFS_MODE_DIS
240 && me_state == CMOS_ME_STATE_NORMAL
241 && (CMOS_ME_STATE(me_state_prev) == CMOS_ME_STATE_DISABLED
242 || !CMOS_ME_CHANGED(me_state_prev))) {
243 printk(BIOS_INFO, "ME: re-enabling ME\n");
244
245 exit_soft_temp_disable(dev);
246 exit_soft_temp_disable_wait(dev);
247
248 /*
249 * ME starts loading firmware immediately after writing to H_GS,
250 * but Lenovo BIOS performs a reboot after bringing ME back to
251 * Normal mode. Assume that global reset is needed.
252 */
253 need_reset = true;
254 } else {
255 intel_me_hide(dev);
256 }
257 break;
258
James Yea85d4a52020-02-22 20:30:49 +1100259#if !CONFIG(HIDE_MEI_ON_ERROR)
Stefan Reinauer8e073822012-04-04 00:07:22 +0200260 case ME_ERROR_BIOS_PATH:
James Yea85d4a52020-02-22 20:30:49 +1100261#endif
Stefan Reinauer8e073822012-04-04 00:07:22 +0200262 case ME_RECOVERY_BIOS_PATH:
Stefan Reinauer8e073822012-04-04 00:07:22 +0200263 case ME_FIRMWARE_UPDATE_BIOS_PATH:
Stefan Reinauer8e073822012-04-04 00:07:22 +0200264 break;
265 }
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300266
267 /* To avoid boot loops if ME fails to get back from disabled mode,
268 set the 'changed' bit here. */
269 if (me_state != CMOS_ME_STATE(me_state_prev) || need_reset) {
270 u8 new_state = me_state | CMOS_ME_STATE_CHANGED;
Angel Pons88dcb312021-04-26 17:10:28 +0200271 set_uint_option("me_state_prev", new_state);
Evgeny Zinoviev833e9ba2019-11-21 21:47:31 +0300272 }
273
274 if (need_reset) {
275 set_global_reset(true);
276 full_reset();
277 }
Stefan Reinauer8e073822012-04-04 00:07:22 +0200278}
279
Stefan Reinauer8e073822012-04-04 00:07:22 +0200280static struct device_operations device_ops = {
281 .read_resources = pci_dev_read_resources,
282 .set_resources = pci_dev_set_resources,
283 .enable_resources = pci_dev_enable_resources,
284 .init = intel_me_init,
Angel Pons1fc0edd2020-05-31 00:03:28 +0200285 .ops_pci = &pci_dev_ops_pci,
Stefan Reinauer8e073822012-04-04 00:07:22 +0200286};
287
288static const struct pci_driver intel_me __pci_driver = {
289 .ops = &device_ops,
290 .vendor = PCI_VENDOR_ID_INTEL,
291 .device = 0x1c3a,
292};