[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