commonlib/coreboot_tables.h: Fix typo in enum type name
[coreboot.git] / util / intelmetool / me.c
blob72430afc907a850ab08b0cf4eb523b46f5711651
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <pci/pci.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <sys/io.h>
8 #include <assert.h>
9 #include <unistd.h>
11 #include "intelmetool.h"
12 #include "me.h"
13 #include "mmap.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; */
39 switch (offset) {
40 case MEI_H_CSR:
41 case MEI_ME_CSR_HA:
43 csr = ptr;
44 if (!csr) {
45 printf("%-9s[%02x] : ", type, offset);
46 printf("ERROR: 0x%08x\n", dword);
47 break;
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);
57 break;
58 case MEI_ME_CB_RW:
59 case MEI_H_CB_WW:
60 printf("%-9s[%02x] : ", type, offset);
61 printf("CB: 0x%08x\n", dword);
62 break;
63 default:
64 printf("%-9s[%02x] : ", type, offset);
65 printf("0x%08x\n", offset);
66 break;
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));
79 if (debug) {
80 mei_dump(ptr, dword, offset, "READ");
84 static inline void mei_write_dword_ptr(void *ptr, uint32_t offset)
86 uint32_t dword = 0;
87 memcpy(&dword, ptr, sizeof(dword));
88 write32(mei_mmap, offset, dword);
90 if (debug) {
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));
100 if (debug) {
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);
124 if (debug) {
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);
133 if (debug) {
134 mei_dump(NULL, dword, MEI_ME_CB_RW, "READ");
137 return dword;
140 /* Wait for ME ready bit to be asserted */
141 static int mei_wait_for_me_ready(void)
143 struct mei_csr me;
144 unsigned try = ME_RETRY;
146 while (try--) {
147 read_me_csr(&me);
148 if (me.ready)
149 return 0;
150 usleep(ME_DELAY);
153 printf("ME: failed to become ready\n");
154 return -1;
157 void mei_reset(void)
159 struct mei_csr host;
161 if (mei_wait_for_me_ready() < 0)
162 return;
164 /* Reset host and ME circular buffers for next message */
165 read_host_csr(&host);
166 host.reset = 1;
167 host.interrupt_generate = 1;
168 write_host_csr(&host);
170 if (mei_wait_for_me_ready() < 0)
171 return;
173 /* Re-init and indicate host is ready */
174 read_host_csr(&host);
175 host.interrupt_generate = 1;
176 host.ready = 1;
177 host.reset = 0;
178 write_host_csr(&host);
181 static int mei_send_msg(struct mei_header *mei, struct mkhi_header *mkhi,
182 void *req_data)
184 struct mei_csr host;
185 unsigned ndata , n;
186 uint32_t *data;
188 /* Number of dwords to write, ignoring MKHI */
189 ndata = (mei->length) >> 2;
191 /* Pad non-dword aligned request message length */
192 if (mei->length & 3)
193 ndata++;
194 if (!ndata) {
195 printf("ME: request does not include MKHI\n");
196 return -1;
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");
207 mei_reset();
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);
219 return -1;
222 /* Write MEI header */
223 mei_write_dword_ptr(mei, MEI_H_CB_WW);
224 ndata--;
226 /* Write MKHI header */
227 mei_write_dword_ptr(mkhi, MEI_H_CB_WW);
228 ndata--;
230 /* Write message data */
231 data = req_data;
232 for (n = 0; n < ndata; ++n)
233 write_cb(*data++);
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;
250 unsigned ndata, n;
251 unsigned expected;
252 uint32_t *data;
254 /* Total number of dwords to read from circular buffer */
255 expected = (rsp_bytes + sizeof(mei_rsp) + sizeof(mkhi_rsp)) >> 2;
256 if (rsp_bytes & 3)
257 expected++;
259 if (debug) {
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) {
268 read_me_csr(&me);
269 if ((me.buffer_write_ptr - me.buffer_read_ptr) >= expected)
270 //if (me.interrupt_generate && !me.interrupt_status)
271 //if (me.interrupt_status)
272 break;
273 usleep(ME_DELAY);
275 if (!n) {
276 printf("ME: timeout waiting for data: expected "
277 "%u, available %u\n", expected,
278 me.buffer_write_ptr - me.buffer_read_ptr);
279 return -1;
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");
285 return -1;
288 /* Handle non-dword responses and expect at least MKHI header */
289 ndata = mei_rsp.length >> 2;
290 if (mei_rsp.length & 3)
291 ndata++;
292 if (ndata != (expected - 1)) { //XXX
293 printf("ME: response is missing data\n");
294 //return -1;
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);
306 //return -1;
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);
314 //return -1;
317 /* Read response data from the circular buffer */
318 data = rsp_data;
319 for (n = 0; n < ndata; ++n)
320 *data++ = read_cb();
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)
335 return -1;
336 if (mei_recv_msg(mei, mkhi, rsp_data, rsp_bytes) < 0)
337 return -1;
338 return 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 = {
350 .is_complete = 1,
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");
358 return -1;
361 printf("ME: END OF POST message successful\n");
362 return 0;
366 /* Get ME firmware version */
367 int mkhi_get_fw_version(int *major, int *minor)
369 uint32_t data = 0;
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,
375 .is_response = 0,
378 struct mei_header mei = {
379 .is_complete = 1,
380 .host_address = MEI_HOST_ADDRESS,
381 .client_address = MEI_ADDRESS_MKHI,
382 .length = sizeof(mkhi),
385 #ifndef OLDARC
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");
389 return -1;
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);
400 #else
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");
404 return -1;
406 printf("ME: Firmware Version %u.%u (code)\n\n",
407 version.code_major, version.code_minor);
408 #endif
409 if (major)
410 *major = version.code_major;
411 if (minor)
412 *minor = version.code_minor;
413 return 0;
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)
425 struct {
426 uint32_t rule_id;
427 uint32_t rule_len;
429 struct me_fwcaps cap;
430 } fwcaps;
432 fwcaps.rule_id = 0;
433 fwcaps.rule_len = 0;
435 struct mkhi_header mkhi = {
436 .group_id = MKHI_GROUP_ID_FWCAPS,
437 .command = MKHI_FWCAPS_GET_RULE,
438 .is_response = 0,
440 struct mei_header mei = {
441 .is_complete = 1,
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");
450 return -1;
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);
470 return 0;
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 = {
485 .is_complete = 1,
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... */
496 asm("hlt");
499 /* If the ME responded it rejected the reset request */
500 printf("ME: Global Reset failed\n");
501 return -1;
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,
512 .dimmnumber = 4,
514 struct mkhi_header mkhi = {
515 .group_id = MKHI_GROUP_ID_CBM,
516 .command = MKHI_THERMAL_REPORTING,
518 struct mei_header mei = {
519 .is_complete = 1,
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)
534 uint32_t data = 0;
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,
546 .is_response = 0,
548 struct mei_header mei = {
549 .is_complete = 1,
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) {
557 printf("failed\n");
558 return -1;
559 } else {
560 printf("done\n");
562 return 0;
565 /* Prepare ME for MEI messages */
566 uint32_t intel_mei_setup(struct pci_dev *dev)
568 struct mei_csr host;
569 uint16_t reg16;
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");
579 return 1;
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;
590 host.ready = 1;
591 host.reset = 0;
592 write_host_csr(&host);
594 return 0;
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};
602 int i, count = 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");
607 return -1;
610 if (!status.extend_reg_valid) {
611 printf("ME: Extend Register not valid\n");
612 return -1;
615 switch (status.extend_reg_algorithm) {
616 case PCI_ME_EXT_SHA1:
617 count = 5;
618 printf("ME: Extend SHA-1: ");
619 break;
620 case PCI_ME_EXT_SHA256:
621 count = 8;
622 printf("ME: Extend SHA-256: ");
623 break;
624 default:
625 printf("ME: Extend Algorithm %d unknown\n",
626 status.extend_reg_algorithm);
627 return -1;
630 for (i = 0; i < count; ++i) {
631 extend[i] = pci_read_long(dev, PCI_ME_HER(i));
632 printf("%08x", extend[i]);
634 printf("\n");
636 return 0;