Arthur Heymans (arthur@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@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@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@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; +}