[coreboot] New patch to review for filo: 4bd7d40 Add Flash lockdown for AMD SB600

Patrick Georgi (patrick@georgi-clan.de) gerrit at coreboot.org
Tue Feb 12 10:36:54 CET 2013


Patrick Georgi (patrick at georgi-clan.de) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/2367

-gerrit

commit 4bd7d40a31a81789c27a4a404d69b25366686faa
Author: Mathias Krause <mathias.krause at secunet.com>
Date:   Thu Feb 3 09:28:37 2011 +0100

    Add Flash lockdown for AMD SB600
    
    This directly drives the flash chip since chipset lockdown
    can be worked around using manual SPI access.
    
    Change-Id: I5e37acea4f40e952f9d97784897e3a0d054718aa
    Signed-off-by: Patrick Georgi <patrick.georgi at secunet.com>
    Signed-off-by: Mathias Krause <mathias.krause at secunet.com>
---
 drivers/Makefile.inc |    1 +
 drivers/amd.c        |  110 +++++
 drivers/intel.c      |    5 +-
 drivers/sb600.c      | 1245 ++++++++++++++++++++++++++++++++++++++++++++++++++
 x86/linux_load.c     |   14 +-
 5 files changed, 1370 insertions(+), 5 deletions(-)

diff --git a/drivers/Makefile.inc b/drivers/Makefile.inc
index f93c287..941c151 100644
--- a/drivers/Makefile.inc
+++ b/drivers/Makefile.inc
@@ -23,3 +23,4 @@ TARGETS-$(CONFIG_IDE_NEW_DISK) += drivers/ide_new.o
 TARGETS-$(CONFIG_VIA_SOUND) += drivers/via-sound.o
 TARGETS-$(CONFIG_USB_DISK) += drivers/usb.o
 TARGETS-$(CONFIG_TARGET_I386) += drivers/intel.o
+TARGETS-$(CONFIG_TARGET_I386) += drivers/amd.o drivers/sb600.o
diff --git a/drivers/amd.c b/drivers/amd.c
new file mode 100644
index 0000000..cac7c53
--- /dev/null
+++ b/drivers/amd.c
@@ -0,0 +1,110 @@
+/*
+ * This file is part of FILO.
+ *
+ * Copyright (C) 2010 secunet Security Networks AG
+ *
+ * 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 <libpayload.h>
+#include <config.h>
+#include <pci.h>
+
+#define DEBUG_THIS CONFIG_DEBUG_AMD
+#include <debug.h>
+
+#define ROM_BASE			0xfff00000	/* FIXME: should be read from chipset */
+#define ROM_CHUNK_SIZE		512 /* kB */
+#define SB600_LPC_BRIDGE	PCI_DEV(0, 0x14, 3)
+
+extern int sb600_spi_lockdown(void);
+
+
+int amd_lockdown_flash(void)
+{
+	u32 base = ROM_BASE;
+	u32 reg32, val32;
+	int spi, lpc;
+	int reg;
+
+	/* for now we only suppport the SB600 chipset */
+	reg32 = pci_read_config32(SB600_LPC_BRIDGE, REG_VENDOR_ID);
+	switch (reg32) {
+		case 0x438d1002:
+			break;
+
+		default:
+			debug("Not an SB600 chipset\n");
+			return -1;
+	}
+
+	/* Lock the SPI interface first */
+	debug("Locking SPI interface...\n");
+	spi = sb600_spi_lockdown();
+	if (spi == 0) {
+		debug("Flash chip SPI write protect enabled!\n");
+	} else {
+		debug("Failed to enable flash chip SPI write protect%s!\n",
+		      spi > 0 ? " (already locked)" : "");
+
+		/* if the chip was already locked, this might be no failure */
+		if (spi > 0) {
+			spi = 0;
+		}
+	}
+
+	/* Enable ROM write protect */
+	debug("Locking BIOS ROM range to read-only...\n");
+
+	/* LPC ROM read/write protect registers (50h, 54h, 58h, 5ch)
+	 * 31:11 = base address (2k aligned)
+	 *  10:2 = length-1 in kB (0=1kB, 511=512kB)
+	 *     1 = read protect bit (1 = read protect)
+	 *     0 = write protect bit (1 = write protect)
+	 *
+	 * => we need 2 of the 4 available registers to protect a chip sized 1MB
+	 */
+
+	lpc = -1;
+	for (reg = 0x50; reg <= 0x5c; reg += 4) {
+		val32  = base & ~((1 << 11) - 1);	// base address
+		val32 |= (ROM_CHUNK_SIZE - 1) << 2;	// size = 512kB
+		val32 |= 1;							// write protect
+
+		pci_write_config32(SB600_LPC_BRIDGE, reg, val32);
+		if (pci_read_config32(SB600_LPC_BRIDGE, reg) != val32) {
+			debug("Setting LPC ROM protection register 0x%x failed, trying to "
+			      "use another\n", reg);
+
+			continue;
+		}
+
+		if (base + ROM_CHUNK_SIZE * 1024 - 1 == 0xffffffff) {
+			lpc = 0;
+
+			break;
+		}
+
+		/* increase to the next chunk to protect */
+		base += ROM_CHUNK_SIZE * 1024;
+	}
+
+	if (lpc == 0) {
+		debug("LPC BIOS ROM range write protectedd until next full reset.\n");
+	} else {
+		debug("Failed to protect LPC BIOS ROM range!\n");
+	}
+
+	return spi | lpc;
+}
diff --git a/drivers/intel.c b/drivers/intel.c
index de61c2e..52430f3 100644
--- a/drivers/intel.c
+++ b/drivers/intel.c
@@ -44,7 +44,7 @@
 #define RCBA32(x) *((volatile u32 *)(DEFAULT_RCBA + x))
 
 
-void intel_lockdown_flash(void)
+int intel_lockdown_flash(void)
 {
 	u8 reg8;
 	u32 reg32;
@@ -58,7 +58,7 @@ void intel_lockdown_flash(void)
 		break;
 	default:
 		debug("Not an ICH7 southbridge\n");
-		return;
+		return -1;
 	}
 
 	/* Now try this: */
@@ -79,6 +79,7 @@ void intel_lockdown_flash(void)
 
 	debug("BIOS hard locked until next full reset.\n");
 
+	return 0;
 }
 
 
diff --git a/drivers/sb600.c b/drivers/sb600.c
new file mode 100644
index 0000000..cce0220
--- /dev/null
+++ b/drivers/sb600.c
@@ -0,0 +1,1245 @@
+/*
+ * This file is part of FILO, based on code from the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2004 Tyan Corp <yhlu at tyan.com>
+ * Copyright (C) 2005-2008 coresystems GmbH
+ * Copyright (C) 2008,2009 Carl-Daniel Hailfinger
+ * Copyright (C) 2010 secunet Security Networks AG
+ *
+ * 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
+ * (at your option) 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.
+ *
+ * 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 <pci/pci.h>
+#include <stdint.h>
+#include <stddef.h>
+
+/*
+ * libpayload quirks
+ */
+#include <libpayload.h>
+
+#define DEBUG_THIS CONFIG_DEBUG_AMD
+#include <debug.h>
+
+#define print(x,...)	debug(__VA_ARGS__)
+
+
+/*
+ * spi.h
+ */
+
+/* Read Electronic ID */
+#define JEDEC_RDID		0x9f
+#define JEDEC_RDID_OUTSIZE	0x01
+/* INSIZE may be 0x04 for some chips*/
+#define JEDEC_RDID_INSIZE	0x03
+
+/* Read Status Register */
+#define JEDEC_RDSR		0x05
+#define JEDEC_RDSR_OUTSIZE	0x01
+#define JEDEC_RDSR_INSIZE	0x01
+#define JEDEC_RDSR_BIT_WIP	(0x01 << 0)
+
+/* Write Status Enable */
+#define JEDEC_EWSR		0x50
+#define JEDEC_EWSR_OUTSIZE	0x01
+#define JEDEC_EWSR_INSIZE	0x00
+
+/* Write Status Register */
+#define JEDEC_WRSR		0x01
+#define JEDEC_WRSR_OUTSIZE	0x02
+#define JEDEC_WRSR_INSIZE	0x00
+
+/* Write Enable */
+#define JEDEC_WREN		0x06
+#define JEDEC_WREN_OUTSIZE	0x01
+#define JEDEC_WREN_INSIZE	0x00
+
+/* Error codes */
+#define SPI_GENERIC_ERROR	-1
+#define SPI_INVALID_OPCODE	-2
+#define SPI_INVALID_ADDRESS	-3
+#define SPI_INVALID_LENGTH	-4
+#define SPI_FLASHROM_BUG	-5
+#define SPI_PROGRAMMER_ERROR	-6
+
+
+/*
+ * flashchips.h
+ */
+
+#define GENERIC_MANUF_ID	0xffff	/* Check if there is a vendor ID */
+#define GENERIC_DEVICE_ID	0xffff	/* Only match the vendor ID */
+
+#define ATMEL_ID		0x1F	/* Atmel */
+#define ATMEL_AT26DF081A		0x4501
+
+/*
+ * flash.h
+ */
+
+/* Error codes */
+#define TIMEOUT_ERROR	-101
+
+typedef unsigned long chipaddr;
+
+static inline void programmer_delay(int usecs);
+
+enum chipbustype {
+	CHIP_BUSTYPE_NONE	= 0,
+	CHIP_BUSTYPE_PARALLEL	= 1 << 0,
+	CHIP_BUSTYPE_LPC	= 1 << 1,
+	CHIP_BUSTYPE_FWH	= 1 << 2,
+	CHIP_BUSTYPE_SPI	= 1 << 3,
+	CHIP_BUSTYPE_NONSPI	= CHIP_BUSTYPE_PARALLEL | CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH,
+	CHIP_BUSTYPE_UNKNOWN	= CHIP_BUSTYPE_PARALLEL | CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_SPI,
+};
+
+/*
+ * How many different contiguous runs of erase blocks with one size each do
+ * we have for a given erase function?
+ */
+#define NUM_ERASEREGIONS 5
+
+/*
+ * How many different erase functions do we have per chip?
+ * Atmel AT25FS010 has 6 different functions.
+ */
+#define NUM_ERASEFUNCTIONS 6
+
+#define FEATURE_REGISTERMAP	(1 << 0)
+#define FEATURE_BYTEWRITES	(1 << 1)
+#define FEATURE_LONG_RESET	(0 << 4)
+#define FEATURE_SHORT_RESET	(1 << 4)
+#define FEATURE_EITHER_RESET	FEATURE_LONG_RESET
+#define FEATURE_RESET_MASK	(FEATURE_LONG_RESET | FEATURE_SHORT_RESET)
+#define FEATURE_ADDR_FULL	(0 << 2)
+#define FEATURE_ADDR_MASK	(3 << 2)
+#define FEATURE_ADDR_2AA	(1 << 2)
+#define FEATURE_ADDR_AAA	(2 << 2)
+#define FEATURE_ADDR_SHIFTED	(1 << 5)
+#define FEATURE_WRSR_EWSR	(1 << 6)
+#define FEATURE_WRSR_WREN	(1 << 7)
+#define FEATURE_WRSR_EITHER	(FEATURE_WRSR_EWSR | FEATURE_WRSR_WREN)
+
+struct flashchip {
+	const char *vendor;
+	const char *name;
+
+	enum chipbustype bustype;
+
+	/*
+	 * With 32bit manufacture_id and model_id we can cover IDs up to
+	 * (including) the 4th bank of JEDEC JEP106W Standard Manufacturer's
+	 * Identification code.
+	 */
+	uint32_t manufacture_id;
+	uint32_t model_id;
+
+	int total_size;
+	int page_size;
+	int feature_bits;
+
+	/*
+	 * Indicate if flashrom has been tested with this flash chip and if
+	 * everything worked correctly.
+	 */
+	uint32_t tested;
+
+	int (*probe) (struct flashchip *flash);
+
+	/* Delay after "enter/exit ID mode" commands in microseconds. */
+	int probe_timing;
+
+	/*
+	 * Erase blocks and associated erase function. Any chip erase function
+	 * is stored as chip-sized virtual block together with said function.
+	 */
+	struct block_eraser {
+		struct eraseblock{
+			unsigned int size; /* Eraseblock size */
+			unsigned int count; /* Number of contiguous blocks with that size */
+		} eraseblocks[NUM_ERASEREGIONS];
+		int (*block_erase) (struct flashchip *flash, unsigned int blockaddr, unsigned int blocklen);
+	} block_erasers[NUM_ERASEFUNCTIONS];
+
+	int (*printlock) (struct flashchip *flash);
+	int (*unlock) (struct flashchip *flash);
+	int (*lock) (struct flashchip *flash);
+	int (*write) (struct flashchip *flash, uint8_t *buf, int start, int len);
+	int (*read) (struct flashchip *flash, uint8_t *buf, int start, int len);
+
+	/* Some flash devices have an additional register space. */
+	chipaddr virtual_memory;
+	chipaddr virtual_registers;
+};
+
+#define TEST_UNTESTED	0
+
+#define TEST_OK_PROBE	(1 << 0)
+#define TEST_OK_READ	(1 << 1)
+#define TEST_OK_ERASE	(1 << 2)
+#define TEST_OK_WRITE	(1 << 3)
+#define TEST_OK_PR	(TEST_OK_PROBE | TEST_OK_READ)
+#define TEST_OK_PRE	(TEST_OK_PROBE | TEST_OK_READ | TEST_OK_ERASE)
+#define TEST_OK_PRW	(TEST_OK_PROBE | TEST_OK_READ | TEST_OK_WRITE)
+#define TEST_OK_PREW	(TEST_OK_PROBE | TEST_OK_READ | TEST_OK_ERASE | TEST_OK_WRITE)
+#define TEST_OK_MASK	0x0f
+
+#define TEST_BAD_PROBE	(1 << 4)
+#define TEST_BAD_READ	(1 << 5)
+#define TEST_BAD_ERASE	(1 << 6)
+#define TEST_BAD_WRITE	(1 << 7)
+#define TEST_BAD_PREW	(TEST_BAD_PROBE | TEST_BAD_READ | TEST_BAD_ERASE | TEST_BAD_WRITE)
+#define TEST_BAD_MASK	0xf0
+
+/* Timing used in probe routines. ZERO is -2 to differentiate between an unset
+ * field and zero delay.
+ * 
+ * SPI devices will always have zero delay and ignore this field.
+ */
+#define TIMING_FIXME	-1
+/* this is intentionally same value as fixme */
+#define TIMING_IGNORED	-1
+#define TIMING_ZERO	-2
+
+/* Something happened that shouldn't happen, but we can go on */
+#define ERROR_NONFATAL 0x100
+
+#define MSG_ERROR	0
+#define MSG_INFO	1
+#define MSG_DEBUG	2
+#define MSG_BARF	3
+#define msg_gerr(...)	print(MSG_ERROR, __VA_ARGS__)	/* general errors */
+#define msg_perr(...)	print(MSG_ERROR, __VA_ARGS__)	/* programmer errors */
+#define msg_cerr(...)	print(MSG_ERROR, __VA_ARGS__)	/* chip errors */
+#define msg_ginfo(...)	print(MSG_INFO, __VA_ARGS__)	/* general info */
+#define msg_pinfo(...)	print(MSG_INFO, __VA_ARGS__)	/* programmer info */
+#define msg_cinfo(...)	print(MSG_INFO, __VA_ARGS__)	/* chip info */
+#define msg_gdbg(...)	print(MSG_DEBUG, __VA_ARGS__)	/* general debug */
+#define msg_pdbg(...)	print(MSG_DEBUG, __VA_ARGS__)	/* programmer debug */
+#define msg_cdbg(...)	print(MSG_DEBUG, __VA_ARGS__)	/* chip debug */
+#define msg_gspew(...)	print(MSG_BARF, __VA_ARGS__)	/* general debug barf  */
+#define msg_pspew(...)	print(MSG_BARF, __VA_ARGS__)	/* programmer debug barf  */
+#define msg_cspew(...)	print(MSG_BARF, __VA_ARGS__)	/* chip debug barf  */
+
+struct spi_command {
+	unsigned int writecnt;
+	unsigned int readcnt;
+	const unsigned char *writearr;
+	unsigned char *readarr;
+};
+
+
+/*
+ * programmer.h
+ */
+
+enum spi_controller {
+	SPI_CONTROLLER_NONE,
+	SPI_CONTROLLER_SB600,
+};
+
+struct spi_programmer {
+	int (*command)(unsigned int writecnt, unsigned int readcnt,
+		   const unsigned char *writearr, unsigned char *readarr);
+	int (*multicommand)(struct spi_command *cmds);
+
+	/* Optimized functions for this programmer */
+	int (*read)(struct flashchip *flash, uint8_t *buf, int start, int len);
+	int (*write_256)(struct flashchip *flash, uint8_t *buf, int start, int len);
+};
+
+static int default_spi_send_multicommand(struct spi_command *cmds);
+
+#define rpci_write_byte	pci_write_byte
+
+static int sb600_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+		      const unsigned char *writearr, unsigned char *readarr);
+static int sb600_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len);
+static int sb600_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len);
+
+
+/*
+ * chipdrivers.h
+ */
+
+
+static uint8_t spi_read_status_register(void);
+
+
+/*
+ * hwaccess.h
+ */
+
+
+#define OUTB outb
+#define OUTW outw
+#define OUTL outl
+#define INB  inb
+#define INW  inw
+#define INL  inl
+
+
+/*
+ * jedec.c
+ */
+
+/* Check one byte for odd parity */
+static uint8_t oddparity(uint8_t val)
+{
+	val = (val ^ (val >> 4)) & 0xf;
+	val = (val ^ (val >> 2)) & 0x3;
+	return (val ^ (val >> 1)) & 0x1;
+}
+
+
+/*
+ * spi.c
+ */
+
+static enum spi_controller spi_controller = SPI_CONTROLLER_NONE;
+
+static const struct spi_programmer spi_programmer[] = {
+	{ /* SPI_CONTROLLER_NONE */
+		.command = NULL,
+		.multicommand = NULL,
+		.read = NULL,
+		.write_256 = NULL,
+	},
+
+	{ /* SPI_CONTROLLER_SB600 */
+		.command = sb600_spi_send_command,
+		.multicommand = default_spi_send_multicommand,
+		.read = sb600_spi_read,
+		.write_256 = sb600_spi_write_256,
+	},
+};
+
+static int spi_send_command(unsigned int writecnt, unsigned int readcnt,
+		const unsigned char *writearr, unsigned char *readarr)
+{
+	if (!spi_programmer[spi_controller].command) {
+		msg_perr("%s called, but SPI is unsupported on this "
+			 "hardware. Please report a bug at "
+			 "flashrom at flashrom.org\n", __func__);
+		return 1;
+	}
+
+	return spi_programmer[spi_controller].command(writecnt, readcnt,
+						      writearr, readarr);
+}
+
+static int spi_send_multicommand(struct spi_command *cmds)
+{
+	if (!spi_programmer[spi_controller].multicommand) {
+		msg_perr("%s called, but SPI is unsupported on this "
+			 "hardware. Please report a bug at "
+			 "flashrom at flashrom.org\n", __func__);
+		return 1;
+	}
+
+	return spi_programmer[spi_controller].multicommand(cmds);
+}
+
+static int default_spi_send_multicommand(struct spi_command *cmds)
+{
+	int result = 0;
+	for (; (cmds->writecnt || cmds->readcnt) && !result; cmds++) {
+		result = spi_send_command(cmds->writecnt, cmds->readcnt,
+					  cmds->writearr, cmds->readarr);
+	}
+	return result;
+}
+
+
+/*
+ * spi25.c
+ */
+
+static int spi_rdid(unsigned char *readarr, int bytes)
+{
+	static const unsigned char cmd[JEDEC_RDID_OUTSIZE] = { JEDEC_RDID };
+	int ret;
+	int i;
+
+	ret = spi_send_command(sizeof(cmd), bytes, cmd, readarr);
+	if (ret)
+		return ret;
+	msg_cspew("RDID returned");
+	for (i = 0; i < bytes; i++)
+		msg_cspew(" 0x%02x", readarr[i]);
+	msg_cspew(". ");
+	return 0;
+}
+
+static void spi_prettyprint_status_register(struct flashchip *flash)
+{
+	uint8_t status;
+
+	status = spi_read_status_register();
+	msg_cdbg("Chip status register is %02x\n", status);
+}
+
+
+static int probe_spi_rdid_generic(struct flashchip *flash, int bytes)
+{
+	unsigned char readarr[4];
+	uint32_t id1;
+	uint32_t id2;
+
+	if (spi_rdid(readarr, bytes))
+		return 0;
+
+	if (!oddparity(readarr[0]))
+		msg_cdbg("RDID byte 0 parity violation. ");
+
+	/* Check if this is a continuation vendor ID.
+	 * FIXME: Handle continuation device IDs.
+	 */
+	if (readarr[0] == 0x7f) {
+		if (!oddparity(readarr[1]))
+			msg_cdbg("RDID byte 1 parity violation. ");
+		id1 = (readarr[0] << 8) | readarr[1];
+		id2 = readarr[2];
+		if (bytes > 3) {
+			id2 <<= 8;
+			id2 |= readarr[3];
+		}
+	} else {
+		id1 = readarr[0];
+		id2 = (readarr[1] << 8) | readarr[2];
+	}
+
+	msg_cdbg("%s: id1 0x%02x, id2 0x%02x\n", __func__, id1, id2);
+
+	if (id1 == flash->manufacture_id && id2 == flash->model_id) {
+		/* Print the status register to tell the
+		 * user about possible write protection.
+		 */
+		spi_prettyprint_status_register(flash);
+
+		return 1;
+	}
+
+	/* Test if this is a pure vendor match. */
+	if (id1 == flash->manufacture_id &&
+	    GENERIC_DEVICE_ID == flash->model_id)
+		return 1;
+
+	/* Test if there is any vendor ID. */
+	if (GENERIC_MANUF_ID == flash->manufacture_id &&
+	    id1 != 0xff)
+		return 1;
+
+	return 0;
+}
+
+static int probe_spi_rdid(struct flashchip *flash)
+{
+	return probe_spi_rdid_generic(flash, 3);
+}
+
+static uint8_t spi_read_status_register(void)
+{
+	static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { JEDEC_RDSR };
+	/* FIXME: No workarounds for driver/hardware bugs in generic code. */
+	unsigned char readarr[2]; /* JEDEC_RDSR_INSIZE=1 but wbsio needs 2 */
+	int ret;
+
+	/* Read Status Register */
+	ret = spi_send_command(sizeof(cmd), sizeof(readarr), cmd, readarr);
+	if (ret)
+		msg_cerr("RDSR failed!\n");
+
+	return readarr[0];
+}
+
+/*
+ * This is according the SST25VF016 datasheet, who knows it is more
+ * generic that this...
+ */
+static int spi_write_status_register_ewsr(struct flashchip *flash, int status)
+{
+	int result;
+	int i = 0;
+	struct spi_command cmds[] = {
+	{
+	/* WRSR requires either EWSR or WREN depending on chip type. */
+		.writecnt	= JEDEC_EWSR_OUTSIZE,
+		.writearr	= (const unsigned char[]){ JEDEC_EWSR },
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= JEDEC_WRSR_OUTSIZE,
+		.writearr	= (const unsigned char[]){ JEDEC_WRSR, (unsigned char) status },
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= 0,
+		.writearr	= NULL,
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}};
+
+	result = spi_send_multicommand(cmds);
+	if (result) {
+		msg_cerr("%s failed during command execution\n",
+			__func__);
+		/* No point in waiting for the command to complete if execution
+		 * failed.
+		 */
+		return result;
+	}
+	/* WRSR performs a self-timed erase before the changes take effect.
+	 * This may take 50-85 ms in most cases, and some chips apparently
+	 * allow running RDSR only once. Therefore pick an initial delay of
+	 * 100 ms, then wait in 10 ms steps until a total of 5 s have elapsed.
+	 */
+	programmer_delay(100 * 1000);
+	while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP) {
+		if (++i > 490) {
+			msg_cerr("Error: WIP bit after WRSR never cleared\n");
+			return TIMEOUT_ERROR;
+		}
+		programmer_delay(10 * 1000);
+	}
+	return 0;
+}
+
+static int spi_write_status_register_wren(struct flashchip *flash, int status)
+{
+	int result;
+	int i = 0;
+	struct spi_command cmds[] = {
+	{
+	/* WRSR requires either EWSR or WREN depending on chip type. */
+		.writecnt	= JEDEC_WREN_OUTSIZE,
+		.writearr	= (const unsigned char[]){ JEDEC_WREN },
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= JEDEC_WRSR_OUTSIZE,
+		.writearr	= (const unsigned char[]){ JEDEC_WRSR, (unsigned char) status },
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}, {
+		.writecnt	= 0,
+		.writearr	= NULL,
+		.readcnt	= 0,
+		.readarr	= NULL,
+	}};
+
+	result = spi_send_multicommand(cmds);
+	if (result) {
+		msg_cerr("%s failed during command execution\n",
+			__func__);
+		/* No point in waiting for the command to complete if execution
+		 * failed.
+		 */
+		return result;
+	}
+	/* WRSR performs a self-timed erase before the changes take effect.
+	 * This may take 50-85 ms in most cases, and some chips apparently
+	 * allow running RDSR only once. Therefore pick an initial delay of
+	 * 100 ms, then wait in 10 ms steps until a total of 5 s have elapsed.
+	 */
+	programmer_delay(100 * 1000);
+	while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP) {
+		if (++i > 490) {
+			msg_cerr("Error: WIP bit after WRSR never cleared\n");
+			return TIMEOUT_ERROR;
+		}
+		programmer_delay(10 * 1000);
+	}
+	return 0;
+}
+
+static int spi_write_status_register(struct flashchip *flash, int status)
+{
+	int ret = 1;
+
+	if (!(flash->feature_bits & (FEATURE_WRSR_WREN | FEATURE_WRSR_EWSR))) {
+		msg_cdbg("Missing status register write definition, assuming "
+			 "EWSR is needed\n");
+		flash->feature_bits |= FEATURE_WRSR_EWSR;
+	}
+	if (flash->feature_bits & FEATURE_WRSR_WREN)
+		ret = spi_write_status_register_wren(flash, status);
+	if (ret && (flash->feature_bits & FEATURE_WRSR_EWSR))
+		ret = spi_write_status_register_ewsr(flash, status);
+	return ret;
+}
+
+static int spi_enable_blockprotect_at25df(struct flashchip *flash)
+{
+	uint8_t status;
+	int result;
+
+	/* Reset SPRL when needed to be able to modify status register */
+	status = spi_read_status_register();
+	if (status & (1 << 7)) {
+		msg_cdbg("Need to disable Sector Protection Register Lock\n");
+		if ((status & (1 << 4)) == 0) {
+			msg_cerr("WP# pin is active, disabling "
+				 "write protection is impossible.\n");
+			return 1;
+		}
+		/* All bits except bit 7 (SPRL) are readonly. */
+		result = spi_write_status_register(flash, status & ~(1 << 7));
+		if (result) {
+			msg_cerr("spi_write_status_register failed\n");
+			return result;
+		}
+
+	}
+	/* Global protect. Make sure to set SPRL as well. */
+	result = spi_write_status_register(flash, status | 0xbc);
+	if (result) {
+		msg_cerr("spi_write_status_register failed\n");
+		return result;
+	}
+	status = spi_read_status_register();
+	if ((status & (3 << 2)) != (3 << 2)) {
+		msg_cerr("Block protection could not be enabled!\n");
+		return 1;
+	}
+	msg_cinfo("Block protection enabled, %s locked!\n",
+		  (status & (1 << 4)) ? "software" : "hardware");
+	return 0;
+}
+
+
+/*
+ * hwaccess.c
+ */
+
+static inline void mmio_writeb(uint8_t val, void *addr)
+{
+	*(volatile uint8_t *) addr = val;
+}
+
+static inline void mmio_writew(uint16_t val, void *addr)
+{
+	*(volatile uint16_t *) addr = val;
+}
+
+static inline void mmio_writel(uint32_t val, void *addr)
+{
+	*(volatile uint32_t *) addr = val;
+}
+
+static inline uint8_t mmio_readb(void *addr)
+{
+	return *(volatile uint8_t *) addr;
+}
+
+static inline uint16_t mmio_readw(void *addr)
+{
+	return *(volatile uint16_t *) addr;
+}
+
+static inline uint32_t mmio_readl(void *addr)
+{
+	return *(volatile uint32_t *) addr;
+}
+
+
+/*
+ * physmap.c
+ */
+
+static inline void *physmap(const char *descr, unsigned long phys_addr,
+                            size_t len)
+{
+	return phys_to_virt(phys_addr);
+}
+
+static inline void physunmap(void *virt_addr, size_t len)
+{
+}
+
+
+/*
+ * internal.c
+ */
+
+static struct pci_dev *pci_dev_find(uint16_t vendor, uint16_t device)
+{
+	static struct pci_access *pacc;
+	struct pci_dev *temp;
+	struct pci_filter filter;
+
+	if (pacc == NULL) {
+		pacc = pci_alloc();     /* Get the pci_access structure */
+		pci_init(pacc);         /* Initialize the PCI library */
+		pci_scan_bus(pacc);     /* We want to get the list of devices */
+	}
+
+	pci_filter_init(NULL, &filter);
+	filter.vendor = vendor;
+	filter.device = device;
+
+	for (temp = pacc->devices; temp; temp = temp->next)
+		if (pci_filter_match(&filter, temp))
+			return temp;
+
+	return NULL;
+}
+
+
+/*
+ * flashchips.c
+ */
+
+
+/**
+ * List of supported flash chips.
+ *
+ * Please keep the list sorted by vendor name and chip name, so that
+ * the output of 'flashrom -L' is alphabetically sorted.
+ */
+static struct flashchip flashchips[] = {
+	{
+		.vendor		= "Atmel",
+		.name		= "AT26DF081A",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= ATMEL_ID,
+		.model_id	= ATMEL_AT26DF081A,
+		.total_size	= 1024,
+		.page_size	= 256,
+		.feature_bits	= FEATURE_WRSR_WREN,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_spi_rdid,
+		.probe_timing	= TIMING_ZERO,
+#if 0			/* not needed */
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 256} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {32 * 1024, 32} },
+				.block_erase = spi_block_erase_52,
+			}, {
+				.eraseblocks = { {64 * 1024, 16} },
+				.block_erase = spi_block_erase_d8,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_60,
+			}, {
+				.eraseblocks = { {1024 * 1024, 1} },
+				.block_erase = spi_block_erase_c7,
+			}
+		},
+		.unlock		= spi_disable_blockprotect_at25df,
+#endif
+		.lock		= spi_enable_blockprotect_at25df,
+#if 0			/* not needed */
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+#endif
+	},
+
+	{ NULL 	}
+};
+
+
+/*
+ * flashrom.c
+ */
+
+/* Supported buses for the current programmer. */
+static enum chipbustype buses_supported;
+
+/* Is writing allowed with this programmer? */
+static int programmer_may_write;
+
+/* If nonzero, used as the start address of bottom-aligned flash. */
+static unsigned long flashbase;
+
+static inline void programmer_delay(int usecs)
+{
+	udelay(usecs);
+}
+
+static struct flashchip *probe_flash(struct flashchip *first_flash, int force)
+{
+	struct flashchip *flash;
+	unsigned long base = 0;
+	char location[64];
+	uint32_t size;
+	enum chipbustype buses_common;
+
+	for (flash = first_flash; flash && flash->name; flash++) {
+		msg_gdbg("Probing for %s %s, %d KB: ",
+			     flash->vendor, flash->name, flash->total_size);
+		if (!flash->probe && !force) {
+			msg_gdbg("failed! flashrom has no probe function for "
+				 "this flash chip.\n");
+			continue;
+		}
+		buses_common = buses_supported & flash->bustype;
+		if (!buses_common) {
+			msg_gdbg("skipped.");
+			msg_gspew(" Host bus type and chip bus type are incompatible.");
+			msg_gdbg("\n");
+			continue;
+		}
+
+		size = flash->total_size * 1024;
+
+		base = flashbase ? flashbase : (0xffffffff - size + 1);
+		flash->virtual_memory = (chipaddr)physmap("flash chip", base, size);
+
+		if (force)
+			break;
+
+		if (flash->probe(flash) != 1)
+			goto notfound;
+
+		if (first_flash == flashchips
+		    || flash->model_id != GENERIC_DEVICE_ID)
+			break;
+
+notfound:
+		physunmap((void *)flash->virtual_memory, size);
+	}
+
+	if (!flash || !flash->name)
+		return NULL;
+
+	snprintf(location, sizeof(location), "at physical address 0x%lx", base);
+
+	msg_cinfo("%s chip \"%s %s\" (%d KB) %s.\n",
+	       force ? "Assuming" : "Found",
+	       flash->vendor, flash->name, flash->total_size, location);
+
+	/* Flash registers will not be mapped if the chip was forced. Lock info
+	 * may be stored in registers, so avoid lock info printing.
+	 */
+	if (!force)
+		if (flash->printlock)
+			flash->printlock(flash);
+
+	return flash;
+}
+
+
+/*
+ * sb600spi.c
+ */
+
+static uint8_t *sb600_spibar = NULL;
+
+static int sb600_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len)
+{
+	/* not needed */
+	msg_perr("%s: unsupported!\n", __func__);
+	return SPI_PROGRAMMER_ERROR;
+}
+
+static int sb600_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
+{
+	/* not needed */
+	msg_perr("%s: unsupported!\n", __func__);
+	return SPI_PROGRAMMER_ERROR;
+}
+
+static void reset_internal_fifo_pointer(void)
+{
+	mmio_writeb(mmio_readb(sb600_spibar + 2) | 0x10, sb600_spibar + 2);
+
+	/* FIXME: This loop makes no sense at all. */
+	while (mmio_readb(sb600_spibar + 0xD) & 0x7)
+		msg_pspew("reset\n");
+}
+
+static int compare_internal_fifo_pointer(uint8_t want)
+{
+	uint8_t tmp;
+
+	tmp = mmio_readb(sb600_spibar + 0xd) & 0x07;
+	want &= 0x7;
+	if (want != tmp) {
+		msg_perr("SB600 FIFO pointer corruption! Pointer is %d, wanted "
+			 "%d\n", tmp, want);
+		msg_perr("Something else is accessing the flash chip and "
+			 "causes random corruption.\nPlease stop all "
+			 "applications and drivers and IPMI which access the "
+			 "flash chip.\n");
+		return 1;
+	} else {
+		msg_pspew("SB600 FIFO pointer is %d, wanted %d\n", tmp, want);
+		return 0;
+	}
+}
+
+static int reset_compare_internal_fifo_pointer(uint8_t want)
+{
+	int ret;
+
+	ret = compare_internal_fifo_pointer(want);
+	reset_internal_fifo_pointer();
+	return ret;
+}
+
+static void execute_command(void)
+{
+	mmio_writeb(mmio_readb(sb600_spibar + 2) | 1, sb600_spibar + 2);
+
+	while (mmio_readb(sb600_spibar + 2) & 1)
+		;
+}
+
+static int sb600_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+		      const unsigned char *writearr, unsigned char *readarr)
+{
+	int count;
+	/* First byte is cmd which can not being sent through FIFO. */
+	unsigned char cmd = *writearr++;
+	unsigned int readoffby1;
+	unsigned char readwrite;
+
+	writecnt--;
+
+	msg_pspew("%s, cmd=%x, writecnt=%x, readcnt=%x\n",
+		  __func__, cmd, writecnt, readcnt);
+
+	if (readcnt > 8) {
+		msg_pinfo("%s, SB600 SPI controller can not receive %d bytes, "
+		       "it is limited to 8 bytes\n", __func__, readcnt);
+		return SPI_INVALID_LENGTH;
+	}
+
+	if (writecnt > 8) {
+		msg_pinfo("%s, SB600 SPI controller can not send %d bytes, "
+		       "it is limited to 8 bytes\n", __func__, writecnt);
+		return SPI_INVALID_LENGTH;
+	}
+
+	/* This is a workaround for a bug in SB600 and SB700. If we only send
+	 * an opcode and no additional data/address, the SPI controller will
+	 * read one byte too few from the chip. Basically, the last byte of
+	 * the chip response is discarded and will not end up in the FIFO.
+	 * It is unclear if the CS# line is set high too early as well.
+	 */
+	readoffby1 = (writecnt) ? 0 : 1;
+	readwrite = (readcnt + readoffby1) << 4 | (writecnt);
+	mmio_writeb(readwrite, sb600_spibar + 1);
+	mmio_writeb(cmd, sb600_spibar + 0);
+
+	/* Before we use the FIFO, reset it first. */
+	reset_internal_fifo_pointer();
+
+	/* Send the write byte to FIFO. */
+	msg_pspew("Writing: ");
+	for (count = 0; count < writecnt; count++, writearr++) {
+		msg_pspew("[%02x]", *writearr);
+		mmio_writeb(*writearr, sb600_spibar + 0xC);
+	}
+	msg_pspew("\n");
+
+	/*
+	 * We should send the data by sequence, which means we need to reset
+	 * the FIFO pointer to the first byte we want to send.
+	 */
+	if (reset_compare_internal_fifo_pointer(writecnt))
+		return SPI_PROGRAMMER_ERROR;
+
+	msg_pspew("Executing: \n");
+	execute_command();
+
+	/*
+	 * After the command executed, we should find out the index of the
+	 * received byte. Here we just reset the FIFO pointer and skip the
+	 * writecnt.
+	 * It would be possible to increase the FIFO pointer by one instead
+	 * of reading and discarding one byte from the FIFO.
+	 * The FIFO is implemented on top of an 8 byte ring buffer and the
+	 * buffer is never cleared. For every byte that is shifted out after
+	 * the opcode, the FIFO already stores the response from the chip.
+	 * Usually, the chip will respond with 0x00 or 0xff.
+	 */
+	if (reset_compare_internal_fifo_pointer(writecnt + readcnt))
+		return SPI_PROGRAMMER_ERROR;
+
+	/* Skip the bytes we sent. */
+	msg_pspew("Skipping: ");
+	for (count = 0; count < writecnt; count++) {
+		cmd = mmio_readb(sb600_spibar + 0xC);
+		msg_pspew("[%02x]", cmd);
+	}
+	msg_pspew("\n");
+	if (compare_internal_fifo_pointer(writecnt))
+		return SPI_PROGRAMMER_ERROR;
+
+	msg_pspew("Reading: ");
+	for (count = 0; count < readcnt; count++, readarr++) {
+		*readarr = mmio_readb(sb600_spibar + 0xC);
+		msg_pspew("[%02x]", *readarr);
+	}
+	msg_pspew("\n");
+	if (reset_compare_internal_fifo_pointer(readcnt + writecnt))
+		return SPI_PROGRAMMER_ERROR;
+
+	if (mmio_readb(sb600_spibar + 1) != readwrite) {
+		msg_perr("Unexpected change in SB600 read/write count!\n");
+		msg_perr("Something else is accessing the flash chip and "
+			 "causes random corruption.\nPlease stop all "
+			 "applications and drivers and IPMI which access the "
+			 "flash chip.\n");
+		return SPI_PROGRAMMER_ERROR;
+	}
+
+	return 0;
+}
+
+static int sb600_probe_spi(struct pci_dev *dev)
+{
+	struct pci_dev *smbus_dev;
+	uint32_t tmp;
+	uint8_t reg;
+	static const char *const speed_names[4] = {
+		"Reserved", "33", "22", "16.5"
+	};
+
+	/* Read SPI_BaseAddr */
+	tmp = pci_read_long(dev, 0xa0);
+	tmp &= 0xffffffe0;	/* remove bits 4-0 (reserved) */
+	msg_pdbg("SPI base address is at 0x%x\n", tmp);
+
+	/* If the BAR has address 0, it is unlikely SPI is used. */
+	if (!tmp)
+		return 0;
+
+	/* Physical memory has to be mapped at page (4k) boundaries. */
+	sb600_spibar = physmap("SB600 SPI registers", tmp & 0xfffff000,
+			       0x1000);
+	/* The low bits of the SPI base address are used as offset into
+	 * the mapped page.
+	 */
+	sb600_spibar += tmp & 0xfff;
+
+	tmp = pci_read_long(dev, 0xa0);
+	msg_pdbg("AltSpiCSEnable=%i, SpiRomEnable=%i, "
+		     "AbortEnable=%i\n", tmp & 0x1, (tmp & 0x2) >> 1,
+		     (tmp & 0x4) >> 2);
+	tmp = (pci_read_byte(dev, 0xba) & 0x4) >> 2;
+	msg_pdbg("PrefetchEnSPIFromIMC=%i, ", tmp);
+
+	tmp = pci_read_byte(dev, 0xbb);
+	/* FIXME: Set bit 3,6,7 if not already set.
+	 * Set bit 5, otherwise SPI accesses are pointless in LPC mode.
+	 * See doc 42413 AMD SB700/710/750 RPR.
+	 */
+	msg_pdbg("PrefetchEnSPIFromHost=%i, SpiOpEnInLpcMode=%i\n",
+		     tmp & 0x1, (tmp & 0x20) >> 5);
+	tmp = mmio_readl(sb600_spibar);
+	/* FIXME: If SpiAccessMacRomEn or SpiHostAccessRomEn are zero on
+	 * SB700 or later, reads and writes will be corrupted. Abort in this
+	 * case. Make sure to avoid this check on SB600.
+	 */
+	msg_pdbg("SpiArbEnable=%i, SpiAccessMacRomEn=%i, "
+		     "SpiHostAccessRomEn=%i, ArbWaitCount=%i, "
+		     "SpiBridgeDisable=%i, DropOneClkOnRd=%i\n",
+		     (tmp >> 19) & 0x1, (tmp >> 22) & 0x1,
+		     (tmp >> 23) & 0x1, (tmp >> 24) & 0x7,
+		     (tmp >> 27) & 0x1, (tmp >> 28) & 0x1);
+	tmp = (mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3;
+	msg_pdbg("NormSpeed is %s MHz\n", speed_names[tmp]);
+
+	/* Look for the SMBus device. */
+	smbus_dev = pci_dev_find(0x1002, 0x4385);
+
+	if (!smbus_dev) {
+		msg_perr("ERROR: SMBus device not found. Not enabling SPI.\n");
+		return ERROR_NONFATAL;
+	}
+
+	/* Note about the bit tests below: If a bit is zero, the GPIO is SPI. */
+	/* GPIO11/SPI_DO and GPIO12/SPI_DI status */
+	reg = pci_read_byte(smbus_dev, 0xAB);
+	reg &= 0xC0;
+	msg_pdbg("GPIO11 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_DO");
+	msg_pdbg("GPIO12 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_DI");
+	if (reg != 0x00) {
+		msg_pdbg("Not enabling SPI");
+		return 0;
+	}
+	/* GPIO31/SPI_HOLD and GPIO32/SPI_CS status */
+	reg = pci_read_byte(smbus_dev, 0x83);
+	reg &= 0xC0;
+	msg_pdbg("GPIO31 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_HOLD");
+	msg_pdbg("GPIO32 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_CS");
+	/* SPI_HOLD is not used on all boards, filter it out. */
+	if ((reg & 0x80) != 0x00) {
+		msg_pdbg("Not enabling SPI");
+		return 0;
+	}
+	/* GPIO47/SPI_CLK status */
+	reg = pci_read_byte(smbus_dev, 0xA7);
+	reg &= 0x40;
+	msg_pdbg("GPIO47 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_CLK");
+	if (reg != 0x00) {
+		msg_pdbg("Not enabling SPI");
+		return 0;
+	}
+
+	reg = pci_read_byte(dev, 0x40);
+	msg_pdbg("SB700 IMC is %sactive.\n", (reg & (1 << 7)) ? "" : "not ");
+	if (reg & (1 << 7)) {
+		/* If we touch any region used by the IMC, the IMC and the SPI
+		 * interface will lock up, and the only way to recover is a
+		 * hard reset, but that is a bad choice for a half-erased or
+		 * half-written flash chip.
+		 * There appears to be an undocumented register which can freeze
+		 * or disable the IMC, but for now we want to play it safe.
+		 */
+		msg_perr("The SB700 IMC is active and may interfere with SPI "
+			 "commands. Disabling write.\n");
+		/* FIXME: Should we only disable SPI writes, or will the lockup
+		 * affect LPC/FWH chips as well?
+		 */
+		programmer_may_write = 0;
+	}
+
+	/* Bring the FIFO to a clean state. */
+	reset_internal_fifo_pointer();
+
+	buses_supported |= CHIP_BUSTYPE_SPI;
+	spi_controller = SPI_CONTROLLER_SB600;
+	return 0;
+}
+
+
+/*
+ * chipset_enable.c 
+ */
+
+static int enable_flash_sb600(struct pci_dev *dev)
+{
+	uint32_t prot;
+	uint8_t reg;
+	int ret;
+
+	/* Clear ROM protect 0-3. */
+	for (reg = 0x50; reg < 0x60; reg += 4) {
+		prot = pci_read_long(dev, reg);
+		/* No protection flags for this region?*/
+		if ((prot & 0x3) == 0)
+			continue;
+		msg_pinfo("SB600 %s%sprotected from 0x%08x to 0x%08x\n",
+			(prot & 0x1) ? "write " : "",
+			(prot & 0x2) ? "read " : "",
+			(prot & 0xfffff800),
+			(prot & 0xfffff800) + (((prot & 0x7fc) << 8) | 0x3ff));
+		prot &= 0xfffffffc;
+		rpci_write_byte(dev, reg, prot);
+		prot = pci_read_long(dev, reg);
+		if (prot & 0x3)
+			msg_perr("SB600 %s%sunprotect failed from 0x%08x to 0x%08x\n",
+				(prot & 0x1) ? "write " : "",
+				(prot & 0x2) ? "read " : "",
+				(prot & 0xfffff800),
+				(prot & 0xfffff800) + (((prot & 0x7fc) << 8) | 0x3ff));
+	}
+
+	buses_supported = CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH;
+
+	ret = sb600_probe_spi(dev);
+
+	/* Read ROM strap override register. */
+	OUTB(0x8f, 0xcd6);
+	reg = INB(0xcd7);
+	reg &= 0x0e;
+	msg_pdbg("ROM strap override is %sactive", (reg & 0x02) ? "" : "not ");
+	if (reg & 0x02) {
+		switch ((reg & 0x0c) >> 2) {
+		case 0x00:
+			msg_pdbg(": LPC");
+			break;
+		case 0x01:
+			msg_pdbg(": PCI");
+			break;
+		case 0x02:
+			msg_pdbg(": FWH");
+			break;
+		case 0x03:
+			msg_pdbg(": SPI");
+			break;
+		}
+	}
+	msg_pdbg("\n");
+
+	/* Force enable SPI ROM in SB600 PM register.
+	 * If we enable SPI ROM here, we have to disable it after we leave.
+	 * But how can we know which ROM we are going to handle? So we have
+	 * to trade off. We only access LPC ROM if we boot via LPC ROM. And
+	 * only SPI ROM if we boot via SPI ROM. If you want to access SPI on
+	 * boards with LPC straps, you have to use the code below.
+	 */
+	/*
+	OUTB(0x8f, 0xcd6);
+	OUTB(0x0e, 0xcd7);
+	*/
+
+	return ret;
+}
+
+
+int sb600_spi_lockdown(void) {
+	struct flashchip *flash;
+	struct pci_dev *dev;
+	int res;
+
+	dev = pci_dev_find(0x1002, 0x438d);
+	if (!dev) {
+		debug("Failed to find SB600 LPC bridge!\n");
+
+		return -1;
+	}
+
+	enable_flash_sb600(dev);
+
+	if (spi_controller == SPI_CONTROLLER_NONE) {
+		debug("Failed to enable SPI controller!\n");
+
+		return -1;
+	}
+
+	flash = probe_flash(flashchips, 0);
+	if (!flash) {
+		debug("Failed to find flash chip!\n");
+
+		return -1;
+	}
+
+	if (!flash->lock) {
+		debug("Flash chip cannot be locked!\n");
+
+		return -1;
+	}
+
+	if (flash->lock(flash)) {
+		debug("Failed to lock flash chip (already locked?)!\n");
+
+		/* signal this case differently, the chip may already be locked */
+		return 1;
+	}
+
+	return 0;
+}
diff --git a/x86/linux_load.c b/x86/linux_load.c
index 01e185e..a4eba5b 100644
--- a/x86/linux_load.c
+++ b/x86/linux_load.c
@@ -680,11 +680,19 @@ static void hardware_setup(void)
 #ifdef CONFIG_FLASHROM_LOCKDOWN
 	/* lockdown flashROM */
 	extern int flashrom_lockdown;
-	extern void intel_lockdown_flash();
+	extern int intel_lockdown_flash(void);
+	extern int amd_lockdown_flash(void);
 
 	if (flashrom_lockdown) {
-		printf("Locking FlashROM...\n");
-		intel_lockdown_flash();
+		printf("Locking FlashROM...");
+		if (intel_lockdown_flash() == 0) {
+			printf("done (Intel)\n");
+		} else if (amd_lockdown_flash() == 0) {
+			printf("done (AMD)\n");
+		} else {
+			printf("FAILED!\n");
+			delay(5);
+		}
 	} else {
 		printf("Leaving FlashROM unlocked...\n");
 	}



More information about the coreboot mailing list