1
0
mirror of https://review.coreboot.org/flashrom.git synced 2025-04-26 06:32:34 +02:00

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 <r.marek@assembler.cz>
Signed-off-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
Acked-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
Acked-by: David Hendricks <dhendrix@google.com>
This commit is contained in:
Rudolf Marek 2013-07-25 22:58:56 +00:00 committed by Stefan Tauner
parent 1aa80b0648
commit 70e145989b
5 changed files with 235 additions and 21 deletions

View File

@ -433,7 +433,7 @@ ifeq ($(CONFIG_INTERNAL), yes)
FEATURE_CFLAGS += -D'CONFIG_INTERNAL=1' FEATURE_CFLAGS += -D'CONFIG_INTERNAL=1'
PROGRAMMER_OBJS += processor_enable.o chipset_enable.o board_enable.o cbtable.o dmi.o internal.o PROGRAMMER_OBJS += processor_enable.o chipset_enable.o board_enable.o cbtable.o dmi.o internal.o
ifeq ($(ARCH), x86) ifeq ($(ARCH), x86)
PROGRAMMER_OBJS += it87spi.o it85spi.o sb600spi.o wbsio_spi.o mcp6x_spi.o PROGRAMMER_OBJS += it87spi.o it85spi.o sb600spi.o amd_imc.o wbsio_spi.o mcp6x_spi.o
PROGRAMMER_OBJS += ichspi.o ich_descriptors.o PROGRAMMER_OBJS += ichspi.o ich_descriptors.o
else else
endif endif

159
amd_imc.c Normal file
View File

@ -0,0 +1,159 @@
/*
* This file is part of the flashrom project.
*
* Copyright (C) 2013 Rudolf Marek <r.marek@assembler.cz>
* Copyright (C) 2013 Stefan Tauner
*
* 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
*/
#if defined(__i386__) || defined(__x86_64__)
#include "flash.h"
#include "programmer.h"
#include "hwaccess.h"
#include "spi.h"
/* same as serverengines */
static void enter_conf_mode_ec(uint16_t port)
{
OUTB(0x5a, port);
}
static void exit_conf_mode_ec(uint16_t port)
{
OUTB(0xa5, port);
}
static uint16_t get_sio_port(struct pci_dev *dev)
{
uint16_t ec_port;
if (!dev) {
return 0;
}
ec_port = pci_read_word(dev, 0xa4);
/* EcPortActive? */
if (!(ec_port & 0x1))
return 0;
ec_port &= ~0x1;
return ec_port;
}
/* Wait for up to 10 ms for a response. */
static int mbox_wait_ack(uint16_t mbox_port)
{
int i = 10;
while (sio_read(mbox_port, 0x82) != 0xfa) {
if (--i == 0) {
msg_pwarn("IMC MBOX: Timeout!\n");
return 1;
}
programmer_delay(1000);
}
return 0;
}
static uint16_t mbox_get_port(uint16_t sio_port)
{
uint16_t mbox_port;
enter_conf_mode_ec(sio_port);
/* Go to LDN 9, mailbox */
sio_write(sio_port, 7, 9);
/* MBOX inactive? */
if ((sio_read(sio_port, 0x30) & 1) == 0) {
exit_conf_mode_ec(sio_port);
return 0;
}
mbox_port = sio_read(sio_port, 0x60) << 8;
mbox_port |= sio_read(sio_port, 0x61);
exit_conf_mode_ec(sio_port);
return mbox_port;
}
/* Returns negative values when IMC is inactive, positive values on errors */
static int imc_send_cmd(struct pci_dev *dev, uint8_t cmd)
{
uint16_t sio_port;
uint16_t mbox_port;
/* IntegratedEcPresent? */
if (!(pci_read_byte(dev, 0x40) & (1 << 7)))
return -1;
sio_port = get_sio_port(dev);
if (!sio_port)
return -1;
msg_pdbg2("IMC SIO is at 0x%x.\n", sio_port);
mbox_port = mbox_get_port(sio_port);
if (!mbox_port)
return -1;
msg_pdbg2("IMC MBOX is at 0x%x.\n", mbox_port);
sio_write(mbox_port, 0x82, 0x0);
sio_write(mbox_port, 0x83, cmd);
sio_write(mbox_port, 0x84, 0x0);
/* trigger transfer 0x96 with subcommand cmd */
sio_write(mbox_port, 0x80, 0x96);
return mbox_wait_ack(mbox_port);
}
static int imc_resume(void *data)
{
struct pci_dev *dev = data;
int ret = imc_send_cmd(dev, 0xb5);
if (ret != 0)
msg_pinfo("Resuming IMC failed)\n");
else
msg_pdbg2("IMC resumed.\n");
return ret;
}
int amd_imc_shutdown(struct pci_dev *dev)
{
/* Try to put IMC to sleep */
int ret = imc_send_cmd(dev, 0xb4);
/* No IMC activity detectable, assume we are fine */
if (ret < 0) {
msg_pdbg2("No IMC found.\n");
return 0;
}
if (ret != 0) {
msg_perr("Shutting down IMC failed.\n");
return ret;
}
msg_pdbg2("Shutting down IMC successful.\n");
if (register_shutdown(imc_resume, dev))
return 1;
return ret;
}
#endif

View File

@ -329,6 +329,23 @@ flashrom doesn't detect an active IT87 LPC<->SPI bridge, please send a bug
report so we can diagnose the problem. report so we can diagnose the problem.
.sp .sp
.TP .TP
.B AMD chipsets
.sp
Beginning with the SB700 chipset there is an integrated microcontroller (IMC) based on the 8051 embedded in
every AMD southbridge. Its firmware resides in the same flash chip as the host's which makes writing to the
flash risky if the IMC is active. Flashrom tries to temporarily disable the IMC but even then changing the
contents of the flash can have unwanted effects: when the IMC continues (at the latest after a reboot) it will
continue executing code from the flash. If the code was removed or changed in an unfortunate way it is
unpredictable what the IMC will do. Therefore, if flashrom detects an active IMC it will disable write support
unless the user forces it with the
.sp
.B " flashrom \-p internal:amd_imc_force=yes"
.sp
syntax. The user is responsible for supplying a suitable image or leaving out the IMC region with the help of
a layout file. This limitation might be removed in the future when we understand the details better and have
received enough feedback from users. Please report the outcome if you had to use this option to write a chip.
.sp
.TP
.B Intel chipsets .B Intel chipsets
.sp .sp
If you have an Intel chipset with an ICH8 or later southbridge with SPI flash If you have an Intel chipset with an ICH8 or later southbridge with SPI flash

View File

@ -572,6 +572,9 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb,
enum ich_chipset ich_generation); enum ich_chipset ich_generation);
int via_init_spi(struct pci_dev *dev, uint32_t mmio_base); int via_init_spi(struct pci_dev *dev, uint32_t mmio_base);
/* imc.c */
int amd_imc_shutdown(struct pci_dev *dev);
/* it85spi.c */ /* it85spi.c */
int it85xx_spi_init(struct superio s); int it85xx_spi_init(struct superio s);

View File

@ -23,6 +23,8 @@
#if defined(__i386__) || defined(__x86_64__) #if defined(__i386__) || defined(__x86_64__)
#include <string.h>
#include <stdlib.h>
#include "flash.h" #include "flash.h"
#include "programmer.h" #include "programmer.h"
#include "hwaccess.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); 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) while (mmio_readb(sb600_spibar + 0xD) & 0x7)
msg_pspew("reset\n"); msg_pspew("reset\n");
} }
@ -59,8 +61,7 @@ static int compare_internal_fifo_pointer(uint8_t want)
tmp = mmio_readb(sb600_spibar + 0xd) & 0x07; tmp = mmio_readb(sb600_spibar + 0xd) & 0x07;
want &= 0x7; want &= 0x7;
if (want != tmp) { if (want != tmp) {
msg_perr("SB600 FIFO pointer corruption! Pointer is %d, wanted " msg_perr("FIFO pointer corruption! Pointer is %d, wanted %d\n", tmp, want);
"%d\n", tmp, want);
msg_perr("Something else is accessing the flash chip and " msg_perr("Something else is accessing the flash chip and "
"causes random corruption.\nPlease stop all " "causes random corruption.\nPlease stop all "
"applications and drivers and IPMI which access the " "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; 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 = { static const struct spi_programmer spi_programmer_sb600 = {
.type = SPI_CONTROLLER_SB600, .type = SPI_CONTROLLER_SB600,
.max_data_read = 8, .max_data_read = 8,
@ -210,10 +244,26 @@ int sb600_probe_spi(struct pci_dev *dev)
struct pci_dev *smbus_dev; struct pci_dev *smbus_dev;
uint32_t tmp; uint32_t tmp;
uint8_t reg; uint8_t reg;
bool amd_imc_force = false;
static const char *const speed_names[4] = { static const char *const speed_names[4] = {
"66/reserved", "33", "22", "16.5" "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 */ /* Read SPI_BaseAddr */
tmp = pci_read_long(dev, 0xa0); tmp = pci_read_long(dev, 0xa0);
tmp &= 0xffffffe0; /* remove bits 4-0 (reserved) */ tmp &= 0xffffffe0; /* remove bits 4-0 (reserved) */
@ -300,23 +350,8 @@ int sb600_probe_spi(struct pci_dev *dev)
return 0; return 0;
} }
reg = pci_read_byte(dev, 0x40); if (sb600_handle_imc(dev, amd_imc_force) != 0)
msg_pdbg("SB700 IMC is %sactive.\n", (reg & (1 << 7)) ? "" : "not "); return ERROR_FATAL;
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. */ /* Bring the FIFO to a clean state. */
reset_internal_fifo_pointer(); reset_internal_fifo_pointer();