1 /* SPDX-License-Identifier: GPL-2.0-only */
11 #include "intelmetool.h"
15 #define read32(addr, off) ( *((uint32_t *) (addr + off)) )
16 #define write32(addr, off, val) ( *((uint32_t *) (addr + off)) = val)
18 /* Path that the BIOS should take based on ME state */
20 static const char *me_bios_path_values[] = {
21 [ME_NORMAL_BIOS_PATH] = "Normal",
22 [ME_S3WAKE_BIOS_PATH] = "S3 Wake",
23 [ME_ERROR_BIOS_PATH] = "Error",
24 [ME_RECOVERY_BIOS_PATH] = "Recovery",
25 [ME_DISABLE_BIOS_PATH] = "Disable",
26 [ME_FIRMWARE_UPDATE_BIOS_PATH] = "Firmware Update",
30 /* MMIO base address for MEI interface */
31 static uint32_t mei_base_address
;
32 static uint8_t* mei_mmap
;
34 static void mei_dump(void *ptr
, int dword
, int offset
, const char *type
)
36 /* struct mei_csr *csr; */
45 printf("%-9s[%02x] : ", type, offset);
46 printf("ERROR: 0x%08x\n", dword);
49 printf("%-9s[%02x] : ", type, offset);
50 printf("depth=%u read=%02u write=%02u ready=%u "
51 "reset=%u intgen=%u intstatus=%u intenable=%u\n",
52 csr->buffer_depth, csr->buffer_read_ptr,
53 csr->buffer_write_ptr, csr->ready, csr->reset,
54 csr->interrupt_generate, csr->interrupt_status,
55 csr->interrupt_enable);
60 printf("%-9s[%02x] : ", type
, offset
);
61 printf("CB: 0x%08x\n", dword
);
64 printf("%-9s[%02x] : ", type
, offset
);
65 printf("0x%08x\n", offset
);
71 * ME/MEI access helpers using memcpy to avoid aliasing.
74 static inline void mei_read_dword_ptr(void *ptr
, uint32_t offset
)
76 uint32_t dword
= read32(mei_mmap
, offset
);
77 memcpy(ptr
, &dword
, sizeof(dword
));
80 mei_dump(ptr
, dword
, offset
, "READ");
84 static inline void mei_write_dword_ptr(void *ptr
, uint32_t offset
)
87 memcpy(&dword
, ptr
, sizeof(dword
));
88 write32(mei_mmap
, offset
, dword
);
91 mei_dump(ptr
, dword
, offset
, "WRITE");
95 static inline void pci_read_dword_ptr(struct pci_dev
*dev
, void *ptr
, uint32_t offset
)
97 uint32_t dword
= pci_read_long(dev
, offset
);
98 memcpy(ptr
, &dword
, sizeof(dword
));
101 mei_dump(ptr
, dword
, offset
, "PCI READ");
105 static inline void read_host_csr(struct mei_csr
*csr
)
107 mei_read_dword_ptr(csr
, MEI_H_CSR
);
110 static inline void write_host_csr(struct mei_csr
*csr
)
112 mei_write_dword_ptr(csr
, MEI_H_CSR
);
115 static inline void read_me_csr(struct mei_csr
*csr
)
117 mei_read_dword_ptr(csr
, MEI_ME_CSR_HA
);
120 static inline void write_cb(uint32_t dword
)
122 write32(mei_mmap
, MEI_H_CB_WW
, dword
);
125 mei_dump(NULL
, dword
, MEI_H_CB_WW
, "WRITE");
129 static inline uint32_t read_cb(void)
131 uint32_t dword
= read32(mei_mmap
, MEI_ME_CB_RW
);
134 mei_dump(NULL
, dword
, MEI_ME_CB_RW
, "READ");
140 /* Wait for ME ready bit to be asserted */
141 static int mei_wait_for_me_ready(void)
144 unsigned try = ME_RETRY
;
153 printf("ME: failed to become ready\n");
161 if (mei_wait_for_me_ready() < 0)
164 /* Reset host and ME circular buffers for next message */
165 read_host_csr(&host
);
167 host
.interrupt_generate
= 1;
168 write_host_csr(&host
);
170 if (mei_wait_for_me_ready() < 0)
173 /* Re-init and indicate host is ready */
174 read_host_csr(&host
);
175 host
.interrupt_generate
= 1;
178 write_host_csr(&host
);
181 static int mei_send_msg(struct mei_header
*mei
, struct mkhi_header
*mkhi
,
188 /* Number of dwords to write, ignoring MKHI */
189 ndata
= (mei
->length
) >> 2;
191 /* Pad non-dword aligned request message length */
195 printf("ME: request does not include MKHI\n");
198 ndata
++; /* Add MEI header */
201 * Make sure there is still room left in the circular buffer.
202 * Reset the buffer pointers if the requested message will not fit.
204 read_host_csr(&host
);
205 if ((host
.buffer_depth
- host
.buffer_write_ptr
) < ndata
) {
206 printf("ME: circular buffer full, resetting...\n");
208 read_host_csr(&host
);
212 * This implementation does not handle splitting large messages
213 * across multiple transactions. Ensure the requested length
214 * will fit in the available circular buffer depth.
216 if ((host
.buffer_depth
- host
.buffer_write_ptr
) < ndata
) {
217 printf("ME: message (%u) too large for buffer (%u)\n",
218 ndata
+ 2, host
.buffer_depth
);
222 /* Write MEI header */
223 mei_write_dword_ptr(mei
, MEI_H_CB_WW
);
226 /* Write MKHI header */
227 mei_write_dword_ptr(mkhi
, MEI_H_CB_WW
);
230 /* Write message data */
232 for (n
= 0; n
< ndata
; ++n
)
235 /* Generate interrupt to the ME */
236 read_host_csr(&host
);
237 host
.interrupt_generate
= 1;
238 write_host_csr(&host
);
240 /* Make sure ME is ready after sending request data */
241 return mei_wait_for_me_ready();
244 static int mei_recv_msg(struct mei_header
*mei
, struct mkhi_header
*mkhi
,
245 void *rsp_data
, uint32_t rsp_bytes
)
247 struct mei_header mei_rsp
;
248 struct mkhi_header mkhi_rsp
;
249 struct mei_csr me
, host
;
254 /* Total number of dwords to read from circular buffer */
255 expected
= (rsp_bytes
+ sizeof(mei_rsp
) + sizeof(mkhi_rsp
)) >> 2;
260 printf("expected u32 = %d\n", expected
);
263 * The interrupt status bit does not appear to indicate that the
264 * message has actually been received. Instead we wait until the
265 * expected number of dwords are present in the circular buffer.
267 for (n
= ME_RETRY
; n
; --n
) {
269 if ((me
.buffer_write_ptr
- me
.buffer_read_ptr
) >= expected
)
270 //if (me.interrupt_generate && !me.interrupt_status)
271 //if (me.interrupt_status)
276 printf("ME: timeout waiting for data: expected "
277 "%u, available %u\n", expected
,
278 me
.buffer_write_ptr
- me
.buffer_read_ptr
);
281 /* Read and verify MEI response header from the ME */
282 mei_read_dword_ptr(&mei_rsp
, MEI_ME_CB_RW
);
283 if (!mei_rsp
.is_complete
) {
284 printf("ME: response is not complete\n");
288 /* Handle non-dword responses and expect at least MKHI header */
289 ndata
= mei_rsp
.length
>> 2;
290 if (mei_rsp
.length
& 3)
292 if (ndata
!= (expected
- 1)) { //XXX
293 printf("ME: response is missing data\n");
297 /* Read and verify MKHI response header from the ME */
298 mei_read_dword_ptr(&mkhi_rsp
, MEI_ME_CB_RW
);
299 if (!mkhi_rsp
.is_response
||
300 mkhi
->group_id
!= mkhi_rsp
.group_id
||
301 mkhi
->command
!= mkhi_rsp
.command
) {
302 printf("ME: invalid response, group %u ?= %u, "
303 "command %u ?= %u, is_response %u\n", mkhi
->group_id
,
304 mkhi_rsp
.group_id
, mkhi
->command
, mkhi_rsp
.command
,
305 mkhi_rsp
.is_response
);
308 ndata
--; /* MKHI header has been read */
310 /* Make sure caller passed a buffer with enough space */
311 if (ndata
!= (rsp_bytes
>> 2)) {
312 printf("ME: not enough room in response buffer: "
313 "%u != %u\n", ndata
, rsp_bytes
>> 2);
317 /* Read response data from the circular buffer */
319 for (n
= 0; n
< ndata
; ++n
)
322 /* Tell the ME that we have consumed the response */
323 read_host_csr(&host
);
324 host
.interrupt_status
= 1;
325 host
.interrupt_generate
= 1;
326 write_host_csr(&host
);
328 return mei_wait_for_me_ready();
331 static inline int mei_sendrecv(struct mei_header
*mei
, struct mkhi_header
*mkhi
,
332 void *req_data
, void *rsp_data
, uint32_t rsp_bytes
)
334 if (mei_send_msg(mei
, mkhi
, req_data
) < 0)
336 if (mei_recv_msg(mei
, mkhi
, rsp_data
, rsp_bytes
) < 0)
341 /* Send END OF POST message to the ME */
343 static int mkhi_end_of_post(void)
345 struct mkhi_header mkhi = {
346 .group_id = MKHI_GROUP_ID_GEN,
347 .command = MKHI_END_OF_POST,
349 struct mei_header mei = {
351 .host_address = MEI_HOST_ADDRESS,
352 .client_address = MEI_ADDRESS_MKHI,
353 .length = sizeof(mkhi),
356 if (mei_sendrecv(&mei, &mkhi, NULL, NULL, 0) < 0) {
357 printf("ME: END OF POST message failed\n");
361 printf("ME: END OF POST message successful\n");
366 /* Get ME firmware version */
367 int mkhi_get_fw_version(int *major
, int *minor
)
370 struct me_fw_version version
= {0};
372 struct mkhi_header mkhi
= {
373 .group_id
= MKHI_GROUP_ID_GEN
,
374 .command
= GEN_GET_FW_VERSION
,
378 struct mei_header mei
= {
380 .host_address
= MEI_HOST_ADDRESS
,
381 .client_address
= MEI_ADDRESS_MKHI
,
382 .length
= sizeof(mkhi
),
386 /* Send request and wait for response */
387 if (mei_sendrecv(&mei
, &mkhi
, &data
, &version
, sizeof(version
) ) < 0) {
388 printf("ME: GET FW VERSION message failed\n");
391 printf("ME: Firmware Version %u.%u.%u.%u (code) "
392 "%u.%u.%u.%u (recovery) "
393 "%u.%u.%u.%u (fitc)\n\n",
394 version
.code_major
, version
.code_minor
,
395 version
.code_build_number
, version
.code_hot_fix
,
396 version
.recovery_major
, version
.recovery_minor
,
397 version
.recovery_build_number
, version
.recovery_hot_fix
,
398 version
.fitcmajor
, version
.fitcminor
,
399 version
.fitcbuildno
, version
.fitchotfix
);
401 /* Send request and wait for response */
402 if (mei_sendrecv(&mei
, &mkhi
, &data
, &version
, 2*sizeof(uint32_t) ) < 0) {
403 printf("ME: GET FW VERSION message failed\n");
406 printf("ME: Firmware Version %u.%u (code)\n\n",
407 version
.code_major
, version
.code_minor
);
410 *major
= version
.code_major
;
412 *minor
= version
.code_minor
;
416 static void print_cap(const char *name
, int state
)
418 printf("ME Capability: %-30s : %s\n",
419 name
, state
? CRED
"ON" RESET
: CGRN
"OFF" RESET
);
422 /* Get ME Firmware Capabilities */
423 int mkhi_get_fwcaps(void)
429 struct me_fwcaps cap
;
435 struct mkhi_header mkhi
= {
436 .group_id
= MKHI_GROUP_ID_FWCAPS
,
437 .command
= MKHI_FWCAPS_GET_RULE
,
440 struct mei_header mei
= {
442 .host_address
= MEI_HOST_ADDRESS
,
443 .client_address
= MEI_ADDRESS_MKHI
,
444 .length
= sizeof(mkhi
) + sizeof(fwcaps
.rule_id
),
447 /* Send request and wait for response */
448 if (mei_sendrecv(&mei
, &mkhi
, &fwcaps
.rule_id
, &fwcaps
.cap
, sizeof(fwcaps
.cap
)) < 0) {
449 printf("ME: GET FWCAPS message failed\n");
453 print_cap("Full Network manageability ", fwcaps
.cap
.caps_sku
.full_net
);
454 print_cap("Regular Network manageability ", fwcaps
.cap
.caps_sku
.std_net
);
455 print_cap("Manageability ", fwcaps
.cap
.caps_sku
.manageability
);
456 print_cap("Small business technology ", fwcaps
.cap
.caps_sku
.small_business
);
457 print_cap("Level III manageability ", fwcaps
.cap
.caps_sku
.l3manageability
);
458 print_cap("IntelR Anti-Theft (AT) ", fwcaps
.cap
.caps_sku
.intel_at
);
459 print_cap("IntelR Capability Licensing Service (CLS) ", fwcaps
.cap
.caps_sku
.intel_cls
);
460 print_cap("IntelR Power Sharing Technology (MPC) ", fwcaps
.cap
.caps_sku
.intel_mpc
);
461 print_cap("ICC Over Clocking ", fwcaps
.cap
.caps_sku
.icc_over_clocking
);
462 print_cap("Protected Audio Video Path (PAVP) ", fwcaps
.cap
.caps_sku
.pavp
);
463 print_cap("IPV6 ", fwcaps
.cap
.caps_sku
.ipv6
);
464 print_cap("KVM Remote Control (KVM) ", fwcaps
.cap
.caps_sku
.kvm
);
465 print_cap("Outbreak Containment Heuristic (OCH) ", fwcaps
.cap
.caps_sku
.och
);
466 print_cap("Virtual LAN (VLAN) ", fwcaps
.cap
.caps_sku
.vlan
);
467 print_cap("TLS ", fwcaps
.cap
.caps_sku
.tls
);
468 print_cap("Wireless LAN (WLAN) ", fwcaps
.cap
.caps_sku
.wlan
);
473 /* Tell ME to issue a global reset */
474 uint32_t mkhi_global_reset(void)
476 struct me_global_reset reset
= {
477 .request_origin
= GLOBAL_RESET_BIOS_POST
,
478 .reset_type
= CBM_RR_GLOBAL_RESET
,
480 struct mkhi_header mkhi
= {
481 .group_id
= MKHI_GROUP_ID_CBM
,
482 .command
= MKHI_GLOBAL_RESET
,
484 struct mei_header mei
= {
486 .length
= sizeof(mkhi
) + sizeof(reset
),
487 .host_address
= MEI_HOST_ADDRESS
,
488 .client_address
= MEI_ADDRESS_MKHI
,
491 printf("ME: Requesting global reset\n");
493 /* Send request and wait for response */
494 if (mei_sendrecv(&mei
, &mkhi
, &reset
, NULL
, 0) < 0) {
495 /* No response means reset will happen shortly... */
499 /* If the ME responded it rejected the reset request */
500 printf("ME: Global Reset failed\n");
504 /* Tell ME thermal reporting parameters */
506 void mkhi_thermal(void)
508 struct me_thermal_reporting thermal = {
509 .polling_timeout = 2,
510 .smbus_ec_msglen = 1,
511 .smbus_ec_msgpec = 0,
514 struct mkhi_header mkhi = {
515 .group_id = MKHI_GROUP_ID_CBM,
516 .command = MKHI_THERMAL_REPORTING,
518 struct mei_header mei = {
520 .length = sizeof(mkhi) + sizeof(thermal),
521 .host_address = MEI_HOST_ADDRESS,
522 .client_address = MEI_ADDRESS_THERMAL,
525 printf("ME: Sending thermal reporting params\n");
527 mei_sendrecv(&mei, &mkhi, &thermal, NULL, 0);
531 /* Enable debug of internal ME memory */
532 int mkhi_debug_me_memory(void *physaddr
)
536 /* copy whole ME memory to a readable space */
537 struct me_debug_mem memory
= {
538 .debug_phys
= (uintptr_t)physaddr
,
539 .debug_size
= 0x2000000,
540 .me_phys
= 0x20000000,
541 .me_size
= 0x2000000,
543 struct mkhi_header mkhi
= {
544 .group_id
= MKHI_GROUP_ID_GEN
,
545 .command
= GEN_SET_DEBUG_MEM
,
548 struct mei_header mei
= {
550 .length
= sizeof(mkhi
) + sizeof(memory
),
551 .host_address
= MEI_HOST_ADDRESS
,
552 .client_address
= MEI_ADDRESS_MKHI
,
555 printf("ME: Debug memory to 0x%zx ...", (size_t)physaddr
);
556 if (mei_sendrecv(&mei
, &mkhi
, &memory
, &data
, 0) < 0) {
565 /* Prepare ME for MEI messages */
566 uint32_t intel_mei_setup(struct pci_dev
*dev
)
570 uint32_t pagerounded
;
572 mei_base_address
= dev
->base_addr
[0] & ~0xf;
573 pagerounded
= mei_base_address
& ~0xfff;
574 mei_mmap
= map_physical(pagerounded
, 0x2000);
575 mei_mmap
+= mei_base_address
- pagerounded
;
576 if (mei_mmap
== NULL
) {
577 printf("Could not map ME setup memory.\n"
578 "Do you have kernel cmdline argument 'iomem=relaxed' set ?\n");
582 /* Ensure Memory and Bus Master bits are set */
583 reg16
= pci_read_word(dev
, PCI_COMMAND
);
584 reg16
|= PCI_COMMAND_MASTER
| PCI_COMMAND_MEMORY
;
585 pci_write_word(dev
, PCI_COMMAND
, reg16
);
587 /* Clean up status for next message */
588 read_host_csr(&host
);
589 host
.interrupt_generate
= 1;
592 write_host_csr(&host
);
597 /* Read the Extend register hash of ME firmware */
598 int intel_me_extend_valid(struct pci_dev
*dev
)
600 struct me_heres status
;
601 uint32_t extend
[8] = {0};
604 pci_read_dword_ptr(dev
, &status
, PCI_ME_HERES
);
605 if (!status
.extend_feature_present
) {
606 printf("ME: Extend Feature not present\n");
610 if (!status
.extend_reg_valid
) {
611 printf("ME: Extend Register not valid\n");
615 switch (status
.extend_reg_algorithm
) {
616 case PCI_ME_EXT_SHA1
:
618 printf("ME: Extend SHA-1: ");
620 case PCI_ME_EXT_SHA256
:
622 printf("ME: Extend SHA-256: ");
625 printf("ME: Extend Algorithm %d unknown\n",
626 status
.extend_reg_algorithm
);
630 for (i
= 0; i
< count
; ++i
) {
631 extend
[i
] = pci_read_long(dev
, PCI_ME_HER(i
));
632 printf("%08x", extend
[i
]);