From 70e145989bb9d5214a2c50b79b23c457d12f04a5 Mon Sep 17 00:00:00 2001 From: Rudolf Marek Date: Thu, 25 Jul 2013 22:58:56 +0000 Subject: sbxxx: Handle active IMCs in AMD chipsets Detect and temporarily disable the IMC while accessing the flash. Disable writes on default, but allow the user to enforce it. Corresponding to flashrom svn r1704. Signed-off-by: Rudolf Marek Signed-off-by: Stefan Tauner Acked-by: Stefan Tauner Acked-by: David Hendricks --- sb600spi.c | 75 +++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 20 deletions(-) (limited to 'sb600spi.c') diff --git a/sb600spi.c b/sb600spi.c index a5c00d8..e76c04a 100644 --- a/sb600spi.c +++ b/sb600spi.c @@ -23,6 +23,8 @@ #if defined(__i386__) || defined(__x86_64__) +#include +#include #include "flash.h" #include "programmer.h" #include "hwaccess.h" @@ -47,7 +49,7 @@ 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. */ + /* FIXME: This loop needs a timeout and a clearer message. */ while (mmio_readb(sb600_spibar + 0xD) & 0x7) msg_pspew("reset\n"); } @@ -59,8 +61,7 @@ static int compare_internal_fifo_pointer(uint8_t want) 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("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 " @@ -194,6 +195,39 @@ static int sb600_spi_send_command(struct flashctx *flash, unsigned int writecnt, return 0; } +static int sb600_handle_imc(struct pci_dev *dev, bool amd_imc_force) +{ + /* Handle IMC everywhere but sb600 which does not have one. */ + if (dev->device_id == 0x438d) + return 0; + + /* TODO: we should not only look at IntegratedImcPresent (LPC Dev 20, Func 3, 40h) but also at + * IMCEnable(Strap) and Override EcEnable(Strap) (sb8xx, sb9xx?, a50: Misc_Reg: 80h-87h; + * sb7xx, sp5100: PM_Reg: B0h-B1h) etc. */ + uint8_t reg = pci_read_byte(dev, 0x40); + if ((reg & (1 << 7)) == 0) { + msg_pdbg("IMC is not active.\n"); + return 0; + } + + if (!amd_imc_force) + programmer_may_write = 0; + msg_pinfo("Writes have been disabled for safety reasons because the IMC is active\n" + "and it could interfere with accessing flash memory. Flashrom will try\n" + "to disable it temporarily but even then this might not be safe:\n" + "when it is reenabled and after a reboot it expects to find working code\n" + "in the flash and it is unpredictable what happens if there is none.\n" + "\n" + "To be safe make sure that there is a working IMC firmware at the right\n" + "location in the image you intend to write and do not attempt to erase.\n" + "\n" + "You can enforce write support with the amd_imc_force programmer option.\n"); + if (amd_imc_force) + msg_pinfo("Continuing with write support because the user forced us to!\n"); + + return amd_imc_shutdown(dev); +} + static const struct spi_programmer spi_programmer_sb600 = { .type = SPI_CONTROLLER_SB600, .max_data_read = 8, @@ -210,10 +244,26 @@ int sb600_probe_spi(struct pci_dev *dev) struct pci_dev *smbus_dev; uint32_t tmp; uint8_t reg; + bool amd_imc_force = false; static const char *const speed_names[4] = { "66/reserved", "33", "22", "16.5" }; + char *arg = extract_programmer_param("amd_imc_force"); + if (arg && !strcmp(arg, "yes")) { + amd_imc_force = true; + msg_pspew("amd_imc_force enabled.\n"); + } else if (arg && !strlen(arg)) { + msg_perr("Missing argument for amd_imc_force.\n"); + free(arg); + return ERROR_FATAL; + } else if (arg) { + msg_perr("Unknown argument for amd_imc_force: \"%s\" (not \"yes\").\n", arg); + free(arg); + return ERROR_FATAL; + } + free(arg); + /* Read SPI_BaseAddr */ tmp = pci_read_long(dev, 0xa0); tmp &= 0xffffffe0; /* remove bits 4-0 (reserved) */ @@ -300,23 +350,8 @@ int sb600_probe_spi(struct pci_dev *dev) 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; - } + if (sb600_handle_imc(dev, amd_imc_force) != 0) + return ERROR_FATAL; /* Bring the FIFO to a clean state. */ reset_internal_fifo_pointer(); -- cgit v1.1