Support variable-size SPI chip for dummy programmer.

  flashrom -p dummy:emulate=VARIABLE_SIZE,image=image_file,size=4M

This is designed for firmware updater to preserve partitions easily. For example, the use case to keep VPDs when upgrading from H2O to H2C.

Change-Id: I84e1a793639cc33b769f6db41be0a0f0b1eacc78

R=dhendrix@chromium.org,hungte@chromiumg.org,reinauer@chromium.org
BUG=Not a bug
TEST=Tested on Z600 and target.
# a H2C file is ready to write.
% flashrom -r H2O  # read existing content from BIOS, including VPDs.
% flashrom -p dummy:emulate=VARIABLE_SIZE,image=H2C,size=4M \
      -i RO_VPD -i RW_VPD -w H2O  # overwrite VPDs from H2O to H2C.
% flashrom -w H2C  # write it down, with VPDs from H2O.

Review URL: http://codereview.chromium.org/6791015
diff --git a/chipdrivers.h b/chipdrivers.h
index fb251fe..7e9fe8b 100644
--- a/chipdrivers.h
+++ b/chipdrivers.h
@@ -137,4 +137,7 @@
 int erase_chip_stm50flw0x0x(struct flashchip *flash, unsigned int addr, unsigned int blocklen);
 int unlock_stm50flw0x0x(struct flashchip *flash);
 
+/* dummyflasher.c */
+int probe_variable_size(struct flashchip *flash);
+
 #endif /* !__CHIPDRIVERS_H__ */
diff --git a/dummyflasher.c b/dummyflasher.c
index 473e45e..91fbc88 100644
--- a/dummyflasher.c
+++ b/dummyflasher.c
@@ -22,6 +22,7 @@
 #include "flash.h"
 #include "chipdrivers.h"
 #include "programmer.h"
+#include "flashchips.h"
 
 /* Remove the #define below if you don't want SPI flash chip emulation. */
 #define EMULATE_SPI_CHIP 1
@@ -34,6 +35,13 @@
 #if EMULATE_CHIP
 #include <sys/types.h>
 #include <sys/stat.h>
+
+#if EMULATE_SPI_CHIP
+/* The name of variable-size virtual chip. A 4MB flash example:
+ *   flashrom -p dummy:emulate=VARIABLE_SIZE,size=4194304
+ */
+#define VARIABLE_SIZE_CHIP_NAME "VARIABLE_SIZE"
+#endif
 #endif
 
 #if EMULATE_CHIP
@@ -43,6 +51,7 @@
 	EMULATE_ST_M25P10_RES,
 	EMULATE_SST_SST25VF040_REMS,
 	EMULATE_SST_SST25VF032B,
+	EMULATE_VARIABLE_SIZE,
 };
 static enum emu_chip emu_chip = EMULATE_NONE;
 static char *emu_persistent_image = NULL;
@@ -66,6 +75,9 @@
 	char *tmp = NULL;
 #if EMULATE_CHIP
 	struct stat image_stat;
+#if EMULATE_SPI_CHIP
+	int size = -1;  /* size for generic chip */
+#endif
 #endif
 
 	msg_pspew("%s\n", __func__);
@@ -110,6 +122,29 @@
 	}
 
 #if EMULATE_CHIP
+#if EMULATE_SPI_CHIP
+	tmp = extract_programmer_param("size");
+	if (tmp) {
+		int multiplier = 1;
+		if (strlen(tmp)) {
+			int remove_last_char = 1;
+			switch (tmp[strlen(tmp) - 1]) {
+			case 'k': case 'K':
+				multiplier = 1024;
+				break;
+			case 'm': case 'M':
+				multiplier = 1024 * 1024;
+				break;
+			default:
+				remove_last_char = 0;
+				break;
+			}
+			if (remove_last_char) tmp[strlen(tmp) - 1] = '\0';
+		}
+		size = atoi(tmp) * multiplier;
+	}
+#endif
+
 	tmp = extract_programmer_param("emulate");
 	if (!tmp) {
 		msg_pdbg("Not emulating any flash chip.\n");
@@ -156,6 +191,26 @@
 		msg_pdbg("Emulating SST SST25VF032B SPI flash chip (RDID, AAI "
 			 "write)\n");
 	}
+	if (!strncmp(tmp, VARIABLE_SIZE_CHIP_NAME,
+	                  strlen(VARIABLE_SIZE_CHIP_NAME))) {
+		if (size == -1) {
+			msg_perr("%s: the size parameter is not given.\n",
+			         __func__);
+			free(tmp);
+			return 1;
+		}
+		emu_chip = EMULATE_VARIABLE_SIZE;
+		emu_chip_size = size;
+		emu_max_byteprogram_size = 256;
+		emu_max_aai_size = 0;
+		emu_jedec_se_size = 4 * 1024;
+		emu_jedec_be_52_size = 32 * 1024;
+		emu_jedec_be_d8_size = 64 * 1024;
+		emu_jedec_ce_60_size = emu_chip_size;
+		emu_jedec_ce_c7_size = emu_chip_size;
+		msg_pdbg("Emulating generic SPI flash chip (size=%d bytes)\n",
+		         emu_chip_size);
+	}
 #endif
 	if (emu_chip == EMULATE_NONE) {
 		msg_perr("Invalid chip specified for emulation: %s\n", tmp);
@@ -305,15 +360,22 @@
 			readarr[1] = 0x44;
 		break;
 	case JEDEC_RDID:
-		if (emu_chip != EMULATE_SST_SST25VF032B)
-			break;
-		/* Respond with SST_SST25VF032B. */
-		if (readcnt > 0)
-			readarr[0] = 0xbf;
-		if (readcnt > 1)
-			readarr[1] = 0x25;
-		if (readcnt > 2)
-			readarr[2] = 0x4a;
+		if (emu_chip == EMULATE_SST_SST25VF032B) {
+			/* Respond with SST_SST25VF032B. */
+			if (readcnt > 0)
+				readarr[0] = 0xbf;
+			if (readcnt > 1)
+				readarr[1] = 0x25;
+			if (readcnt > 2)
+				readarr[2] = 0x4a;
+		} else if (emu_chip == EMULATE_VARIABLE_SIZE) {
+			const uint16_t man_id = VARIABLE_SIZE_MANUF_ID;
+			const uint16_t dev_id = VARIABLE_SIZE_DEVICE_ID;
+			if (readcnt > 0) readarr[0] = man_id >> 8;
+			if (readcnt > 1) readarr[1] = man_id & 0xff;
+			if (readcnt > 2) readarr[2] = dev_id >> 8;
+			if (readcnt > 3) readarr[3] = dev_id & 0xff;
+		}
 		break;
 	case JEDEC_RDSR:
 		memset(readarr, 0, readcnt);
@@ -495,6 +557,7 @@
 	case EMULATE_ST_M25P10_RES:
 	case EMULATE_SST_SST25VF040_REMS:
 	case EMULATE_SST_SST25VF032B:
+	case EMULATE_VARIABLE_SIZE:
 		if (emulate_spi_chip_response(writecnt, readcnt, writearr,
 					      readarr)) {
 			msg_perr("Invalid command sent to flash chip!\n");
@@ -524,3 +587,47 @@
 	return spi_write_chunked(flash, buf, start, len,
 				 spi_write_256_chunksize);
 }
+
+#if EMULATE_CHIP && EMULATE_SPI_CHIP
+int probe_variable_size(struct flashchip *flash)
+{
+	int i;
+
+	/* Skip the probing if we don't emulate this chip. */
+	if (emu_chip != EMULATE_VARIABLE_SIZE)
+		return 0;
+
+	/*
+	 * This will break if one day flashchip becomes read-only.
+	 * Once that happens, we need to have special hacks in functions:
+	 *
+	 *     erase_and_write_flash() in flashrom.c
+	 *     read_flash_to_file()
+	 *     handle_romentries()
+	 *     ...
+	 *
+	 * Search "total_size * 1024" in code.
+	 */
+	if (emu_chip_size % 1024)
+		msg_perr("%s: emu_chip_size is not multipler of 1024.\n",
+		         __func__);
+	flash->total_size = emu_chip_size / 1024;
+	msg_cdbg("%s: set flash->total_size to %dK bytes.\n", __func__,
+	         flash->total_size);
+
+	/* Update eraser count */
+	for (i = 0; i < NUM_ERASEFUNCTIONS; i++) {
+		struct block_eraser *eraser = &flash->block_erasers[i];
+		if (eraser->block_erase == NULL)
+			break;
+
+		eraser->eraseblocks[0].count = emu_chip_size /
+		    eraser->eraseblocks[0].size;
+		msg_cdbg("%s: eraser.size=%d, .count=%d\n",
+		         __func__, eraser->eraseblocks[0].size,
+		         eraser->eraseblocks[0].count);
+	}
+
+	return 1;
+}
+#endif
diff --git a/flashchips.c b/flashchips.c
index dca01a2..4b921b5 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -8075,6 +8075,30 @@
 
 	{
 		.vendor		= "Generic",
+		.name		= "Variable Size SPI chip",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= VARIABLE_SIZE_MANUF_ID,
+		.model_id	= VARIABLE_SIZE_DEVICE_ID,
+		.total_size	= 64,  /* This size is set temporarily */
+		.page_size	= 256,
+		.tested		= TEST_OK_PREW,
+		.probe		= probe_variable_size,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {4 * 1024, 16} },
+				.block_erase = spi_block_erase_20,
+			}, {
+				.eraseblocks = { {64 * 1024, 1} },
+				.block_erase = spi_block_erase_d8,
+			}
+		},
+		.write		= spi_chip_write_256,
+		.read		= spi_chip_read,
+	},
+
+	{
+		.vendor		= "Generic",
 		.name		= "unknown SPI chip (RDID)",
 		.bustype	= CHIP_BUSTYPE_SPI,
 		.manufacture_id	= GENERIC_MANUF_ID,
diff --git a/flashchips.h b/flashchips.h
index 95481f4..e211a46 100644
--- a/flashchips.h
+++ b/flashchips.h
@@ -37,6 +37,9 @@
 #define GENERIC_MANUF_ID	0xffff	/* Check if there is a vendor ID */
 #define GENERIC_DEVICE_ID	0xffff	/* Only match the vendor ID */
 
+#define VARIABLE_SIZE_MANUF_ID 0x3eaf
+#define VARIABLE_SIZE_DEVICE_ID 0x10af
+
 #define ALLIANCE_ID		0x52	/* Alliance Semiconductor */
 #define ALLIANCE_AS29F002B	0x34
 #define ALLIANCE_AS29F002T	0xB0