Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/25402
Change subject: [WIP]util/intemetool: Add P2SB interface support ......................................................................
[WIP]util/intemetool: Add P2SB interface support
Try to unhide HECI using P2SB interface. Tests showed that it's not possible to toggle HECI after the interface has been locked. Maybe some other bits need to be toggled first.
TODO: Needs test on hardware that hides the interface.
Change-Id: I6e67238095856d9937f8f392b8113d99af4cd186 Signed-off-by: Patrick Rudolph patrick.rudolph@9elements.com --- M util/intelmetool/Makefile M util/intelmetool/intelmetool.c A util/intelmetool/p2sb.c A util/intelmetool/p2sb.h 4 files changed, 255 insertions(+), 1 deletion(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/02/25402/1
diff --git a/util/intelmetool/Makefile b/util/intelmetool/Makefile index b455a0e..850c46b 100644 --- a/util/intelmetool/Makefile +++ b/util/intelmetool/Makefile @@ -18,9 +18,10 @@ INSTALL ?= /usr/bin/install PREFIX ?= /usr/local CFLAGS ?= -O0 -g -Wall -W -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function +CFLAGS += -I../../src/commonlib/include LDFLAGS += -lpci -lz
-OBJS = intelmetool.o me.o me_status.o mmap.o rcba.o msr.o +OBJS = intelmetool.o me.o me_status.o mmap.o rcba.o msr.o p2sb.o
OS_ARCH = $(shell uname) ifeq ($(OS_ARCH), Darwin) diff --git a/util/intelmetool/intelmetool.c b/util/intelmetool/intelmetool.c index 0e75a50..34a8012 100644 --- a/util/intelmetool/intelmetool.c +++ b/util/intelmetool/intelmetool.c @@ -28,11 +28,13 @@ #include "mmap.h" #include "msr.h" #include "rcba.h" +#include "p2sb.h"
extern int fd_mem; int debug = 0;
static uint32_t fd2 = 0; +static uint32_t psfx_t0_shdw_pcien = 0; static int ME_major_ver = 0; static int ME_minor_ver = 0;
@@ -176,6 +178,11 @@ return 0; }
+#define PSF_BASE_ADDRESS 0xA00 +#define PCR_PSFX_T0_SHDW_PCIEN 0x1C +#define PCR_PSFX_T0_SHDW_PCIEN_FUNDIS (1 << 8) +#define PID_PSF1 0xBA + static int activate_me(void) { const uint32_t rcba = get_rcba_phys(); @@ -194,6 +201,21 @@ printf("MEI was hidden on PCI, now unlocked\n"); else if (debug) printf("MEI not hidden on PCI, checking if visible\n"); + } else { + const uint64_t p2sb = get_p2sb_phys(); + if (debug) + printf("P2SB bar: 0x%" PRIx64 "\n", p2sb); + if (p2sb > 0) { + if (pcr_read32(PID_PSF1, PSF_BASE_ADDRESS + + PCR_PSFX_T0_SHDW_PCIEN, + &psfx_t0_shdw_pcien)) + printf("Error reading PCR\n"); + + if (pcr_and32(PID_PSF1, PSF_BASE_ADDRESS + + PCR_PSFX_T0_SHDW_PCIEN, + ~PCR_PSFX_T0_SHDW_PCIEN_FUNDIS)) + printf("Error writing PCR\n"); + } }
return 0; @@ -217,6 +239,15 @@ if (debug) printf("done\n"); } + } else { + const uint64_t p2sb = get_p2sb_phys(); + if (p2sb > 0 && + (psfx_t0_shdw_pcien & PCR_PSFX_T0_SHDW_PCIEN_FUNDIS)) { + if (pcr_or32(PID_PSF1, PSF_BASE_ADDRESS + + PCR_PSFX_T0_SHDW_PCIEN, + PCR_PSFX_T0_SHDW_PCIEN_FUNDIS)) + printf("Error writing PCR\n"); + } } }
diff --git a/util/intelmetool/p2sb.c b/util/intelmetool/p2sb.c new file mode 100644 index 0000000..28ce46f --- /dev/null +++ b/util/intelmetool/p2sb.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2014 Damien Zammit damien@zamaudio.com + * Copyright (C) 2017 Patrick Rudolph siro@das-labor.org + * + * 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 "intelmetool.h" +#include "mmap.h" +#include "p2sb.h" +#include "assert.h" +#include "commonlib/helpers.h" + +static const int size = 0x1000000; + +/* Returns the physical P2SB base address or zero on error. */ +u64 get_p2sb_phys(void) +{ + struct pci_access *pacc; + struct pci_dev *sb; + uint64_t p2sb_phys = 0; + + pacc = pci_alloc(); + pacc->method = PCI_ACCESS_I386_TYPE1; + + pci_init(pacc); + pci_scan_bus(pacc); + + sb = pci_get_dev(pacc, 0, 0, 0x1f, 1); + if (!sb) { + printf("Uh oh, southbridge not on BDF(0:31:1), please report " + "this error, exiting.\n"); + pci_cleanup(pacc); + return 0; + } + pci_fill_info(sb, PCI_FILL_IDENT | PCI_FILL_BASES | PCI_FILL_SIZES | + PCI_FILL_CLASS); + + const u8 reg8 = pci_read_byte(sb, 0xe1); + + if (pci_read_byte(sb, 0xe2) & 2) { + static int once; + if (!once) + printf("Warning: Endpoint Mask Lock Bit set !\n"); + once = 1; + } + + /* Make device visible */ + pci_write_byte(sb, 0xe1, 0); + + /* someone home ? */ + if (pci_read_word(sb, 0) == 0x0000 || + pci_read_word(sb, 0) == 0xffff) + goto out; + + /* Probe for memory bar */ + if (pci_read_long(sb, 0x10) & 1) + goto out; + + /* Get Sideband Register Access address */ + if ((pci_read_long(sb, 0x10) & 0x6) == 0x0) + p2sb_phys = pci_read_long(sb, 0x10) & 0xfffffff8UL; + else if ((pci_read_long(sb, 0x10) & 0x6) == 0x4) + p2sb_phys = (pci_read_long(sb, 0x10) & 0xfffffff8UL) | + (((unsigned long long)pci_read_long(sb, 0x14)) << 32); + +out: + /* Make device invisible */ + pci_write_byte(sb, 0xe1, reg8 | 1); + + pci_free_dev(sb); + pci_cleanup(pacc); + + return p2sb_phys; +} + +/* + * Writes 'val' to P2SB register at address 'addr'. + * Returns 1 on error and 0 on success. + */ +static int write_p2sb32(uint32_t addr, uint32_t val) +{ + volatile uint8_t *p2sb; + const uint64_t p2sb_phys = get_p2sb_phys(); + + if (!p2sb_phys) { + printf("Could not get P2SB address\n"); + return 1; + } + + p2sb = map_physical((off_t)p2sb_phys, size); + if (p2sb == NULL) { + printf("Could not map P2SB\n"); + return 1; + } + *(uint32_t *)(p2sb + addr) = val; + + munmap((void *)p2sb, size); + return 0; +} + +/* + * Reads P2SB register at address 'addr' and stores it in 'val'. + * Returns 1 on error and 0 on success. + */ +static int read_p2sb32(uint32_t addr, uint32_t *val) +{ + volatile uint8_t *p2sb; + const uint64_t p2sb_phys = get_p2sb_phys(); + + if (!p2sb_phys) { + printf("Could not get P2SB address\n"); + return 1; + } + + p2sb = map_physical((off_t)p2sb_phys, size); + if (p2sb == NULL) { + printf("Could not map P2SB\n"); + return 1; + } + + *val = *(uint32_t *)(p2sb + addr); + + munmap((void *)p2sb, size); + return 0; +} + +/* Port Id lives in bits 23:16 and register offset lives in 15:0 of address. */ +#define PCR_PORTID_SHIFT 16 +#define PCR_OFFSET_SHIFT 0 + +static uint32_t __pcr_reg_address(uint8_t pid, uint16_t offset) +{ + return (pid << PCR_PORTID_SHIFT) | (offset << PCR_OFFSET_SHIFT); +} + +int pcr_read32(uint8_t pid, uint16_t offset, uint32_t *val) +{ + /* Ensure the PCR offset is corretcly aligned. */ + assert(IS_ALIGNED(offset, sizeof(uint32_t))); + return read_p2sb32(__pcr_reg_address(pid, offset), val); +} +/* + * After every write one needs to perform a read an innocuous register to + * ensure the writes are completed for certain ports. This is done for + * all ports so that the callers don't need the per-port knowledge for + * each transaction. + */ +static inline int write_completion(uint8_t pid, uint16_t offset) +{ + uint32_t reg32; + uint32_t a = __pcr_reg_address(pid, ALIGN_DOWN(offset, sizeof(uint32_t))); + return read_p2sb32(a, ®32); +} + +int pcr_write32(uint8_t pid, uint16_t offset, uint32_t indata) +{ + /* Ensure the PCR offset is corretcly aligned. */ + assert(IS_ALIGNED(offset, sizeof(indata))); + + if (write_p2sb32(__pcr_reg_address(pid, offset), indata)) + return 1; + /* Ensure the writes complete. */ + if (write_completion(pid, offset)) + return 1; + + return 0; +} + +int pcr_or32(uint8_t pid, uint16_t offset, uint32_t ordata) +{ + uint32_t data32; + + if (pcr_read32(pid, offset, &data32)) + return 1; + data32 |= ordata; + if (pcr_write32(pid, offset, data32)) + return 1; + + return 0; +} + +int pcr_and32(uint8_t pid, uint16_t offset, uint32_t anddata) +{ + uint32_t data32; + + if (pcr_read32(pid, offset, &data32)) + return 1; + data32 &= anddata; + if (pcr_write32(pid, offset, data32)) + return 1; + + return 0; +} diff --git a/util/intelmetool/p2sb.h b/util/intelmetool/p2sb.h new file mode 100644 index 0000000..f5c8515 --- /dev/null +++ b/util/intelmetool/p2sb.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 Patrick Rudolph siro@das-labor.org + * + * 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. + */ + +int pcr_read32(uint8_t pid, uint16_t offset, uint32_t *val); +int pcr_write32(uint8_t pid, uint16_t offset, uint32_t indata); +int pcr_or32(uint8_t pid, uint16_t offset, uint32_t ordata); +int pcr_and32(uint8_t pid, uint16_t offset, uint32_t anddata); +u64 get_p2sb_phys(void);