[flashrom] [commit] r1704 - trunk

repository service svn at flashrom.org
Fri Jul 26 00:58:56 CEST 2013


Author: stefanct
Date: Fri Jul 26 00:58:56 2013
New Revision: 1704
URL: http://flashrom.org/trac/flashrom/changeset/1704

Log:
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.

Signed-off-by: Rudolf Marek <r.marek at assembler.cz>
Signed-off-by: Stefan Tauner <stefan.tauner at student.tuwien.ac.at>
Acked-by: Stefan Tauner <stefan.tauner at student.tuwien.ac.at>
Acked-by: David Hendricks <dhendrix at google.com>

Added:
   trunk/amd_imc.c   (contents, props changed)
Modified:
   trunk/Makefile
   trunk/flashrom.8
   trunk/programmer.h
   trunk/sb600spi.c

Modified: trunk/Makefile
==============================================================================
--- trunk/Makefile	Fri Jul 26 00:58:51 2013	(r1703)
+++ trunk/Makefile	Fri Jul 26 00:58:56 2013	(r1704)
@@ -433,7 +433,7 @@
 FEATURE_CFLAGS += -D'CONFIG_INTERNAL=1'
 PROGRAMMER_OBJS += processor_enable.o chipset_enable.o board_enable.o cbtable.o dmi.o internal.o
 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
 else
 endif

Added: trunk/amd_imc.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/amd_imc.c	Fri Jul 26 00:58:56 2013	(r1704)
@@ -0,0 +1,159 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2013 Rudolf Marek <r.marek at 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

Modified: trunk/flashrom.8
==============================================================================
--- trunk/flashrom.8	Fri Jul 26 00:58:51 2013	(r1703)
+++ trunk/flashrom.8	Fri Jul 26 00:58:56 2013	(r1704)
@@ -329,6 +329,23 @@
 report so we can diagnose the problem.
 .sp
 .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
 .sp
 If you have an Intel chipset with an ICH8 or later southbridge with SPI flash

Modified: trunk/programmer.h
==============================================================================
--- trunk/programmer.h	Fri Jul 26 00:58:51 2013	(r1703)
+++ trunk/programmer.h	Fri Jul 26 00:58:56 2013	(r1704)
@@ -572,6 +572,9 @@
 		 enum ich_chipset ich_generation);
 int via_init_spi(struct pci_dev *dev, uint32_t mmio_base);
 
+/* imc.c */
+int amd_imc_shutdown(struct pci_dev *dev);
+
 /* it85spi.c */
 int it85xx_spi_init(struct superio s);
 

Modified: trunk/sb600spi.c
==============================================================================
--- trunk/sb600spi.c	Fri Jul 26 00:58:51 2013	(r1703)
+++ trunk/sb600spi.c	Fri Jul 26 00:58:56 2013	(r1704)
@@ -23,6 +23,8 @@
 
 #if defined(__i386__) || defined(__x86_64__)
 
+#include <string.h>
+#include <stdlib.h>
 #include "flash.h"
 #include "programmer.h"
 #include "hwaccess.h"
@@ -47,7 +49,7 @@
 {
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 		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();




More information about the flashrom mailing list