[coreboot-gerrit] New patch to review for coreboot: [WIP] util/bucts: Add tool to flip the BUC.TS bit on Intel targets

Arthur Heymans (arthur@aheymans.xyz) gerrit at coreboot.org
Tue Jan 24 20:32:40 CET 2017


Arthur Heymans (arthur at aheymans.xyz) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/18224

-gerrit

commit 909ed6e3dc7a2ff37592a16920594457873cc20c
Author: Arthur Heymans <arthur at aheymans.xyz>
Date:   Tue Jan 24 20:27:43 2017 +0100

    [WIP] util/bucts: Add tool to flip the BUC.TS bit on Intel targets
    
    This tool is initially written by Peter Stuge.
    
    This tool makes it possible to have a backup mechanism for the
    bootblock by using the southbridges Back Up Control Top Swap.
    
    Change-Id: I12cc2e91396f096fc979e23848e1929cb6c44fc5
    Signed-off-by: Arthur Heymans <arthur at aheymans.xyz>
---
 .gitignore          |   1 +
 util/bucts/Makefile |  20 ++++
 util/bucts/README   |  51 +++++++++
 util/bucts/bucts.c  | 301 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 373 insertions(+)

diff --git a/.gitignore b/.gitignore
index df7bff4..4ca04bc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -81,6 +81,7 @@ util/amdfwtool/amdfwtool
 util/archive/archive
 util/bimgtool/bimgtool
 util/board_status/board-status
+util/bucts/bucts
 util/cbfstool/cbfstool
 util/cbfstool/fmaptool
 util/cbfstool/ifwitool
diff --git a/util/bucts/Makefile b/util/bucts/Makefile
new file mode 100644
index 0000000..68541e6
--- /dev/null
+++ b/util/bucts/Makefile
@@ -0,0 +1,20 @@
+CC:=gcc
+OBJ:=bucts.o
+VERSION:=$(shell git describe)
+
+ifeq ($(shell uname), FreeBSD)
+	CFLAGS = -I/usr/local/include
+	LDFLAGS = -L/usr/local/lib
+endif
+
+all: bucts
+
+bucts: $(OBJ)
+	$(CC) -o $@ $(OBJ) $(LDFLAGS) -lpci
+
+%.o: %.c
+	$(CC) $(CFLAGS) -DVERSION='"$(VERSION)"' -c $<
+
+.PHONY: clean
+clean:
+	rm -f bucts $(OBJ)
diff --git a/util/bucts/README b/util/bucts/README
new file mode 100644
index 0000000..882ad12
--- /dev/null
+++ b/util/bucts/README
@@ -0,0 +1,51 @@
+What is bucts?
+--------------
+Many Intel southbridges provide a mechanism called Back Up Control Top Swap.
+This functionality allows to have the southbridge fetch the reset vector or
+the beginning of the bootblock at a 64K offset from the usual top of flash.
+
+This can be useful in different ways:
+- Have a backup bootblock in case of bootblock hacking;
+- Some vendor BIOS only write protect their bootblock so this tool makes it
+possible circumvent this protection to allow flashing and booting coreboot.
+
+The BUC.TS status is stored in a nvram bit. To clear it one has to remove
+the RTC battery.
+
+Operation Description
+---------------------
+First compile bucts by running make:
+	make
+
+bucts only takes 2 arguments: 0 or 1.
+- 0 is the southbridge's default and results in the bootblock being fetched at
+the usual top of flash
+- 1 enable BUC.TS and the bootblock is fetched at a 64K offset.
+
+Example of operation:
+	./bucts 1
+
+The BUC.TS functionality can also be disabled by the southbridge in which case
+bucts will output:
+	"BUC.TS is prevented from being changed."
+before attempting anything.
+
+Details
+-------
+Example Bootblock size of 64KB.
+
+FLASH (2M)			Memory Map BUC.TS=0		Memory Map BUC.TS=1
+|             |			|             |			|             |
+Z             Z			Z             Z			Z             Z
+|             |			|             |			|             |
+|             |			|             |			|             |
+|             |			|             |			|             |
++-------------+ -> 0x1E0000	+-------------+ -> 0xFFFE0000	+-------------+ -> 0xFFFE0000
+|             |			|             |			|             |
+| bootblock_1 |			| bootblock_1 |			| bootblock_0 |
+|             |			|             |			|             |
++-------------+ -> 0x1F0000	+-------------+ -> 0xFFFF0000	+-------------+ -> 0xFFFF0000
+|             |			|             |			|             |
+| bootblock_0 |			| bootblock_0 |			| bootblock_1 |
+|             |			|             |			|             |
++-------------+ -> 0x200000	+-------------+ -> 0xFFFFFFFF	+-------------+ -> 0xFFFFFFFF
diff --git a/util/bucts/bucts.c b/util/bucts/bucts.c
new file mode 100644
index 0000000..b11dea6
--- /dev/null
+++ b/util/bucts/bucts.c
@@ -0,0 +1,301 @@
+/* compile: gcc -s -o bucts bucts.c -lpci  or:  make
+ * run as root: ./bucts [0|1]
+ */
+/*
+ * Copyright (C) 2011 Peter Stuge <peter at stuge.se>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#if defined(__GLIBC__)
+#include <sys/io.h>
+#endif
+#include <pci/pci.h>
+
+#if defined (__sun) && (defined(__i386) || defined(__amd64))
+#define MEM_DEV "/dev/xsvc"
+#else
+#define MEM_DEV "/dev/mem"
+#endif
+
+#define BUC_OFFSET 0x3414
+#define GCS_OFFSET 0x3410
+
+static int fd_mem = -1;
+
+static void *sys_physmap(unsigned long phys_addr, size_t len)
+{
+	void *virt_addr =
+	    mmap(0, len, PROT_WRITE | PROT_READ, MAP_SHARED, fd_mem,
+		 (off_t) phys_addr);
+	return MAP_FAILED == virt_addr ? NULL : virt_addr;
+}
+
+static void physunmap(void *virt_addr, size_t len)
+{
+	if (!len) {
+		printf("Not unmapping zero size at %p\n", virt_addr);
+		return;
+	}
+	munmap(virt_addr, len);
+}
+
+static void *physmap(const char *descr, unsigned long phys_addr, size_t len)
+{
+	void *virt_addr;
+
+	if (!descr)
+		descr = "memory";
+
+	if (!len) {
+		printf("Not mapping %s, zero size at 0x%08lx.\n", descr,
+		       phys_addr);
+		return NULL;
+	}
+
+	if ((getpagesize() - 1) & len)
+		fprintf(stderr, "Unaligned size 0x%lx for %s at 0x%08lx!\n",
+			(unsigned long)len, descr, phys_addr);
+
+	if ((getpagesize() - 1) & phys_addr)
+		fprintf(stderr, "Unaligned address 0x%08lx for %s!\n",
+			phys_addr, descr);
+
+	virt_addr = sys_physmap(phys_addr, len);
+	if (!virt_addr) {
+		fprintf(stderr, "Error accessing 0x%lx bytes %s at 0x%08lx!\n",
+			(unsigned long)len, descr, phys_addr);
+		perror("mmap(" MEM_DEV ")");
+		if (EINVAL == errno) {
+			fprintf(stderr, "\n");
+			fprintf(stderr,
+				"In Linux this error can be caused by the CONFIG_NONPROMISC_DEVMEM (<2.6.27),\n");
+			fprintf(stderr,
+				"CONFIG_STRICT_DEVMEM (>=2.6.27) and CONFIG_X86_PAT kernel options.\n");
+			fprintf(stderr,
+				"Please check if either is enabled in your kernel before reporting a failure.\n");
+			fprintf(stderr,
+				"You can override CONFIG_X86_PAT at boot with the nopat kernel parameter but\n");
+			fprintf(stderr,
+				"disabling the other option unfortunately requires a kernel recompile. Sorry!\n");
+		}
+	}
+
+	return virt_addr;
+}
+
+int bucts(struct pci_dev *sb, int8_t newts)
+{
+	uint8_t buc, ts;
+	uint32_t rcba_addr;
+	volatile uint8_t *rcba;
+	const char *str[2] = {
+		"128kb address range 0xFFFE0000-0xFFFFFFFF is untranslated",
+		"64kb address ranges at 0xFFFE0000 and 0xFFFF0000 are swapped"
+	};
+
+	switch (sb->device_id) {
+	case 0x1c44: /* Z68 */
+	case 0x1c46: /* P67 */
+	case 0x1c47: /* UM67 */
+	case 0x1c49: /* HM68 */
+	case 0x1c4a: /* H67 */
+	case 0x1c4b: /* HM67 */
+	case 0x1c4c: /* Q65 */
+	case 0x1c4d: /* QS67 */
+	case 0x1c4e: /* Q67 */
+	case 0x1c4f: /* QM67 */
+	case 0x1c50: /* B65 */
+	case 0x1c52: /* C202 */
+	case 0x1c54: /* C204 */
+	case 0x1c56: /* C026 */
+	case 0x1c5c: /* H61 */
+	case 0x1d40: /* C60x/X79 */
+	case 0x1c41: /* C60x/X79 */
+	case 0x1e44: /* Z77 */
+	case 0x1e46: /* Z75 */
+	case 0x1e47: /* Q77 */
+	case 0x1e48: /* Q75 */
+	case 0x1e49: /* B75 */
+	case 0x1e4a: /* H77 */
+	case 0x1e53: /* C216 */
+	case 0x1e55: /* QM77 */
+	case 0x1e56: /* QS77 */
+	case 0x1e57: /* HM77 */
+	case 0x1e58: /* UM77 */
+	case 0x1e59: /* HM76 */
+	case 0x1e5d: /* HM75 */
+	case 0x1e5e: /* HM70 */
+	case 0x1e5f: /* NM70 */
+	case 0x2640: /* ICH6/ICH6R */
+	case 0x2641: /* ICH6-M */
+	case 0x2642: /* ICH6W/ICH6RW */
+	case 0x2670: /* 631xESB/632xESB/3100 */
+	case 0x27b0: /* ICH7DH */
+	case 0x27b8: /* ICH7/ICH7R */
+	case 0x27b9: /* ICH7M */
+	case 0x27bd: /* ICH7MDH */
+	case 0x27bc: /* NM10 */
+	case 0x2810: /* ICH8/ICH8R */
+	case 0x2811: /* ICH8M-E */
+	case 0x2812: /* ICH8DH */
+	case 0x2814: /* ICH8DO */
+	case 0x2815: /* ICH8M */
+	case 0x2910: /* ICH9 Eng. Sample */
+	case 0x2912: /* ICH9DH */
+	case 0x2914: /* ICH9DO */
+	case 0x2916: /* ICH9R */
+	case 0x2917: /* ICH9M-E */
+	case 0x2918: /* ICH9 */
+	case 0x2919: /* ICH9M */
+	case 0x3a10: /* ICH10R Eng. Sample */
+	case 0x3a14: /* ICH10D0 */
+	case 0x3a16: /* ICH10R */
+	case 0x3a18: /* ICH10 */
+	case 0x3a1a: /* ICH10D */
+	case 0x3a1e: /* ICH10 Eng. Sample*/
+	case 0x3b00: /* PCH 3400 Desktop */
+	case 0x3b01: /* PCH 3400 Mobile */
+	case 0x3b02: /* P55 */
+	case 0x3b03: /* PM55 */
+	case 0x3b06: /* H55 */
+	case 0x3b07: /* QM55 */
+	case 0x3b08: /* H57 */
+	case 0x3b09: /* HM55 */
+	case 0x3b0a: /* Q57 */
+	case 0x3b0b: /* HM57 */
+	case 0x3b0d: /* PCH 3400 Mobile SFF */
+	case 0x3b0e: /* B55 */
+	case 0x3b0f: /* QS57 */
+	case 0x3b12: /* PCH 3400 */
+	case 0x3b14: /* PCH 3420 */
+	case 0x3b16: /* PCH 3450 */
+	case 0x3b1e: /* B55 */
+	case 0x8c40: /* Lynx Point */
+	case 0x8c41: /* Lynx Point Mobile Eng. Sample */
+	case 0x8c42: /* Lynx Point Desktop Eng. Sample */
+	case 0x8c43: /* Lynx Point */
+	case 0x8c44: /* Z87 */
+	case 0x8c45: /* Lynx Point */
+		rcba_addr = pci_read_long(sb, 0xf0) & ~1;
+		break;
+	default:
+		fprintf(stderr, "Unsupported LPC bridge. Sorry.\n");
+		return 1;
+	}
+
+	rcba = physmap("RCBA", rcba_addr, 0x4000);
+	if (!rcba)
+		return 1;
+
+	buc = rcba[BUC_OFFSET];
+	ts = buc & 1;
+	if (rcba[GCS_OFFSET] & 1) {
+		printf("BUC.TS is prevented from being changed.\n");
+		goto unmap;
+	}
+
+	printf("Current BUC.TS=%d - %s\n", ts, str[ts]);
+
+	if (0 == newts || 1 == newts) {
+		if (ts == newts) {
+			printf
+			    ("Not writing BUC register since TS is already correct.\n");
+			goto unmap;
+		}
+		buc &= ~1;
+		buc |= newts;
+		rcba[0x3414] = buc;
+		buc = rcba[0x3414];
+		ts = buc & 1;
+		printf("Updated BUC.TS=%d - %s\n", ts, str[ts]);
+	}
+
+unmap:
+	physunmap((void *)rcba, 0x4000);
+	return 0;
+}
+
+int main(int argc, const char *argv[], const char *envp[])
+{
+	int ret;
+	char *endp;
+	int8_t newts = -1;
+	struct pci_access *pacc;
+	struct pci_dev *dev, *sb = NULL;
+#if defined(__FreeBSD__)
+	int io_fd;
+#endif
+
+	printf("bucts utility version '" VERSION "'\n");
+
+#if defined(__FreeBSD__)
+	if ((io_fd = open("/dev/io", O_RDWR)) < 0) {
+		perror("open(/dev/io)");
+#else
+	if (iopl(3)) {
+		perror("iopl");
+#endif
+		printf("You need to be root.\n");
+		return 1;
+	}
+
+	if (-1 == (fd_mem = open(MEM_DEV, O_RDWR | O_SYNC))) {
+		perror("Error: open(" MEM_DEV ")");
+		return 1;
+	}
+
+	pacc = pci_alloc();
+	pci_init(pacc);
+	pci_scan_bus(pacc);
+	for (dev = pacc->devices; dev && !sb; dev = dev->next) {
+		pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_CLASS);
+		if (dev->vendor_id != 0x8086 || dev->device_class != 0x0601)
+			continue;
+		sb = dev;
+	}
+	if (!sb) {
+		fprintf(stderr, "Error: LPC bridge not found!\n");
+		return 1;
+	}
+
+	printf("Using LPC bridge %04x:%04x at %02x%02x:%02x.%02x\n",
+	       sb->vendor_id, sb->device_id, sb->domain, sb->bus, sb->dev,
+	       sb->func);
+
+	if (argc > 1) {
+		newts = strtoul(argv[1], &endp, 10);
+		if (endp == argv[1]) {
+			fprintf(stderr,
+				"Invalid new TS value '%s', please specify 0 or 1.\n",
+				argv[1]);
+			newts = -1;
+		}
+	}
+
+	ret = bucts(sb, newts);
+
+	close(fd_mem);
+	return ret;
+}



More information about the coreboot-gerrit mailing list