1 /* SPDX-License-Identifier: GPL-2.0-only */
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.
11 #include <acpi/acpi.h>
12 #include <cf9_reset.h>
13 #include <device/mmio.h>
14 #include <device/device.h>
15 #include <device/pci.h>
16 #include <device/pci_ops.h>
17 #include <console/console.h>
18 #include <device/pci_ids.h>
22 #include <southbridge/intel/common/me.h>
27 static inline void print_cap(const char *name
, int state
)
29 printk(BIOS_DEBUG
, "ME Capability: %-41s : %sabled\n",
30 name
, state
? " en" : "dis");
33 static void me_print_fw_version(mbp_fw_version_name
*vers_name
)
35 if (!vers_name
->major_version
) {
36 printk(BIOS_ERR
, "ME: mbp missing version report\n");
40 printk(BIOS_DEBUG
, "ME: found version %d.%d.%d.%d\n",
41 vers_name
->major_version
, vers_name
->minor_version
,
42 vers_name
->hotfix_version
, vers_name
->build_version
);
45 /* Determine the path that we should take based on ME status */
46 static me_bios_path
intel_me_path(struct device
*dev
)
48 me_bios_path path
= ME_DISABLE_BIOS_PATH
;
52 /* S3 wake skips all MKHI messages */
53 if (acpi_is_wakeup_s3())
54 return ME_S3WAKE_BIOS_PATH
;
56 hfs
.raw
= pci_read_config32(dev
, PCI_ME_HFS
);
57 gmes
.raw
= pci_read_config32(dev
, PCI_ME_GMES
);
59 /* Check and dump status */
60 intel_me_status(&hfs
, &gmes
);
62 /* Check Current Working State */
63 switch (hfs
.working_state
) {
64 case ME_HFS_CWS_NORMAL
:
65 path
= ME_NORMAL_BIOS_PATH
;
68 path
= ME_RECOVERY_BIOS_PATH
;
71 path
= ME_DISABLE_BIOS_PATH
;
75 /* Check Current Operation Mode */
76 switch (hfs
.operation_mode
) {
77 case ME_HFS_MODE_NORMAL
:
79 case ME_HFS_MODE_DEBUG
:
81 case ME_HFS_MODE_OVER_JMPR
:
82 case ME_HFS_MODE_OVER_MEI
:
84 path
= ME_DISABLE_BIOS_PATH
;
88 /* Check for any error code and valid firmware and MBP */
89 if (hfs
.error_code
|| hfs
.fpt_bad
)
90 path
= ME_ERROR_BIOS_PATH
;
92 /* Check if the MBP is ready */
94 printk(BIOS_CRIT
, "%s: mbp is not ready!\n", __func__
);
95 path
= ME_ERROR_BIOS_PATH
;
98 if (CONFIG(ELOG
) && path
!= ME_NORMAL_BIOS_PATH
) {
99 struct elog_event_data_me_extended data
= {
100 .current_working_state
= hfs
.working_state
,
101 .operation_state
= hfs
.operation_state
,
102 .operation_mode
= hfs
.operation_mode
,
103 .error_code
= hfs
.error_code
,
104 .progress_code
= gmes
.progress_code
,
105 .current_pmevent
= gmes
.current_pmevent
,
106 .current_state
= gmes
.current_state
,
108 elog_add_event_byte(ELOG_TYPE_MANAGEMENT_ENGINE
, path
);
109 elog_add_event_raw(ELOG_TYPE_MANAGEMENT_ENGINE_EXT
,
110 &data
, sizeof(data
));
116 static int intel_me_read_mbp(me_bios_payload
*mbp_data
);
118 /* Get ME Firmware Capabilities */
119 static int mkhi_get_fwcaps(mefwcaps_sku
*cap
)
122 struct me_fwcaps cap_msg
;
123 struct mkhi_header mkhi
= {
124 .group_id
= MKHI_GROUP_ID_FWCAPS
,
125 .command
= MKHI_FWCAPS_GET_RULE
,
127 struct mei_header mei
= {
129 .host_address
= MEI_HOST_ADDRESS
,
130 .client_address
= MEI_ADDRESS_MKHI
,
131 .length
= sizeof(mkhi
) + sizeof(rule_id
),
134 /* Send request and wait for response */
135 if (mei_sendrecv(&mei
, &mkhi
, &rule_id
, &cap_msg
, sizeof(cap_msg
)) < 0) {
136 printk(BIOS_ERR
, "ME: GET FWCAPS message failed\n");
139 *cap
= cap_msg
.caps_sku
;
143 /* Get ME Firmware Capabilities */
144 static void me_print_fwcaps(mbp_fw_caps
*caps_section
)
146 mefwcaps_sku
*cap
= &caps_section
->fw_capabilities
;
147 if (!caps_section
->available
) {
148 printk(BIOS_ERR
, "ME: mbp missing fwcaps report\n");
149 if (mkhi_get_fwcaps(cap
))
153 print_cap("Full Network manageability", cap
->full_net
);
154 print_cap("Regular Network manageability", cap
->std_net
);
155 print_cap("Manageability", cap
->manageability
);
156 print_cap("Small business technology", cap
->small_business
);
157 print_cap("Level III manageability", cap
->l3manageability
);
158 print_cap("IntelR Anti-Theft (AT)", cap
->intel_at
);
159 print_cap("IntelR Capability Licensing Service (CLS)", cap
->intel_cls
);
160 print_cap("IntelR Power Sharing Technology (MPC)", cap
->intel_mpc
);
161 print_cap("ICC Over Clocking", cap
->icc_over_clocking
);
162 print_cap("Protected Audio Video Path (PAVP)", cap
->pavp
);
163 print_cap("IPV6", cap
->ipv6
);
164 print_cap("KVM Remote Control (KVM)", cap
->kvm
);
165 print_cap("Outbreak Containment Heuristic (OCH)", cap
->och
);
166 print_cap("Virtual LAN (VLAN)", cap
->vlan
);
167 print_cap("TLS", cap
->tls
);
168 print_cap("Wireless LAN (WLAN)", cap
->wlan
);
171 /* Check whether ME is present and do basic init */
172 static void intel_me_init(struct device
*dev
)
174 me_bios_path path
= intel_me_path(dev
);
175 me_bios_payload mbp_data
;
176 bool need_reset
= false;
179 /* Do initial setup and determine the BIOS path */
180 printk(BIOS_NOTICE
, "ME: BIOS path: %s\n", me_get_bios_path_string(path
));
182 u8 me_state
= get_uint_option("me_state", 0);
183 u8 me_state_prev
= get_uint_option("me_state_prev", 0);
185 printk(BIOS_DEBUG
, "ME: me_state=%u, me_state_prev=%u\n", me_state
, me_state_prev
);
188 case ME_S3WAKE_BIOS_PATH
:
189 #if CONFIG(HIDE_MEI_ON_ERROR)
190 case ME_ERROR_BIOS_PATH
:
195 case ME_NORMAL_BIOS_PATH
:
196 /* Validate the extend register */
197 if (intel_me_extend_valid(dev
) < 0)
198 break; /* TODO: force recovery mode */
200 /* Prepare MEI MMIO interface */
201 if (intel_mei_setup(dev
) < 0)
204 if (intel_me_read_mbp(&mbp_data
))
207 if (CONFIG_DEFAULT_CONSOLE_LOGLEVEL
>= BIOS_DEBUG
) {
208 me_print_fw_version(&mbp_data
.fw_version_name
);
209 me_print_fwcaps(&mbp_data
.fw_caps_sku
);
212 /* Put ME in Software Temporary Disable Mode, if needed */
213 if (me_state
== CMOS_ME_STATE_DISABLED
214 && CMOS_ME_STATE(me_state_prev
) == CMOS_ME_STATE_NORMAL
) {
215 printk(BIOS_INFO
, "ME: disabling ME\n");
216 if (enter_soft_temp_disable()) {
217 enter_soft_temp_disable_wait();
220 printk(BIOS_ERR
, "ME: failed to enter Soft Temporary Disable mode\n");
227 * Leave the ME unlocked in this path.
228 * It will be locked via SMI command later.
232 case ME_DISABLE_BIOS_PATH
:
233 /* Bring ME out of Soft Temporary Disable mode, if needed */
234 hfs
.raw
= pci_read_config32(dev
, PCI_ME_HFS
);
235 if (hfs
.operation_mode
== ME_HFS_MODE_DIS
236 && me_state
== CMOS_ME_STATE_NORMAL
237 && (CMOS_ME_STATE(me_state_prev
) == CMOS_ME_STATE_DISABLED
238 || !CMOS_ME_CHANGED(me_state_prev
))) {
239 printk(BIOS_INFO
, "ME: re-enabling ME\n");
241 exit_soft_temp_disable(dev
);
242 exit_soft_temp_disable_wait(dev
);
245 * ME starts loading firmware immediately after writing to H_GS,
246 * but Lenovo BIOS performs a reboot after bringing ME back to
247 * Normal mode. Assume that global reset is needed.
255 #if !CONFIG(HIDE_MEI_ON_ERROR)
256 case ME_ERROR_BIOS_PATH
:
258 case ME_RECOVERY_BIOS_PATH
:
259 case ME_FIRMWARE_UPDATE_BIOS_PATH
:
263 /* To avoid boot loops if ME fails to get back from disabled mode,
264 set the 'changed' bit here. */
265 if (me_state
!= CMOS_ME_STATE(me_state_prev
) || need_reset
) {
266 u8 new_state
= me_state
| CMOS_ME_STATE_CHANGED
;
267 set_uint_option("me_state_prev", new_state
);
271 set_global_reset(true);
276 static struct device_operations device_ops
= {
277 .read_resources
= pci_dev_read_resources
,
278 .set_resources
= pci_dev_set_resources
,
279 .enable_resources
= pci_dev_enable_resources
,
280 .init
= intel_me_init
,
281 .ops_pci
= &pci_dev_ops_pci
,
284 static const struct pci_driver intel_me __pci_driver
= {
286 .vendor
= PCI_VID_INTEL
,
290 /******************************************************************************
292 static u32
me_to_host_words_pending(void)
298 return (me
.buffer_write_ptr
- me
.buffer_read_ptr
) &
299 (me
.buffer_depth
- 1);
303 * mbp seems to be following its own flow, let's retrieve it in a dedicated
306 static int intel_me_read_mbp(me_bios_payload
*mbp_data
)
309 mbp_item_header mbp_item_hdr
;
314 me2host_pending
= me_to_host_words_pending();
315 if (!me2host_pending
) {
316 printk(BIOS_ERR
, "ME: no mbp data!\n");
320 /* we know for sure that at least the header is there */
321 mei_read_dword_ptr(&mbp_hdr
, MEI_ME_CB_RW
);
323 if ((mbp_hdr
.num_entries
> (mbp_hdr
.mbp_size
/ 2)) ||
324 (me2host_pending
< mbp_hdr
.mbp_size
)) {
325 printk(BIOS_ERR
, "ME: mbp of %d entries, total size %d words"
326 " buffer contains %d words\n",
327 mbp_hdr
.num_entries
, mbp_hdr
.mbp_size
,
333 memset(mbp_data
, 0, sizeof(*mbp_data
));
335 while (mbp_hdr
.num_entries
--) {
337 u32 copy_size
, buffer_room
;
340 if (!me2host_pending
) {
341 printk(BIOS_ERR
, "ME: no mbp data %d entries to go!\n",
342 mbp_hdr
.num_entries
+ 1);
346 mei_read_dword_ptr(&mbp_item_hdr
, MEI_ME_CB_RW
);
348 if (mbp_item_hdr
.length
> me2host_pending
) {
349 printk(BIOS_ERR
, "ME: insufficient mbp data %d "
351 mbp_hdr
.num_entries
+ 1);
355 me2host_pending
-= mbp_item_hdr
.length
;
357 mbp_item_id
= (((u32
)mbp_item_hdr
.item_id
) << 8) +
360 copy_size
= mbp_item_hdr
.length
- 1;
362 #define SET_UP_COPY(field) { copy_addr = (u32 *)&mbp_data->field; \
363 buffer_room = sizeof(mbp_data->field) / sizeof(u32); \
368 printk(BIOS_INFO
, "ME: MBP item header %8.8x\n", *((u32
*)p
));
370 switch (mbp_item_id
) {
372 SET_UP_COPY(fw_version_name
);
375 SET_UP_COPY(icc_profile
);
378 SET_UP_COPY(at_state
);
381 mbp_data
->fw_caps_sku
.available
= 1;
382 SET_UP_COPY(fw_caps_sku
.fw_capabilities
);
385 SET_UP_COPY(rom_bist_data
);
388 SET_UP_COPY(platform_key
);
391 mbp_data
->fw_plat_type
.available
= 1;
392 SET_UP_COPY(fw_plat_type
.rule_data
);
395 SET_UP_COPY(mfsintegrity
);
398 printk(BIOS_ERR
, "ME: unknown mbp item id 0x%x! Skipping\n",
405 if (buffer_room
!= copy_size
) {
406 printk(BIOS_ERR
, "ME: buffer room %d != %d copy size"
407 " for item 0x%x!!!\n",
408 buffer_room
, copy_size
, mbp_item_id
);
412 *copy_addr
++ = read_cb();
415 read_host_csr(&host
);
416 host
.interrupt_generate
= 1;
417 write_host_csr(&host
);
421 while (host
.interrupt_generate
) {
422 read_host_csr(&host
);
425 printk(BIOS_SPEW
, "ME: mbp read OK after %d cycles\n", cntr
);