Philipp Deppenwiese (zaolin.daisuki@googlemail.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/14136
-gerrit
commit 972a07932b84506e4cc1595eb83970fb81a978eb Author: Philipp Deppenwiese zaolin@das-labor.org Date: Fri Mar 18 00:52:54 2016 +0100
util/intelmetool: Add intelmetool from Damien Zammit
The intelmetool shows information about the Intel Management Engine for different platforms.
Original source code can be found under following link: https://github.com/zamaudio/intelmetool.git
Change-Id: I0eb17833a21eb04cf9245a7312289a4102bec1a9 Signed-off-by: Philipp Deppenwiese zaolin@das-labor.org --- util/intelmetool/Makefile | 87 ++++++ util/intelmetool/intelmetool.c | 363 +++++++++++++++++++++++ util/intelmetool/intelmetool.h | 274 ++++++++++++++++++ util/intelmetool/me.c | 638 +++++++++++++++++++++++++++++++++++++++++ util/intelmetool/me.h | 409 ++++++++++++++++++++++++++ util/intelmetool/me_status.c | 205 +++++++++++++ util/intelmetool/mmap.c | 58 ++++ util/intelmetool/mmap.h | 27 ++ 8 files changed, 2061 insertions(+)
diff --git a/util/intelmetool/Makefile b/util/intelmetool/Makefile new file mode 100644 index 0000000..4461f86 --- /dev/null +++ b/util/intelmetool/Makefile @@ -0,0 +1,87 @@ +# intelmetool + +# Copyright (C) 2013-2015 Damien Zammit damien@zamaudio.com + +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +PROGRAM = intelmetool + +CC ?= gcc +INSTALL ?= /usr/bin/install +PREFIX ?= /usr/local +CFLAGS ?= -O0 -g -Wall -W -Wno-unused-parameter -Wno-unused-but-set-variable -Wno-sign-compare -Wno-unused-function +LDFLAGS += -lpci -lz + +OBJS = intelmetool.o me.o me_status.o mmap.o + +OS_ARCH = $(shell uname) +ifeq ($(OS_ARCH), Darwin) +LDFLAGS += -framework DirectHW +endif +ifeq ($(OS_ARCH), FreeBSD) +CFLAGS += -I/usr/local/include +LDFLAGS += -L/usr/local/lib +LIBS = -lz +endif +ifeq ($(OS_ARCH), NetBSD) +CFLAGS += -I/usr/pkg/include +LDFLAGS += -L/usr/pkg/lib -Wl,-rpath-link,/usr/pkg/lib -lz -lpciutils -lpci -l$(shell uname -p) +endif + +all: pciutils dep $(PROGRAM) + +$(PROGRAM): $(OBJS) + $(CC) $(CFLAGS) -o $(PROGRAM) $(OBJS) $(LDFLAGS) + +clean: + rm -f $(PROGRAM) *.o *~ junit.xml + +distclean: clean + rm -f .dependencies + +dep: + @$(CC) $(CFLAGS) -MM *.c > .dependencies + +define LIBPCI_TEST +/* Avoid a failing test due to libpci header symbol shadowing breakage */ +#define index shadow_workaround_index +#ifdef __NetBSD__ +#include <pciutils/pci.h> +#else +#include <pci/pci.h> +#endif +struct pci_access *pacc; +int main(int argc, char **argv) +{ + (void) argc; + (void) argv; + pacc = pci_alloc(); + return 0; +} +endef +export LIBPCI_TEST + +pciutils: + @printf "\nChecking for pciutils and zlib... " + @echo "$$LIBPCI_TEST" > .test.c + @$(CC) $(CFLAGS) .test.c -o .test $(LDFLAGS) >/dev/null 2>&1 && \ + printf "found.\n" || ( printf "not found.\n\n"; \ + printf "Please install pciutils-devel and zlib-devel.\n"; \ + rm -f .test.c .test; exit 1) + @rm -rf .test.c .test .test.dSYM + +install: $(PROGRAM) + mkdir -p $(DESTDIR)$(PREFIX)/sbin + $(INSTALL) $(PROGRAM) $(DESTDIR)$(PREFIX)/sbin + +.PHONY: all clean distclean dep pciutils + +-include .dependencies diff --git a/util/intelmetool/intelmetool.c b/util/intelmetool/intelmetool.c new file mode 100644 index 0000000..82fdee6 --- /dev/null +++ b/util/intelmetool/intelmetool.c @@ -0,0 +1,363 @@ +/* intelmetool Dump interesting things about Management Engine even if hidden + * Copyright (C) 2014 Damien Zammit damien@zamaudio.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <stdio.h> +#include <inttypes.h> +#include <stdlib.h> +#include <getopt.h> + +#ifdef __NetBSD__ +#include <machine/sysarch.h> +#endif + +#include "me.h" +#include "mmap.h" +#include "intelmetool.h" + +#define FD2 0x3428 +#define ME_COMMAND_DELAY 10000 + +extern int fd_mem; + +static uint32_t fd2 = 0; +static const int size = 0x4000; +static volatile uint8_t *rcba; + +void dumpmem(uint8_t *phys, uint32_t size) +{ + uint32_t i; + printf("Dumping cloned ME memory:\n"); + for (i = 0; i < size; i++) { + printf("%02X",*((uint8_t *) (phys + i))); + } + printf("\n"); +} + +void zeroit(uint8_t *phys, uint32_t size) +{ + uint32_t i; + for (i = 0; i < size; i++) { + *((uint8_t *) (phys + i)) = 0x00; + } +} + +void dumpmemfile(uint8_t *phys, uint32_t size) +{ + FILE *fp = fopen("medump.bin", "w"); + uint32_t i; + for (i = 0; i < size; i++) { + fprintf(fp, "%c", *((uint8_t *) (phys + i))); + } + fclose(fp); +} + +void rehide_me() { + if (fd2 & 0x2) { + printf("Re-hiding MEI device..."); + fd2 = *(uint32_t *)(rcba + FD2); + *(uint32_t *)(rcba + FD2) = fd2 | 0x2; + printf("done, "); + } +} + +/* You need >4GB total ram, in kernel cmdline, use 'mem=1000m' + * then this code will clone to absolute memory address 0xe0000000 + * which can be read using a mmap tool at that offset. + * Real ME memory is located around top of memory minus 64MB. (I think) + * so we avoid cloning to this part. + */ +void dump_me_memory() { + uint32_t me_clone = 0x60000000; + uint8_t *dump; + + dump = map_physical_exact(me_clone, me_clone, 0x2000000); + zeroit(dump, 0x2000000); + printf("Send magic command for memory clone\n"); + + mei_reset(); + udelay(ME_COMMAND_DELAY); + void* ptr = &me_clone; + int err = mkhi_debug_me_memory(ptr); + + if (!err) { + printf("Wait a second..."); + udelay(ME_COMMAND_DELAY); + printf("done\n\nHere are the first bytes:\n"); + dumpmemfile(dump, 0x2000000); + //printf("Try reading 0x%zx with other mmap tool...\n" + // "Press enter to quit, you only get one chance to run this tool before reboot required for some reason\n", me_clone); + while (getc(stdin) != '\n') {}; + unmap_physical(dump, 0x2000000); + } +} + +int pci_platform_scan() { + struct pci_access *pacc; + struct pci_dev *dev; + char namebuf[1024], *name; + + pacc = pci_alloc(); + pacc->method = PCI_ACCESS_I386_TYPE1; + + pci_init(pacc); + pci_scan_bus(pacc); + + for (dev=pacc->devices; dev; dev=dev->next) { + pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_SIZES | PCI_FILL_CLASS); + name = pci_lookup_name(pacc, namebuf, sizeof(namebuf), + PCI_LOOKUP_DEVICE, dev->vendor_id, dev->device_id); + if (dev->vendor_id == 0x8086) { + if (PCI_DEV_HAS_ME_DISABLE(dev->device_id)) { + printf(CGRN "Good news, you have a `%s` so ME is present but can be disabled, continuing...\n\n" RESET, name); + break; + } else if (PCI_DEV_HAS_ME_DIFFICULT(dev->device_id)) { + printf(CRED "Bad news, you have a `%s` so you have ME hardware on board and you can't control or disable it, continuing...\n\n" RESET, name); + break; + } else if (PCI_DEV_CAN_DISABLE_ME_IF_PRESENT(dev->device_id)) { + printf(CYEL "Not sure if ME hardware is present because you have a `%s`, but it is possible to disable it if you do, continuing...\n\n" RESET, name); + break; + } else if (PCI_DEV_ME_NOT_SURE(dev->device_id)) { + printf(CYEL "Found `%s`. Not sure whether you have ME hardware, exiting\n\n" RESET, name); + pci_cleanup(pacc); + return 1; + break; + } + } + } + + if (!PCI_DEV_HAS_ME_DISABLE(dev->device_id) && + !PCI_DEV_HAS_ME_DIFFICULT(dev->device_id) && + !PCI_DEV_CAN_DISABLE_ME_IF_PRESENT(dev->device_id) && + !PCI_DEV_ME_NOT_SURE(dev->device_id)) { + printf(CCYN "ME is not present on your board or unkown\n\n" RESET); + pci_cleanup(pacc); + return 1; + } + + pci_cleanup(pacc); + + return 0; +} + +struct pci_dev *pci_me_interface_scan(char **name) { + struct pci_access *pacc; + struct pci_dev *dev; + char namebuf[1024]; + + pacc = pci_alloc(); + pacc->method = PCI_ACCESS_I386_TYPE1; + + pci_init(pacc); + pci_scan_bus(pacc); + + for (dev=pacc->devices; dev; dev=dev->next) { + pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_SIZES | PCI_FILL_CLASS); + *name = pci_lookup_name(pacc, namebuf, sizeof(namebuf), + PCI_LOOKUP_DEVICE, dev->vendor_id, dev->device_id); + if (dev->vendor_id == 0x8086) { + if (PCI_DEV_HAS_SUPPORTED_ME(dev->device_id)) { + break; + } + } + } + + if (!PCI_DEV_HAS_SUPPORTED_ME(dev->device_id)) { + rehide_me(); + + printf("MEI device not found\n"); + pci_cleanup(pacc); + return NULL; + } + + return dev; +} + +int activate_me() { + struct pci_access *pacc; + struct pci_dev *sb; + uint32_t rcba_phys; + + pacc = pci_alloc(); + pacc->method = PCI_ACCESS_I386_TYPE1; + + pci_init(pacc); + pci_scan_bus(pacc); + + sb = pci_get_dev(pacc, 0, 0, 0x1f, 0); + if (!sb) { + printf("Uh oh, southbridge not on BDF(0:31:0), please report this error, exiting.\n"); + pci_cleanup(pacc); + return 1; + } + pci_fill_info(sb, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_SIZES | PCI_FILL_CLASS); + + rcba_phys = pci_read_long(sb, 0xf0) & 0xfffffffe; + rcba = map_physical(rcba_phys, size); + + //printf("RCBA at 0x%08" PRIx32 "\n", (uint32_t)rcba_phys); + fd2 = *(uint32_t *)(rcba + FD2); + *(uint32_t *)(rcba + FD2) = fd2 & ~0x2; + if (fd2 & 0x2) { + printf("MEI was hidden on PCI, now unlocked\n"); + } else { + printf("MEI not hidden on PCI, checking if visible\n"); + } + + pci_cleanup(pacc); + + return 0; +} + +void dump_me_info() { + struct pci_dev *dev; + uint32_t stat, stat2; + char *name; + + if (pci_platform_scan()) { + exit(1); + } + + dev = pci_me_interface_scan(&name); + if (!dev) { + exit(1); + } + + if (activate_me()) { + exit(1); + } + + printf("MEI found: [%x:%x] %s\n\n", dev->vendor_id, dev->device_id, name); + stat = pci_read_long(dev, 0x40); + printf("ME Status : 0x%x\n", stat); + stat2 = pci_read_long(dev, 0x48); + printf("ME Status 2 : 0x%x\n\n", stat2); + + intel_me_status(stat, stat2); + printf("\n"); + intel_me_extend_valid(dev); + printf("\n"); + + if ((stat & 0xf000) >> 12 != 0) { + printf("ME: has a broken implementation on your board with this BIOS\n"); + } + + intel_mei_setup(dev); + udelay(ME_COMMAND_DELAY); + mei_reset(); + udelay(ME_COMMAND_DELAY); + mkhi_get_fw_version(); + udelay(ME_COMMAND_DELAY); + mei_reset(); + udelay(ME_COMMAND_DELAY); + mkhi_get_fwcaps(); + udelay(ME_COMMAND_DELAY); + + intel_mei_unmap(); + rehide_me(); + + munmap((void*)rcba, size); +} + +void print_version(void) +{ + printf("intelmetool v%s -- ", INTELMETOOL_VERSION); + printf("Copyright (C) 2015 Damien Zammit\n\n"); + printf( + "This program is free software: you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation, version 2 of the License.\n\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n\n"); +} + +void print_usage(const char *name) +{ + printf("usage: %s [-vh?s]\n", name); + printf("\n" + " -v | --version: print the version\n" + " -h | --help: print this help\n\n" + " -s | --show: dump all me information on console\n" + "\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + int opt, option_index = 0; + unsigned cmd_exec = 0; + + static struct option long_options[] = { + {"version", 0, 0, 'v'}, + {"help", 0, 0, 'h'}, + {"show", 0, 0, 's'}, + {0, 0, 0, 0} + }; + + while ((opt = getopt_long(argc, argv, "vh?s", + long_options, &option_index)) != EOF) { + switch (opt) { + case 'v': + print_version(); + exit(0); + break; + case 's': + cmd_exec = 1; + break; + case 'h': + case '?': + default: + print_usage(argv[0]); + exit(0); + break; + } + } + + #if defined(__FreeBSD__) + if (open("/dev/io", O_RDWR) < 0) { + perror("/dev/io"); + #elif defined(__NetBSD__) + # ifdef __i386__ + if (i386_iopl(3)) { + perror("iopl"); + # else + if (x86_64_iopl(3)) { + perror("iopl"); + # endif + #else + if (iopl(3)) { + perror("iopl"); + #endif + printf("You need to be root.\n"); + exit(1); + } + + #ifndef __DARWIN__ + if ((fd_mem = open("/dev/mem", O_RDWR)) < 0) { + perror("Can not open /dev/mem"); + exit(1); + } + #endif + + switch(cmd_exec) { + case 1: + dump_me_info(); + break; + default: + print_usage(argv[0]); + break; + } + + return 0; +} diff --git a/util/intelmetool/intelmetool.h b/util/intelmetool/intelmetool.h new file mode 100644 index 0000000..6c1d52b --- /dev/null +++ b/util/intelmetool/intelmetool.h @@ -0,0 +1,274 @@ +/* + * intelmetool + * + * Copyright (C) 2008-2010 by coresystems GmbH + * Copyright (C) 2009 Carl-Daniel Hailfinger + * Copyright (C) 2015 Damien Zammit + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define ME_NOT_PRESENT 0 +#define ME_FOUND_NOTHING 1 +#define ME_FOUND_SOMETHING_NOT_SURE 2 +#define ME_CAN_DISABLE_IF_PRESENT 3 +#define ME_PRESENT_CAN_DISABLE 4 +#define ME_PRESENT_CANNOT_DISABLE 5 + +#define INTELMETOOL_VERSION "1.0" + +#if defined(__GLIBC__) +#include <sys/io.h> +#endif +#if (defined(__MACH__) && defined(__APPLE__)) +/* DirectHW is available here: http://www.coreboot.org/DirectHW */ +#define __DARWIN__ +#include <DirectHW/DirectHW.h> +#endif +#ifdef __NetBSD__ +#include <pciutils/pci.h> +#else +#include <pci/pci.h> +#endif + +#define CNRM "\x1B[0m" +#define CRED "\x1B[31m" +#define CGRN "\x1B[32m" +#define CYEL "\x1B[33m" +#define CBLU "\x1B[34m" +#define CMAG "\x1B[35m" +#define CCYN "\x1B[36m" +#define CWHT "\x1B[37m" +#define RESET "\033[0m" + +// Definitely has ME and can be disabled +#define PCI_DEVICE_ID_INTEL_ICH8ME 0x2811 +#define PCI_DEVICE_ID_INTEL_ICH9ME 0x2917 +#define PCI_DEVICE_ID_INTEL_ICH9M 0x2919 + +#define PCI_DEV_HAS_ME_DISABLE(x) ( \ + ( (x) == PCI_DEVICE_ID_INTEL_ICH8ME ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_ICH9ME ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_ICH9M )) + +// Definitely has ME and is very difficult to remove +#define PCI_DEVICE_ID_INTEL_ICH10R 0x3a16 +#define PCI_DEVICE_ID_INTEL_3400_DESKTOP 0x3b00 +#define PCI_DEVICE_ID_INTEL_3400_MOBILE 0x3b01 +#define PCI_DEVICE_ID_INTEL_P55 0x3b02 +#define PCI_DEVICE_ID_INTEL_PM55 0x3b03 +#define PCI_DEVICE_ID_INTEL_H55 0x3b06 +#define PCI_DEVICE_ID_INTEL_QM57 0x3b07 +#define PCI_DEVICE_ID_INTEL_H57 0x3b08 +#define PCI_DEVICE_ID_INTEL_HM55 0x3b09 +#define PCI_DEVICE_ID_INTEL_Q57 0x3b0a +#define PCI_DEVICE_ID_INTEL_HM57 0x3b0b +#define PCI_DEVICE_ID_INTEL_3400_MOBILE_SFF 0x3b0d +#define PCI_DEVICE_ID_INTEL_B55_A 0x3b0e +#define PCI_DEVICE_ID_INTEL_QS57 0x3b0f +#define PCI_DEVICE_ID_INTEL_3400 0x3b12 +#define PCI_DEVICE_ID_INTEL_3420 0x3b14 +#define PCI_DEVICE_ID_INTEL_3450 0x3b16 +#define PCI_DEVICE_ID_INTEL_B55_B 0x3b1e +#define PCI_DEVICE_ID_INTEL_Z68 0x1c44 +#define PCI_DEVICE_ID_INTEL_P67 0x1c46 +#define PCI_DEVICE_ID_INTEL_UM67 0x1c47 +#define PCI_DEVICE_ID_INTEL_HM65 0x1c49 +#define PCI_DEVICE_ID_INTEL_H67 0x1c4a +#define PCI_DEVICE_ID_INTEL_HM67 0x1c4b +#define PCI_DEVICE_ID_INTEL_Q65 0x1c4c +#define PCI_DEVICE_ID_INTEL_QS67 0x1c4d +#define PCI_DEVICE_ID_INTEL_Q67 0x1c4e +#define PCI_DEVICE_ID_INTEL_QM67 0x1c4f +#define PCI_DEVICE_ID_INTEL_B65 0x1c50 +#define PCI_DEVICE_ID_INTEL_C202 0x1c52 +#define PCI_DEVICE_ID_INTEL_C204 0x1c54 +#define PCI_DEVICE_ID_INTEL_C206 0x1c56 +#define PCI_DEVICE_ID_INTEL_H61 0x1c5c +#define PCI_DEVICE_ID_INTEL_Z77 0x1e44 +#define PCI_DEVICE_ID_INTEL_Z75 0x1e46 +#define PCI_DEVICE_ID_INTEL_Q77 0x1e47 +#define PCI_DEVICE_ID_INTEL_Q75 0x1e48 +#define PCI_DEVICE_ID_INTEL_B75 0x1e49 +#define PCI_DEVICE_ID_INTEL_H77 0x1e4a +#define PCI_DEVICE_ID_INTEL_C216 0x1e53 +#define PCI_DEVICE_ID_INTEL_QM77 0x1e55 +#define PCI_DEVICE_ID_INTEL_QS77 0x1e56 +#define PCI_DEVICE_ID_INTEL_HM77 0x1e57 +#define PCI_DEVICE_ID_INTEL_UM77 0x1e58 +#define PCI_DEVICE_ID_INTEL_HM76 0x1e59 +#define PCI_DEVICE_ID_INTEL_HM75 0x1e5d +#define PCI_DEVICE_ID_INTEL_HM70 0x1e5e +#define PCI_DEVICE_ID_INTEL_NM70 0x1e5f +#define PCI_DEVICE_ID_INTEL_QM87 0x8c4f +#define PCI_DEVICE_ID_INTEL_DH89XXCC 0x2310 +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_FULL 0x9c41 +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_PREM 0x9c43 +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_BASE 0x9c45 + +#define PCI_DEV_HAS_ME_DIFFICULT(x) ( \ + ( (x) == PCI_DEVICE_ID_INTEL_ICH10R ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_3400_DESKTOP ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_3400_MOBILE ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_P55 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_PM55 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_H55 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_QM57 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_H57 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_HM55 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_Q57 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_HM57 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_3400_MOBILE_SFF ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_B55_A ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_QS57 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_3400 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_3420 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_3450 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_B55_B ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_Z68 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_P67 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_UM67 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_HM65 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_H67 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_HM67 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_Q65 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_QS67 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_Q67 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_QM67 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_B65 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_C202 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_C204 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_C206 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_H61 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_Z77 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_Z75 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_Q77 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_Q75 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_B75 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_H77 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_C216 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_QM77 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_QS77 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_HM77 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_UM77 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_HM76 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_HM75 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_HM70 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_NM70 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_QM87 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_DH89XXCC ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_FULL ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_PREM ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_BASE )) + +// Not sure if ME present, but should be able to disable it easily +#define PCI_DEVICE_ID_INTEL_ICH8 0x2810 +#define PCI_DEVICE_ID_INTEL_ICH8M 0x2815 +#define PCI_DEVICE_ID_INTEL_ICH9DH 0x2912 +#define PCI_DEVICE_ID_INTEL_ICH9DO 0x2914 +#define PCI_DEVICE_ID_INTEL_ICH9R 0x2916 +#define PCI_DEVICE_ID_INTEL_ICH9 0x2918 + +#define PCI_DEV_CAN_DISABLE_ME_IF_PRESENT(x) ( \ + ( (x) == PCI_DEVICE_ID_INTEL_ICH8 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_ICH8M ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_ICH9DH ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_ICH9DO ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_ICH9R ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_ICH9 )) + +// Not sure at all +#define PCI_DEVICE_ID_INTEL_SCH_POULSBO_LPC 0x8119 +#define PCI_DEVICE_ID_INTEL_SCH_POULSBO 0x8100 + +#define PCI_DEV_ME_NOT_SURE(x) ( \ + ( (x) == PCI_DEVICE_ID_INTEL_SCH_POULSBO_LPC ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_SCH_POULSBO)) + +#define PCI_DEVICE_ID_INTEL_COUGARPOINT_1 0x1C3A /* Cougar Point */ +#define PCI_DEVICE_ID_INTEL_PATSBURG_1 0x1D3A /* C600/X79 Patsburg */ +#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_1 0x1CBA /* Panther Point */ +#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_2 0x1DBA /* Panther Point */ +#define PCI_DEVICE_ID_INTEL_PANTHERPOINT_3 0x1E3A /* Panther Point */ +#define PCI_DEVICE_ID_INTEL_CAVECREEK 0x2364 /* Cave Creek */ +#define PCI_DEVICE_ID_INTEL_BEARLAKE_1 0x28B4 /* Bearlake */ +#define PCI_DEVICE_ID_INTEL_BEARLAKE_2 0x28C4 /* Bearlake */ +#define PCI_DEVICE_ID_INTEL_BEARLAKE_3 0x28D4 /* Bearlake */ +#define PCI_DEVICE_ID_INTEL_BEARLAKE_4 0x28E4 /* Bearlake */ +#define PCI_DEVICE_ID_INTEL_BEARLAKE_5 0x28F4 /* Bearlake */ +#define PCI_DEVICE_ID_INTEL_82946GZ 0x2974 /* 82946GZ/GL */ +#define PCI_DEVICE_ID_INTEL_82G35 0x2984 /* 82G35 Express */ +#define PCI_DEVICE_ID_INTEL_82Q963 0x2994 /* 82Q963/Q965 */ +#define PCI_DEVICE_ID_INTEL_82P965 0x29A4 /* 82P965/G965 */ +#define PCI_DEVICE_ID_INTEL_82Q35 0x29B4 /* 82Q35 Express */ +#define PCI_DEVICE_ID_INTEL_82G33 0x29C4 /* 82G33/G31/P35/P31 Express */ +#define PCI_DEVICE_ID_INTEL_82Q33 0x29D4 /* 82Q33 Express */ +#define PCI_DEVICE_ID_INTEL_82X38 0x29E4 /* 82X38/X48 Express */ +#define PCI_DEVICE_ID_INTEL_3200 0x29F4 /* 3200/3210 Server */ +#define PCI_DEVICE_ID_INTEL_PM965 0x2A04 /* Mobile PM965/GM965 */ +#define PCI_DEVICE_ID_INTEL_GME965 0x2A14 /* Mobile GME965/GLE960 */ +#define PCI_DEVICE_ID_INTEL_CANTIGA_1 0x2A44 /* Cantiga */ +#define PCI_DEVICE_ID_INTEL_CANTIGA_2 0x2a50 /* Cantiga */ +#define PCI_DEVICE_ID_INTEL_CANTIGA_3 0x2A54 /* Cantiga */ +#define PCI_DEVICE_ID_INTEL_CANTIGA_4 0x2A64 /* Cantiga */ +#define PCI_DEVICE_ID_INTEL_CANTIGA_5 0x2A74 /* Cantiga */ +#define PCI_DEVICE_ID_INTEL_EAGLELAKE_1 0x2E04 /* Eaglelake */ +#define PCI_DEVICE_ID_INTEL_EAGLELAKE_2 0x2E14 /* Eaglelake */ +#define PCI_DEVICE_ID_INTEL_EAGLELAKE_3 0x2E24 /* Eaglelake */ +#define PCI_DEVICE_ID_INTEL_EAGLELAKE_4 0x2E34 /* Eaglelake */ +#define PCI_DEVICE_ID_INTEL_CALPELLA_1 0x3B64 /* Calpella */ +#define PCI_DEVICE_ID_INTEL_CALPELLA_2 0x3B65 /* Calpella */ +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_1 0x8C3A /* Lynx Point H */ +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_2 0x8CBA /* Lynx Point H Refresh */ +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_3 0x8D3A /* Lynx Point - Wellsburg */ +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_4 0x9C3A /* Lynx Point LP */ +#define PCI_DEVICE_ID_INTEL_WILDCAT_1 0x9CBA /* Wildcat Point LP */ +#define PCI_DEVICE_ID_INTEL_WILDCAT_2 0x9CBB /* Wildcat Point LP 2 */ + +#define PCI_DEV_HAS_SUPPORTED_ME(x) ( \ + ( (x) == PCI_DEVICE_ID_INTEL_COUGARPOINT_1 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_PATSBURG_1 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_PANTHERPOINT_1 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_PANTHERPOINT_2 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_PANTHERPOINT_3 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_CAVECREEK ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_BEARLAKE_1 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_BEARLAKE_2 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_BEARLAKE_3 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_BEARLAKE_4 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_BEARLAKE_5 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_82946GZ ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_82G35 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_82Q963 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_82P965 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_82Q35 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_82G33 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_82Q33 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_82X38 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_3200 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_PM965 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_GME965 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_CANTIGA_1 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_CANTIGA_2 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_CANTIGA_3 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_CANTIGA_4 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_CANTIGA_5 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_EAGLELAKE_1 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_EAGLELAKE_2 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_EAGLELAKE_3 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_EAGLELAKE_4 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_CALPELLA_1 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_CALPELLA_2 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_LYNXPOINT_1 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_LYNXPOINT_2 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_LYNXPOINT_3 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_LYNXPOINT_4 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_WILDCAT_1 ) || \ + ( (x) == PCI_DEVICE_ID_INTEL_WILDCAT_2)) diff --git a/util/intelmetool/me.c b/util/intelmetool/me.c new file mode 100644 index 0000000..cf85db7 --- /dev/null +++ b/util/intelmetool/me.c @@ -0,0 +1,638 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <pci/pci.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/io.h> +#include <assert.h> +#include "me.h" +#include "mmap.h" + +#define read32(addr, off) ( *((uint32_t *) (addr + off)) ) +#define write32(addr, off, val) ( *((uint32_t *) (addr + off)) = val) + +// FIXME: define this for old firmware only +//#define OLDARC 1 + +void udelay(uint32_t usecs) +{ + uint32_t i; + for(i = 0; i < usecs; i++) + inb(0x80); +} + +/* Path that the BIOS should take based on ME state */ +/* +static const char *me_bios_path_values[] = { + [ME_NORMAL_BIOS_PATH] = "Normal", + [ME_S3WAKE_BIOS_PATH] = "S3 Wake", + [ME_ERROR_BIOS_PATH] = "Error", + [ME_RECOVERY_BIOS_PATH] = "Recovery", + [ME_DISABLE_BIOS_PATH] = "Disable", + [ME_FIRMWARE_UPDATE_BIOS_PATH] = "Firmware Update", +}; +*/ + +/* MMIO base address for MEI interface */ +static uint32_t mei_base_address; +static uint8_t* mei_mmap; + +#if 1 +static void mei_dump(void *ptr, int dword, int offset, const char *type) +{ + struct mei_csr *csr; + + + switch (offset) { + case MEI_H_CSR: + case MEI_ME_CSR_HA: + csr = ptr; +/* if (!csr) { + printf("%-9s[%02x] : ", type, offset); + printf("ERROR: 0x%08x\n", dword); + break; + } + printf("%-9s[%02x] : ", type, offset); + printf("depth=%u read=%02u write=%02u ready=%u " + "reset=%u intgen=%u intstatus=%u intenable=%u\n", csr->buffer_depth, + csr->buffer_read_ptr, csr->buffer_write_ptr, + csr->ready, csr->reset, csr->interrupt_generate, + csr->interrupt_status, csr->interrupt_enable); +*/ break; + case MEI_ME_CB_RW: + case MEI_H_CB_WW: + printf("%-9s[%02x] : ", type, offset); + printf("CB: 0x%08x\n", dword); + break; + default: + printf("%-9s[%02x] : ", type, offset); + printf("0x%08x\n", offset); + break; + } +} +#else +# define mei_dump(ptr,dword,offset,type) do {} while (0) +#endif + +/* + * ME/MEI access helpers using memcpy to avoid aliasing. + */ + +static inline void mei_read_dword_ptr(void *ptr, uint32_t offset) +{ + uint32_t dword = read32(mei_mmap, offset); + memcpy(ptr, &dword, sizeof(dword)); + //mei_dump(ptr, dword, offset, "READ"); +} + +static inline void mei_write_dword_ptr(void *ptr, uint32_t offset) +{ + uint32_t dword = 0; + memcpy(&dword, ptr, sizeof(dword)); + write32(mei_mmap, offset, dword); + //mei_dump(ptr, dword, offset, "WRITE"); +} + +static inline void pci_read_dword_ptr(struct pci_dev *dev, void *ptr, uint32_t offset) +{ + uint32_t dword = pci_read_long(dev, offset); + memcpy(ptr, &dword, sizeof(dword)); + //mei_dump(ptr, dword, offset, "PCI READ"); +} + +static inline void read_host_csr(struct mei_csr *csr) +{ + mei_read_dword_ptr(csr, MEI_H_CSR); +} + +static inline void write_host_csr(struct mei_csr *csr) +{ + mei_write_dword_ptr(csr, MEI_H_CSR); +} + +static inline void read_me_csr(struct mei_csr *csr) +{ + mei_read_dword_ptr(csr, MEI_ME_CSR_HA); +} + +static inline void write_cb(uint32_t dword) +{ + write32(mei_mmap, MEI_H_CB_WW, dword); + //mei_dump(NULL, dword, MEI_H_CB_WW, "WRITE"); +} + +static inline uint32_t read_cb(void) +{ + uint32_t dword = read32(mei_mmap, MEI_ME_CB_RW); + //mei_dump(NULL, dword, MEI_ME_CB_RW, "READ"); + return dword; +} + +/* Wait for ME ready bit to be asserted */ +static int mei_wait_for_me_ready(void) +{ + struct mei_csr me; + unsigned try = ME_RETRY; + + while (try--) { + read_me_csr(&me); + if (me.ready) + return 0; + udelay(ME_DELAY); + } + + printf("ME: failed to become ready\n"); + return -1; +} + +void mei_reset(void) +{ + struct mei_csr host; + + if (mei_wait_for_me_ready() < 0) + return; + + /* Reset host and ME circular buffers for next message */ + read_host_csr(&host); + host.reset = 1; + host.interrupt_generate = 1; + write_host_csr(&host); + + if (mei_wait_for_me_ready() < 0) + return; + + /* Re-init and indicate host is ready */ + read_host_csr(&host); + host.interrupt_generate = 1; + host.ready = 1; + host.reset = 0; + write_host_csr(&host); +} + +static int mei_send_msg(struct mei_header *mei, struct mkhi_header *mkhi, + void *req_data) +{ + struct mei_csr host; + unsigned ndata , n; + uint32_t *data; + + /* Number of dwords to write, ignoring MKHI */ + ndata = (mei->length) >> 2; + + /* Pad non-dword aligned request message length */ + if (mei->length & 3) + ndata++; + if (!ndata) { + printf("ME: request does not include MKHI\n"); + return -1; + } + ndata++; /* Add MEI header */ + + /* + * Make sure there is still room left in the circular buffer. + * Reset the buffer pointers if the requested message will not fit. + */ + read_host_csr(&host); + if ((host.buffer_depth - host.buffer_write_ptr) < ndata) { + printf("ME: circular buffer full, resetting...\n"); + mei_reset(); + read_host_csr(&host); + } + + /* + * This implementation does not handle splitting large messages + * across multiple transactions. Ensure the requested length + * will fit in the available circular buffer depth. + */ + if ((host.buffer_depth - host.buffer_write_ptr) < ndata) { + printf("ME: message (%u) too large for buffer (%u)\n", + ndata + 2, host.buffer_depth); + return -1; + } + + /* Write MEI header */ + mei_write_dword_ptr(mei, MEI_H_CB_WW); + ndata--; + + /* Write MKHI header */ + mei_write_dword_ptr(mkhi, MEI_H_CB_WW); + ndata--; + + /* Write message data */ + data = req_data; + for (n = 0; n < ndata; ++n) + write_cb(*data++); + + /* Generate interrupt to the ME */ + read_host_csr(&host); + host.interrupt_generate = 1; + write_host_csr(&host); + + /* Make sure ME is ready after sending request data */ + return mei_wait_for_me_ready(); +} + +static int mei_recv_msg(struct mei_header *mei, struct mkhi_header *mkhi, + void *rsp_data, uint32_t rsp_bytes) +{ + struct mei_header mei_rsp; + struct mkhi_header mkhi_rsp; + struct mei_csr me, host; + unsigned ndata, n; + unsigned expected; + uint32_t *data; + + /* Total number of dwords to read from circular buffer */ + expected = (rsp_bytes + sizeof(mei_rsp) + sizeof(mkhi_rsp)) >> 2; + if (rsp_bytes & 3) + expected++; + + //printf("expected u32 = %d\n", expected); + /* + * The interrupt status bit does not appear to indicate that the + * message has actually been received. Instead we wait until the + * expected number of dwords are present in the circular buffer. + */ + for (n = ME_RETRY; n; --n) { + read_me_csr(&me); + if ((me.buffer_write_ptr - me.buffer_read_ptr) >= expected) + //if (me.interrupt_generate && !me.interrupt_status) + //if (me.interrupt_status) + break; + udelay(ME_DELAY); + } + if (!n) { + printf("ME: timeout waiting for data: expected " + "%u, available %u\n", expected, + me.buffer_write_ptr - me.buffer_read_ptr); + return -1; + } + /* Read and verify MEI response header from the ME */ + mei_read_dword_ptr(&mei_rsp, MEI_ME_CB_RW); + if (!mei_rsp.is_complete) { + printf("ME: response is not complete\n"); + return -1; + } + + /* Handle non-dword responses and expect at least MKHI header */ + ndata = mei_rsp.length >> 2; + if (mei_rsp.length & 3) + ndata++; + if (ndata != (expected - 1)) { //XXX + printf("ME: response is missing data\n"); + //return -1; + } + + /* Read and verify MKHI response header from the ME */ + mei_read_dword_ptr(&mkhi_rsp, MEI_ME_CB_RW); + if (!mkhi_rsp.is_response || + mkhi->group_id != mkhi_rsp.group_id || + mkhi->command != mkhi_rsp.command) { + printf("ME: invalid response, group %u ?= %u, " + "command %u ?= %u, is_response %u\n", mkhi->group_id, + mkhi_rsp.group_id, mkhi->command, mkhi_rsp.command, + mkhi_rsp.is_response); + //return -1; + } + ndata--; /* MKHI header has been read */ + + /* Make sure caller passed a buffer with enough space */ + if (ndata != (rsp_bytes >> 2)) { + printf("ME: not enough room in response buffer: " + "%u != %u\n", ndata, rsp_bytes >> 2); + //return -1; + } + + /* Read response data from the circular buffer */ + data = rsp_data; + for (n = 0; n < ndata; ++n) + *data++ = read_cb(); + + /* Tell the ME that we have consumed the response */ + read_host_csr(&host); + host.interrupt_status = 1; + host.interrupt_generate = 1; + write_host_csr(&host); + + return mei_wait_for_me_ready(); +} + +static inline int mei_sendrecv(struct mei_header *mei, struct mkhi_header *mkhi, + void *req_data, void *rsp_data, uint32_t rsp_bytes) +{ + if (mei_send_msg(mei, mkhi, req_data) < 0) + return -1; + if (mei_recv_msg(mei, mkhi, rsp_data, rsp_bytes) < 0) + return -1; + return 0; +} + +/* Send END OF POST message to the ME */ +/* +static int mkhi_end_of_post(void) +{ + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_GEN, + .command = MKHI_END_OF_POST, + }; + struct mei_header mei = { + .is_complete = 1, + .host_address = MEI_HOST_ADDRESS, + .client_address = MEI_ADDRESS_MKHI, + .length = sizeof(mkhi), + }; + + if (mei_sendrecv(&mei, &mkhi, NULL, NULL, 0) < 0) { + printf("ME: END OF POST message failed\n"); + return -1; + } + + printf("ME: END OF POST message successful\n"); + return 0; +} +*/ + +/* Get ME firmware version */ +int mkhi_get_fw_version(void) +{ + uint32_t data = 0; + struct me_fw_version version = {0}; + + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_GEN, + .command = GEN_GET_FW_VERSION, + .is_response = 0, + }; + + struct mei_header mei = { + .is_complete = 1, + .host_address = MEI_HOST_ADDRESS, + .client_address = MEI_ADDRESS_MKHI, + .length = sizeof(mkhi), + }; + +#ifndef OLDARC + /* Send request and wait for response */ + if (mei_sendrecv(&mei, &mkhi, &data, &version, sizeof(version) ) < 0) { + printf("ME: GET FW VERSION message failed\n"); + return -1; + } + printf("ME: Firmware Version %u.%u.%u.%u (code) " + "%u.%u.%u.%u (recovery) " + "%u.%u.%u.%u (fitc)\n\n", + version.code_major, version.code_minor, + version.code_build_number, version.code_hot_fix, + version.recovery_major, version.recovery_minor, + version.recovery_build_number, version.recovery_hot_fix, + version.fitcmajor, version.fitcminor, + version.fitcbuildno, version.fitchotfix); +#else + /* Send request and wait for response */ + if (mei_sendrecv(&mei, &mkhi, &data, &version, 2*sizeof(uint32_t) ) < 0) { + printf("ME: GET FW VERSION message failed\n"); + return -1; + } + printf("ME: Firmware Version %u.%u (code)\n\n" + version.code_major, version.code_minor); +#endif + return 0; +} + +static inline void print_cap(const char *name, int state) +{ + printf("ME Capability: %-30s : %s\n", + name, state ? "ON" : "OFF"); +} + +/* Get ME Firmware Capabilities */ +int mkhi_get_fwcaps(void) +{ + struct { + uint32_t rule_id; + uint32_t rule_len; + + struct me_fwcaps cap; + } fwcaps; + + fwcaps.rule_id = 0; + fwcaps.rule_len = 0; + + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_FWCAPS, + .command = MKHI_FWCAPS_GET_RULE, + .is_response = 0, + }; + struct mei_header mei = { + .is_complete = 1, + .host_address = MEI_HOST_ADDRESS, + .client_address = MEI_ADDRESS_MKHI, + .length = sizeof(mkhi) + sizeof(fwcaps.rule_id), + }; + + /* Send request and wait for response */ + if (mei_sendrecv(&mei, &mkhi, &fwcaps.rule_id, &fwcaps.cap, sizeof(fwcaps.cap)) < 0) { + printf("ME: GET FWCAPS message failed\n"); + return -1; + } + + print_cap("Full Network manageability ", fwcaps.cap.caps_sku.full_net); + print_cap("Regular Network manageability ", fwcaps.cap.caps_sku.std_net); + print_cap("Manageability ", fwcaps.cap.caps_sku.manageability); + print_cap("Small business technology ", fwcaps.cap.caps_sku.small_business); + print_cap("Level III manageability ", fwcaps.cap.caps_sku.l3manageability); + print_cap("IntelR Anti-Theft (AT) ", fwcaps.cap.caps_sku.intel_at); + print_cap("IntelR Capability Licensing Service (CLS) ", + fwcaps.cap.caps_sku.intel_cls); + print_cap("IntelR Power Sharing Technology (MPC) ", + fwcaps.cap.caps_sku.intel_mpc); + print_cap("ICC Over Clocking ", fwcaps.cap.caps_sku.icc_over_clocking); + print_cap("Protected Audio Video Path (PAVP) ", fwcaps.cap.caps_sku.pavp); + print_cap("IPV6 ", fwcaps.cap.caps_sku.ipv6); + print_cap("KVM Remote Control (KVM) ", fwcaps.cap.caps_sku.kvm); + print_cap("Outbreak Containment Heuristic (OCH) ", fwcaps.cap.caps_sku.och); + print_cap("Virtual LAN (VLAN) ", fwcaps.cap.caps_sku.vlan); + print_cap("TLS ", fwcaps.cap.caps_sku.tls); + print_cap("Wireless LAN (WLAN) ", fwcaps.cap.caps_sku.wlan); + + return 0; +} + +/* Tell ME to issue a global reset */ +uint32_t mkhi_global_reset(void) +{ + struct me_global_reset reset = { + .request_origin = GLOBAL_RESET_BIOS_POST, + .reset_type = CBM_RR_GLOBAL_RESET, + }; + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_CBM, + .command = MKHI_GLOBAL_RESET, + }; + struct mei_header mei = { + .is_complete = 1, + .length = sizeof(mkhi) + sizeof(reset), + .host_address = MEI_HOST_ADDRESS, + .client_address = MEI_ADDRESS_MKHI, + }; + + printf("ME: Requesting global reset\n"); + + /* Send request and wait for response */ + if (mei_sendrecv(&mei, &mkhi, &reset, NULL, 0) < 0) { + /* No response means reset will happen shortly... */ + asm("hlt"); + } + + /* If the ME responded it rejected the reset request */ + printf("ME: Global Reset failed\n"); + return -1; +} + +/* Tell ME thermal reporting parameters */ +/* +void mkhi_thermal(void) +{ + struct me_thermal_reporting thermal = { + .polling_timeout = 2, + .smbus_ec_msglen = 1, + .smbus_ec_msgpec = 0, + .dimmnumber = 4, + }; + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_CBM, + .command = MKHI_THERMAL_REPORTING, + }; + struct mei_header mei = { + .is_complete = 1, + .length = sizeof(mkhi) + sizeof(thermal), + .host_address = MEI_HOST_ADDRESS, + .client_address = MEI_ADDRESS_THERMAL, + }; + + printf("ME: Sending thermal reporting params\n"); + + mei_sendrecv(&mei, &mkhi, &thermal, NULL, 0); +} +*/ + +/* Enable debug of internal ME memory */ +int mkhi_debug_me_memory(void *physaddr) +{ + uint32_t data = 0; + + /* copy whole ME memory to a readable space */ + struct me_debug_mem memory = { + .debug_phys = (uintptr_t)physaddr, + .debug_size = 0x2000000, + .me_phys = 0x20000000, + .me_size = 0x2000000, + }; + struct mkhi_header mkhi = { + .group_id = MKHI_GROUP_ID_GEN, + .command = GEN_SET_DEBUG_MEM, + .is_response = 0, + }; + struct mei_header mei = { + .is_complete = 1, + .length = sizeof(mkhi) + sizeof(memory), + .host_address = MEI_HOST_ADDRESS, + .client_address = MEI_ADDRESS_MKHI, + }; + + printf("ME: Debug memory to 0x%zx ...", (size_t)physaddr); + if (mei_sendrecv(&mei, &mkhi, &memory, &data, 0) < 0) { + printf("failed\n"); + return -1; + } else { + printf("done\n"); + } + return 0; +} + +/* Prepare ME for MEI messages */ +uint32_t intel_mei_setup(struct pci_dev *dev) +{ + struct mei_csr host; + uint32_t reg32; + uint32_t pagerounded; + + mei_base_address = dev->base_addr[0] & ~0xf; + pagerounded = mei_base_address & ~0xfff; + mei_mmap = map_physical(pagerounded, 0x2000) + mei_base_address - pagerounded; + + /* Ensure Memory and Bus Master bits are set */ + reg32 = pci_read_long(dev, PCI_COMMAND); + reg32 |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_write_long(dev, PCI_COMMAND, reg32); + + /* Clean up status for next message */ + read_host_csr(&host); + host.interrupt_generate = 1; + host.ready = 1; + host.reset = 0; + write_host_csr(&host); + + return 0; +} + +void intel_mei_unmap(void) +{ + //munmap(mei_mmap, 0x2000); +} + +/* Read the Extend register hash of ME firmware */ +int intel_me_extend_valid(struct pci_dev *dev) +{ + struct me_heres status; + uint32_t extend[8] = {0}; + int i, count = 0; + + pci_read_dword_ptr(dev, &status, PCI_ME_HERES); + if (!status.extend_feature_present) { + printf("ME: Extend Feature not present\n"); + return -1; + } + + if (!status.extend_reg_valid) { + printf("ME: Extend Register not valid\n"); + return -1; + } + + switch (status.extend_reg_algorithm) { + case PCI_ME_EXT_SHA1: + count = 5; + printf("ME: Extend SHA-1: "); + break; + case PCI_ME_EXT_SHA256: + count = 8; + printf("ME: Extend SHA-256: "); + break; + default: + printf("ME: Extend Algorithm %d unknown\n", + status.extend_reg_algorithm); + return -1; + } + + for (i = 0; i < count; ++i) { + extend[i] = pci_read_long(dev, PCI_ME_HER(i)); + printf("%08x", extend[i]); + } + printf("\n"); + + return 0; +} diff --git a/util/intelmetool/me.h b/util/intelmetool/me.h new file mode 100644 index 0000000..2778266 --- /dev/null +++ b/util/intelmetool/me.h @@ -0,0 +1,409 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ME_H +#define ME_H + +#include <inttypes.h> +#include <pci/pci.h> + +#define ME_RETRY 100000 /* 1 second */ +#define ME_DELAY 10 /* 10 us */ + +#pragma pack(1) + +/* + * Management Engine PCI registers + */ + +#define PCI_ME_HFS 0x40 +#define ME_HFS_CWS_RESET 0 +#define ME_HFS_CWS_INIT 1 +#define ME_HFS_CWS_REC 2 +#define ME_HFS_CWS_NORMAL 5 +#define ME_HFS_CWS_WAIT 6 +#define ME_HFS_CWS_TRANS 7 +#define ME_HFS_CWS_INVALID 8 +#define ME_HFS_STATE_PREBOOT 0 +#define ME_HFS_STATE_M0_UMA 1 +#define ME_HFS_STATE_M3 4 +#define ME_HFS_STATE_M0 5 +#define ME_HFS_STATE_BRINGUP 6 +#define ME_HFS_STATE_ERROR 7 +#define ME_HFS_ERROR_NONE 0 +#define ME_HFS_ERROR_UNCAT 1 +#define ME_HFS_ERROR_IMAGE 3 +#define ME_HFS_ERROR_DEBUG 4 +#define ME_HFS_MODE_NORMAL 0 +#define ME_HFS_MODE_DEBUG 2 +#define ME_HFS_MODE_DIS 3 +#define ME_HFS_MODE_OVER_JMPR 4 +#define ME_HFS_MODE_OVER_MEI 5 +#define ME_HFS_BIOS_DRAM_ACK 1 +#define ME_HFS_ACK_NO_DID 0 +#define ME_HFS_ACK_RESET 1 +#define ME_HFS_ACK_PWR_CYCLE 2 +#define ME_HFS_ACK_S3 3 +#define ME_HFS_ACK_S4 4 +#define ME_HFS_ACK_S5 5 +#define ME_HFS_ACK_GBL_RESET 6 +#define ME_HFS_ACK_CONTINUE 7 + +struct me_hfs { + uint32_t working_state: 4; + uint32_t mfg_mode: 1; + uint32_t fpt_bad: 1; + uint32_t operation_state: 3; + uint32_t fw_init_complete: 1; + uint32_t ft_bup_ld_flr: 1; + uint32_t update_in_progress: 1; + uint32_t error_code: 4; + uint32_t operation_mode: 4; + uint32_t reserved: 4; + uint32_t boot_options_present: 1; + uint32_t ack_data: 3; + uint32_t bios_msg_ack: 4; +} __attribute__ ((packed)); + +#define PCI_ME_UMA 0x44 + +struct me_uma { + uint32_t size: 6; + uint32_t reserved_1: 10; + uint32_t valid: 1; + uint32_t reserved_0: 14; + uint32_t set_to_one: 1; +} __attribute__ ((packed)); + +#define PCI_ME_H_GS 0x4c +#define ME_INIT_DONE 1 +#define ME_INIT_STATUS_SUCCESS 0 +#define ME_INIT_STATUS_NOMEM 1 +#define ME_INIT_STATUS_ERROR 2 + +struct me_did { + uint32_t uma_base: 16; + uint32_t reserved: 8; + uint32_t status: 4; + uint32_t init_done: 4; +} __attribute__ ((packed)); + +#define PCI_ME_GMES 0x48 +#define ME_GMES_PHASE_ROM 0 +#define ME_GMES_PHASE_BUP 1 +#define ME_GMES_PHASE_UKERNEL 2 +#define ME_GMES_PHASE_POLICY 3 +#define ME_GMES_PHASE_MODULE 4 +#define ME_GMES_PHASE_UNKNOWN 5 +#define ME_GMES_PHASE_HOST 6 + +struct me_gmes { + uint32_t bist_in_prog : 1; + uint32_t icc_prog_sts : 2; + uint32_t invoke_mebx : 1; + uint32_t cpu_replaced_sts : 1; + uint32_t mbp_rdy : 1; + uint32_t mfs_failure : 1; + uint32_t warm_rst_req_for_df : 1; + uint32_t cpu_replaced_valid : 1; + uint32_t reserved_1 : 2; + uint32_t fw_upd_ipu : 1; + uint32_t reserved_2 : 4; + uint32_t current_state: 8; + uint32_t current_pmevent: 4; + uint32_t progress_code: 4; +} __attribute__ ((packed)); + +#define PCI_ME_HERES 0xbc +#define PCI_ME_EXT_SHA1 0x00 +#define PCI_ME_EXT_SHA256 0x02 +#define PCI_ME_HER(x) (0xc0+(4*(x))) + +struct me_heres { + uint32_t extend_reg_algorithm: 4; + uint32_t reserved: 26; + uint32_t extend_feature_present: 1; + uint32_t extend_reg_valid: 1; +} __attribute__ ((packed)); + +struct me_thermal_reporting { + uint32_t polling_timeout: 8; + uint32_t smbus_ec_msglen: 8; + uint32_t smbus_ec_msgpec: 8; + uint32_t dimmnumber: 8; +} __attribute__ ((packed)); + +/* + * Management Engine MEI registers + */ + +#define MEI_H_CB_WW 0x00 +#define MEI_H_CSR 0x04 +#define MEI_ME_CB_RW 0x08 +#define MEI_ME_CSR_HA 0x0c + +struct mei_csr { + uint32_t interrupt_enable: 1; + uint32_t interrupt_status: 1; + uint32_t interrupt_generate: 1; + uint32_t ready: 1; + uint32_t reset: 1; + uint32_t reserved: 3; + uint32_t buffer_read_ptr: 8; + uint32_t buffer_write_ptr: 8; + uint32_t buffer_depth: 8; +} __attribute__ ((packed)); + +#define MEI_ADDRESS_HBM 0x00 +#define MEI_ADDRESS_CORE_WD 0x01 +#define MEI_ADDRESS_AMT 0x02 +#define MEI_ADDRESS_RESERVED 0x03 +#define MEI_ADDRESS_WDT 0x04 +#define MEI_ADDRESS_POLICY 0x05 +#define MEI_ADDRESS_PASSWORD 0x06 +#define MEI_ADDRESS_MKHI 0x07 +#define MEI_ADDRESS_ICC 0x08 +#define MEI_ADDRESS_THERMAL 0x09 +#define MEI_ADDRESS_SPI 0x0a + +#define MEI_HOST_ADDRESS 0 + +struct mei_header { + uint32_t client_address: 8; + uint32_t host_address: 8; + uint32_t length: 9; + uint32_t reserved: 6; + uint32_t is_complete: 1; +} __attribute__ ((packed)); + +#define MKHI_GROUP_ID_CBM 0x00 +#define MKHI_GROUP_ID_PM 0x01 +#define MKHI_GROUP_ID_PWD 0x02 +#define MKHI_GROUP_ID_FWCAPS 0x03 +#define MKHI_GROUP_ID_APP 0x04 +#define MKHI_GROUP_ID_SPI 0x05 +#define MKHI_GROUP_ID_MDES 0x08 +#define MKHI_GROUP_ID_MAX 0x09 +#define MKHI_GROUP_ID_GEN 0xff + +#define MKHI_FWCAPS_GET_RULE 0x02 +#define MKHI_FWCAPS_SET_RULE 0x03 +#define MKHI_GLOBAL_RESET 0x0b + +#define GEN_GET_MKHI_VERSION 0x01 +#define GEN_GET_FW_VERSION 0x02 +#define GEN_UNCONFIG_NO_PWD 0x0d +#define GEN_SET_DEBUG_MEM 0x11 + +#define FWCAPS_ME_FWU_RULE 0x2e +#define FWCAPS_OVERRIDE 0x14 + +#define MKHI_THERMAL_REPORTING 0x00 +#define MKHI_GET_FW_VERSION 0x02 +#define MKHI_MDES_ENABLE 0x09 +#define MKHI_END_OF_POST 0x0c +#define MKHI_FEATURE_OVERRIDE 0x14 + +#define HBM_HOST_START_REQ_CMD 0x01 +#define HBM_HOST_STOP_REQ_CMD 0x02 +#define HBM_ME_STOP_REQ_CMD 0x03 +#define HBM_HOST_ENUM_REQ_CMD 0x04 +#define HBM_HOST_CLIENT_PROPERTIES_REQ_CMD 0x05 +#define HBM_CLIENT_CONNECT_REQ_CMD 0x06 +#define HBM_CLIENT_DISCONNECT_REQ_CMD 0x07 + +struct mkhi_header { + uint32_t group_id: 8; + uint32_t command: 7; + uint32_t is_response: 1; + uint32_t reserved: 8; + uint32_t result: 8; +} __attribute__ ((packed)); + +struct me_fw_version { + uint16_t code_minor; + uint16_t code_major; + uint16_t code_build_number; + uint16_t code_hot_fix; + uint16_t recovery_minor; + uint16_t recovery_major; + uint16_t recovery_build_number; + uint16_t recovery_hot_fix; + uint16_t fitcminor; + uint16_t fitcmajor; + uint16_t fitcbuildno; + uint16_t fitchotfix; +} __attribute__ ((packed)); + + +#define HECI_EOP_STATUS_SUCCESS 0x0 +#define HECI_EOP_PERFORM_GLOBAL_RESET 0x1 + +#define CBM_RR_GLOBAL_RESET 0x01 + +#define GLOBAL_RESET_BIOS_MRC 0x01 +#define GLOBAL_RESET_BIOS_POST 0x02 +#define GLOBAL_RESET_MEBX 0x03 + +struct me_global_reset { + uint8_t request_origin; + uint8_t reset_type; +} __attribute__ ((packed)); + +typedef enum { + ME_NORMAL_BIOS_PATH, + ME_S3WAKE_BIOS_PATH, + ME_ERROR_BIOS_PATH, + ME_RECOVERY_BIOS_PATH, + ME_DISABLE_BIOS_PATH, + ME_FIRMWARE_UPDATE_BIOS_PATH, +} me_bios_path; + +typedef struct { + uint32_t major_version : 16; + uint32_t minor_version : 16; + uint32_t hotfix_version : 16; + uint32_t build_version : 16; +} __attribute__ ((packed)) mbp_fw_version_name; + +typedef struct { + uint8_t num_icc_profiles; + uint8_t icc_profile_soft_strap; + uint8_t icc_profile_index; + uint8_t reserved; + uint32_t register_lock_mask[3]; +} __attribute__ ((packed)) mbp_icc_profile; + +typedef struct { + uint32_t full_net : 1; + uint32_t std_net : 1; + uint32_t manageability : 1; + uint32_t small_business : 1; + uint32_t l3manageability : 1; + uint32_t intel_at : 1; + uint32_t intel_cls : 1; + uint32_t reserved : 3; + uint32_t intel_mpc : 1; + uint32_t icc_over_clocking : 1; + uint32_t pavp : 1; + uint32_t reserved_1 : 4; + uint32_t ipv6 : 1; + uint32_t kvm : 1; + uint32_t och : 1; + uint32_t vlan : 1; + uint32_t tls : 1; + uint32_t reserved_4 : 1; + uint32_t wlan : 1; + uint32_t reserved_5 : 8; +} __attribute__ ((packed)) mefwcaps_sku; + +typedef struct { + uint16_t lock_state : 1; + uint16_t authenticate_module : 1; + uint16_t s3authentication : 1; + uint16_t flash_wear_out : 1; + uint16_t flash_variable_security : 1; + uint16_t wwan3gpresent : 1; + uint16_t wwan3goob : 1; + uint16_t reserved : 9; +} __attribute__ ((packed)) tdt_state_flag; + +typedef struct { + uint8_t state; + uint8_t last_theft_trigger; + tdt_state_flag flags; +} __attribute__ ((packed)) tdt_state_info; + +typedef struct { + uint32_t platform_target_usage_type : 4; + uint32_t platform_target_market_type : 2; + uint32_t super_sku : 1; + uint32_t reserved : 1; + uint32_t intel_me_fw_image_type : 4; + uint32_t platform_brand : 4; + uint32_t reserved_1 : 16; +} __attribute__ ((packed)) platform_type_rule_data; + +typedef struct { + mefwcaps_sku fw_capabilities; + uint8_t available; +} mbp_fw_caps; + +typedef struct { + uint16_t device_id; + uint16_t fuse_test_flags; + uint32_t umchid[4]; +} __attribute__ ((packed)) mbp_rom_bist_data; + +typedef struct { + uint32_t key[8]; +} mbp_platform_key; + +typedef struct { + platform_type_rule_data rule_data; + uint8_t available; +} mbp_plat_type; + +typedef struct { + mbp_fw_version_name fw_version_name; + mbp_fw_caps fw_caps_sku; + mbp_rom_bist_data rom_bist_data; + mbp_platform_key platform_key; + mbp_plat_type fw_plat_type; + mbp_icc_profile icc_profile; + tdt_state_info at_state; + uint32_t mfsintegrity; +} me_bios_payload; + +typedef struct { + uint32_t mbp_size : 8; + uint32_t num_entries : 8; + uint32_t rsvd : 16; +} __attribute__ ((packed)) mbp_header; + +typedef struct { + uint32_t app_id : 8; + uint32_t item_id : 8; + uint32_t length : 8; + uint32_t rsvd : 8; +} __attribute__ ((packed)) mbp_item_header; + +struct me_fwcaps { + uint32_t id; + uint8_t length; + mefwcaps_sku caps_sku; + uint8_t reserved[3]; +} __attribute__ ((packed)); + +struct me_debug_mem { + uint32_t debug_phys; + uint32_t debug_size; + uint32_t me_phys; + uint32_t me_size; +} __attribute__ ((packed)); + +void intel_me_status(uint32_t hfs, uint32_t gmes); +void mkhi_thermal(void); +uint32_t intel_mei_setup(struct pci_dev *dev); +void intel_mei_unmap(void); +int mkhi_get_fwcaps(void); +int mkhi_get_fw_version(void); +int mkhi_debug_me_memory(void *addr); +void mei_reset(void); +void udelay(uint32_t usecs); +int intel_me_extend_valid(struct pci_dev *dev); + +#endif diff --git a/util/intelmetool/me_status.c b/util/intelmetool/me_status.c new file mode 100644 index 0000000..1de51f4 --- /dev/null +++ b/util/intelmetool/me_status.c @@ -0,0 +1,205 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <stdio.h> +#include "me.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +/* HFS1[3:0] Current Working State Values */ +static const char *me_cws_values[] = { + [ME_HFS_CWS_RESET] = "Reset", + [ME_HFS_CWS_INIT] = "Initializing", + [ME_HFS_CWS_REC] = "Recovery", + [ME_HFS_CWS_NORMAL] = "Normal", + [ME_HFS_CWS_WAIT] = "Platform Disable Wait", + [ME_HFS_CWS_TRANS] = "OP State Transition", + [ME_HFS_CWS_INVALID] = "Invalid CPU Plugged In" +}; + +/* HFS1[8:6] Current Operation State Values */ +static const char *me_opstate_values[] = { + [ME_HFS_STATE_PREBOOT] = "Preboot", + [ME_HFS_STATE_M0_UMA] = "M0 with UMA", + [ME_HFS_STATE_M3] = "M3 without UMA", + [ME_HFS_STATE_M0] = "M0 without UMA", + [ME_HFS_STATE_BRINGUP] = "Bring up", + [ME_HFS_STATE_ERROR] = "M0 without UMA but with error" +}; + +/* HFS[19:16] Current Operation Mode Values */ +static const char *me_opmode_values[] = { + [ME_HFS_MODE_NORMAL] = "Normal", + [ME_HFS_MODE_DEBUG] = "Debug", + [ME_HFS_MODE_DIS] = "Soft Temporary Disable", + [ME_HFS_MODE_OVER_JMPR] = "Security Override via Jumper", + [ME_HFS_MODE_OVER_MEI] = "Security Override via MEI Message" +}; + +/* HFS[15:12] Error Code Values */ +static const char *me_error_values[] = { + [ME_HFS_ERROR_NONE] = "No Error", + [ME_HFS_ERROR_UNCAT] = "Uncategorized Failure", + [ME_HFS_ERROR_IMAGE] = "Image Failure", + [ME_HFS_ERROR_DEBUG] = "Debug Failure" +}; + +/* GMES[31:28] ME Progress Code */ +static const char *me_progress_values[] = { + [ME_GMES_PHASE_ROM] = "ROM Phase", + [ME_GMES_PHASE_BUP] = "BUP Phase", + [ME_GMES_PHASE_UKERNEL] = "uKernel Phase", + [ME_GMES_PHASE_POLICY] = "Policy Module", + [ME_GMES_PHASE_MODULE] = "Module Loading", + [ME_GMES_PHASE_UNKNOWN] = "Unknown", + [ME_GMES_PHASE_HOST] = "Host Communication" +}; + +/* GMES[27:24] Power Management Event */ +static const char *me_pmevent_values[] = { + [0x00] = "Clean Moff->Mx wake", + [0x01] = "Moff->Mx wake after an error", + [0x02] = "Clean global reset", + [0x03] = "Global reset after an error", + [0x04] = "Clean Intel ME reset", + [0x05] = "Intel ME reset due to exception", + [0x06] = "Pseudo-global reset", + [0x07] = "S0/M0->Sx/M3", + [0x08] = "Sx/M3->S0/M0", + [0x09] = "Non-power cycle reset", + [0x0a] = "Power cycle reset through M3", + [0x0b] = "Power cycle reset through Moff", + [0x0c] = "Sx/Mx->Sx/Moff" +}; + +/* Progress Code 0 states */ +static const char *me_progress_rom_values[] = { + [0x00] = "BEGIN", + [0x06] = "DISABLE" +}; + +/* Progress Code 1 states */ +static const char *me_progress_bup_values[] = { + [0x00] = "Initialization starts", + [0x01] = "Disable the host wake event", + [0x04] = "Flow determination start process", + [0x08] = "Error reading/matching the VSCC table in the descriptor", + [0x0a] = "Check to see if straps say ME DISABLED", + [0x0b] = "Timeout waiting for PWROK", + [0x0d] = "Possibly handle BUP manufacturing override strap", + [0x11] = "Bringup in M3", + [0x12] = "Bringup in M0", + [0x13] = "Flow detection error", + [0x15] = "M3 clock switching error", + [0x18] = "M3 kernel load", + [0x1c] = "T34 missing - cannot program ICC", + [0x1f] = "Waiting for DID BIOS message", + [0x20] = "Waiting for DID BIOS message failure", + [0x21] = "DID reported an error", + [0x22] = "Enabling UMA", + [0x23] = "Enabling UMA error", + [0x24] = "Sending DID Ack to BIOS", + [0x25] = "Sending DID Ack to BIOS error", + [0x26] = "Switching clocks in M0", + [0x27] = "Switching clocks in M0 error", + [0x28] = "ME in temp disable", + [0x32] = "M0 kernel load", +}; + +/* Progress Code 3 states */ +static const char *me_progress_policy_values[] = { + [0x00] = "Entery into Policy Module", + [0x03] = "Received S3 entry", + [0x04] = "Received S4 entry", + [0x05] = "Received S5 entry", + [0x06] = "Received UPD entry", + [0x07] = "Received PCR entry", + [0x08] = "Received NPCR entry", + [0x09] = "Received host wake", + [0x0a] = "Received AC<>DC switch", + [0x0b] = "Received DRAM Init Done", + [0x0c] = "VSCC Data not found for flash device", + [0x0d] = "VSCC Table is not valid", + [0x0e] = "Flash Partition Boundary is outside address space", + [0x0f] = "ME cannot access the chipset descriptor region", + [0x10] = "Required VSCC values for flash parts do not match", +}; + +void intel_me_status(uint32_t hfs, uint32_t gmes) +{ + /* Check Current States */ + printf("ME: FW Partition Table : %s\n", + ((hfs & 0x20) >> 5) ? "BAD" : "OK"); + printf("ME: Bringup Loader Failure : %s\n", + ((hfs & 0x400) >> 10) ? "YES" : "NO"); + printf("ME: Firmware Init Complete : %s\n", + ((hfs & 0x200) >> 9) ? "YES" : "NO"); + printf("ME: Manufacturing Mode : %s\n", + ((hfs & 0x10) >> 4) ? "YES" : "NO"); + printf("ME: Boot Options Present : %s\n", + ((hfs & 0x1000000) >> 24) ? "YES" : "NO"); + printf("ME: Update In Progress : %s\n", + ((hfs & 0x800) >> 11) ? "YES" : "NO"); + printf("ME: Current Working State : %s\n", + me_cws_values[hfs & 0xf]); + printf("ME: Current Operation State : %s\n", + me_opstate_values[(hfs & 0x1c0) >> 6]); + printf("ME: Current Operation Mode : %s\n", + me_opmode_values[(hfs & 0xf0000) >> 16]); + printf("ME: Error Code : %s\n", + me_error_values[(hfs & 0xf000) >> 12]); + printf("ME: Progress Phase : %s\n", + me_progress_values[(gmes & 0xf0000000) >> 28]); + printf("ME: Power Management Event : %s\n", + me_pmevent_values[(gmes & 0xf000000) >> 24]); + + printf("ME: Progress Phase State : "); + switch ((gmes & 0xf0000000) >> 28) { + case ME_GMES_PHASE_ROM: /* ROM Phase */ + printf("%s", + me_progress_rom_values[(gmes & 0xff0000) >> 16]); + break; + + case ME_GMES_PHASE_BUP: /* Bringup Phase */ + if ((gmes & 0xff0000) >> 16 < ARRAY_SIZE(me_progress_bup_values) + && me_progress_bup_values[(gmes & 0xff0000) >> 16]) + printf("%s", + me_progress_bup_values[(gmes & 0xff0000) >> 16]); + else + printf("0x%02x", (gmes & 0xff0000) >> 16); + break; + + case ME_GMES_PHASE_POLICY: /* Policy Module Phase */ + if ((gmes & 0xff0000) >> 16 < ARRAY_SIZE(me_progress_policy_values) + && me_progress_policy_values[(gmes & 0xff0000) >> 16]) + printf("%s", + me_progress_policy_values[(gmes & 0xff0000) >> 16]); + else + printf("0x%02x", (gmes & 0xff0000) >> 16); + break; + + case ME_GMES_PHASE_HOST: /* Host Communication Phase */ + if (!((gmes & 0xff0000) >> 16)) + printf("Host communication established"); + else + printf("0x%02x", (gmes & 0xff0000) >> 16); + break; + + default: + printf("Unknown 0x%02x", (gmes & 0xff0000) >> 16); + } + printf("\n"); +} diff --git a/util/intelmetool/mmap.c b/util/intelmetool/mmap.c new file mode 100644 index 0000000..da36eaa --- /dev/null +++ b/util/intelmetool/mmap.c @@ -0,0 +1,58 @@ +/* intelmetool + * + * Copyright (C) 2013-2015 Damien Zammit damien@zamaudio.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "mmap.h" +#include <errno.h> + +#ifndef __DARWIN__ +int fd_mem; + +void *map_physical_exact(uint64_t phys_addr, uint64_t mapto, size_t len) { + void *virt_addr; + int err; + + virt_addr = mmap((void*)mapto, len, PROT_WRITE | PROT_READ, + MAP_SHARED | MAP_FIXED, fd_mem, (off_t) phys_addr); + + if (virt_addr == MAP_FAILED) { + err = errno; + printf("Error mapping physical memory 0x%016" PRIx64 "[0x%zx] ERRNO=%d\n", + phys_addr, len, err); + return NULL; + } + + return virt_addr; +} + +void *map_physical(uint64_t phys_addr, size_t len) { + void *virt_addr; + int err; + + virt_addr = mmap(NULL, len, PROT_WRITE | PROT_READ, MAP_SHARED, fd_mem, (off_t) phys_addr); + + if (virt_addr == MAP_FAILED) { + err = errno; + printf("Error mapping physical memory 0x%016" PRIx64 "[0x%zx] ERRNO=%d\n", + phys_addr, len, err); + return NULL; + } + + return virt_addr; +} + +void unmap_physical(void *virt_addr, size_t len) { + munmap(virt_addr, len); +} +#endif diff --git a/util/intelmetool/mmap.h b/util/intelmetool/mmap.h new file mode 100644 index 0000000..109ceff --- /dev/null +++ b/util/intelmetool/mmap.h @@ -0,0 +1,27 @@ +/* intelmetool + * + * Copyright (C) 2013-2015 Damien Zammit damien@zamaudio.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <inttypes.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <stdio.h> + +#ifndef __DARWIN__ +extern int fd_mem; +extern void *map_physical(uint64_t phys_addr, size_t len); +extern void unmap_physical(void *virt_addr, size_t len); +extern void *map_physical_exact(uint64_t phys_addr, uint64_t mapto, size_t len); +#endif