[flashrom] [PATCH] SPI flash chip emulator

Carl-Daniel Hailfinger c-d.hailfinger.devel.2006 at gmx.net
Sun Oct 31 02:06:58 CET 2010


New version. Fully emulate all flash chip functionality.

Add SPI flash emulation capability to the dummy programmer.

You have to choose between
- no emulation
- ST M25P10.RES SPI flash chip (RES, page write)
- SST SST25VF040.REMS SPI flash chip (REMS, byte write)
- SST SST25VF032B SPI flash chip (RDID, AAI write)
Example usage: flashrom -p dummy:emulate=SST25VF032B

Flash image persistence is available as well.
Example usage: flashrom -p dummy:image=dummy_simulator.rom

Allow setting the max chunksize for page write with the dummy
programmer.
Example usage: flashrom -p dummy:spi_write_256_chunksize=5

Flash emulation is compiled in by default. 

This code helped me find and fix various bugs in the SPI write code
as well as in the testsuite.

Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006 at gmx.net>

Index: flashrom-emulate_spi_flashchip/flash.h
===================================================================
--- flashrom-emulate_spi_flashchip/flash.h	(Revision 1218)
+++ flashrom-emulate_spi_flashchip/flash.h	(Arbeitskopie)
@@ -207,6 +207,8 @@
 void list_programmers_linebreak(int startcol, int cols, int paren);
 int selfcheck(void);
 int doit(struct flashchip *flash, int force, char *filename, int read_it, int write_it, int erase_it, int verify_it);
+int read_buf_from_file(unsigned char *buf, unsigned long size, char *filename);
+int write_buf_to_file(unsigned char *buf, unsigned long size, char *filename);
 
 #define OK 0
 #define NT 1    /* Not tested */
Index: flashrom-emulate_spi_flashchip/dummyflasher.c
===================================================================
--- flashrom-emulate_spi_flashchip/dummyflasher.c	(Revision 1218)
+++ flashrom-emulate_spi_flashchip/dummyflasher.c	(Arbeitskopie)
@@ -1,12 +1,11 @@
 /*
  * This file is part of the flashrom project.
  *
- * Copyright (C) 2009 Carl-Daniel Hailfinger
+ * Copyright (C) 2009,2010 Carl-Daniel Hailfinger
  *
  * 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.
+ * 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
@@ -25,6 +24,42 @@
 #include "chipdrivers.h"
 #include "programmer.h"
 
+#define EMULATE_SPI_CHIP 1
+
+#if EMULATE_SPI_CHIP
+#define EMULATE_CHIP 1
+#include "spi.h"
+#endif
+
+#if EMULATE_CHIP
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+#if EMULATE_CHIP
+static uint8_t *flashchip_contents = NULL;
+enum emu_chip {
+	EMULATE_NONE,
+	EMULATE_ST_M25P10_RES,
+	EMULATE_SST_SST25VF040_REMS,
+	EMULATE_SST_SST25VF032B,
+};
+static enum emu_chip emu_chip = EMULATE_NONE;
+static char *emu_persistent_image = NULL;
+static int emu_chip_size = 0;
+#if EMULATE_SPI_CHIP
+static int emu_max_byteprogram_size = 0;
+static int emu_max_aai_size = 0;
+static int emu_jedec_se_size = 0;
+static int emu_jedec_be_52_size = 0;
+static int emu_jedec_be_d8_size = 0;
+static int emu_jedec_ce_60_size = 0;
+static int emu_jedec_ce_c7_size = 0;
+#endif
+#endif
+
+static int spi_write_256_chunksize = 256;
+
 static void tolower_string(char *str)
 {
 	for (; *str != '\0'; str++)
@@ -34,6 +69,10 @@
 int dummy_init(void)
 {
 	char *bustext = NULL;
+	char *tmp = NULL;
+#if EMULATE_CHIP
+	struct stat image_stat;
+#endif
 
 	msg_pspew("%s\n", __func__);
 
@@ -65,12 +104,113 @@
 	if (buses_supported == CHIP_BUSTYPE_NONE)
 		msg_pdbg("Support for all flash bus types disabled.\n");
 	free(bustext);
+
+	tmp = extract_programmer_param("spi_write_256_chunksize");
+	if (tmp) {
+		spi_write_256_chunksize = atoi(tmp);
+		free(tmp);
+		if (spi_write_256_chunksize < 1) {
+			msg_perr("invalid spi_write_256_chunksize\n");
+			return 1;
+		}
+	}
+
+#if EMULATE_CHIP
+	tmp = extract_programmer_param("emulate");
+	if (!tmp) {
+		msg_pdbg("Not emulating any flash chip.\n");
+		/* Nothing else to do. */
+		return 0;
+	}
+#if EMULATE_SPI_CHIP
+	if (!strcmp(tmp, "M25P10.RES")) {
+		emu_chip = EMULATE_ST_M25P10_RES;
+		emu_chip_size = 128 * 1024;
+		emu_max_byteprogram_size = 128;
+		emu_max_aai_size = 0;
+		emu_jedec_se_size = 0;
+		emu_jedec_be_52_size = 0;
+		emu_jedec_be_d8_size = 32 * 1024;
+		emu_jedec_ce_60_size = 0;
+		emu_jedec_ce_c7_size = emu_chip_size;
+		msg_pdbg("Emulating ST M25P10.RES SPI flash chip (RES, page "
+			 "write)\n");
+	}
+	if (!strcmp(tmp, "SST25VF040.REMS")) {
+		emu_chip = EMULATE_SST_SST25VF040_REMS;
+		emu_chip_size = 512 * 1024;
+		emu_max_byteprogram_size = 1;
+		emu_max_aai_size = 0;
+		emu_jedec_se_size = 4 * 1024;
+		emu_jedec_be_52_size = 32 * 1024;
+		emu_jedec_be_d8_size = 0;
+		emu_jedec_ce_60_size = emu_chip_size;
+		emu_jedec_ce_c7_size = 0;
+		msg_pdbg("Emulating SST SST25VF040.REMS SPI flash chip (REMS, "
+			 "byte write)\n");
+	}
+	if (!strcmp(tmp, "SST25VF032B")) {
+		emu_chip = EMULATE_SST_SST25VF032B;
+		emu_chip_size = 4 * 1024 * 1024;
+		emu_max_byteprogram_size = 1;
+		emu_max_aai_size = 2;
+		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 SST SST25VF032B SPI flash chip (RDID, AAI "
+			 "write)\n");
+	}
+#endif
+	if (emu_chip == EMULATE_NONE) {
+		msg_perr("Invalid chip specified for emulation: %s\n", tmp);
+		free(tmp);
+		return 1;
+	}
+	free(tmp);
+	flashchip_contents = malloc(emu_chip_size);
+	if (!flashchip_contents) {
+		msg_perr("Out of memory!\n");
+		return 1;
+	}
+	msg_pdbg("Filling fake flash chip with 0xff, size %i\n", emu_chip_size);
+	memset(flashchip_contents, 0xff, emu_chip_size);
+
+	emu_persistent_image = extract_programmer_param("image");
+	if (!emu_persistent_image) {
+		/* Nothing else to do. */
+		return 0;
+	}
+	if (!stat(emu_persistent_image, &image_stat)) {
+		msg_pdbg("Found persistent image %s, size %li ",
+			 emu_persistent_image, (long)image_stat.st_size);
+		if (image_stat.st_size == emu_chip_size) {
+			msg_pdbg("matches.\n");
+			msg_pdbg("Reading %s\n", emu_persistent_image);
+			read_buf_from_file(flashchip_contents, emu_chip_size,
+					   emu_persistent_image);
+		} else {
+			msg_pdbg("doesn't match.\n");
+		}
+	}
+#endif
 	return 0;
 }
 
 int dummy_shutdown(void)
 {
 	msg_pspew("%s\n", __func__);
+#if EMULATE_CHIP
+	if (emu_chip != EMULATE_NONE) {
+		if (emu_persistent_image) {
+			msg_pdbg("Writing %s\n", emu_persistent_image);
+			write_buf_to_file(flashchip_contents, emu_chip_size,
+					  emu_persistent_image);
+		}
+		free(flashchip_contents);
+	}
+#endif
 	return 0;
 }
 
@@ -140,6 +280,256 @@
 	return;
 }
 
+#if EMULATE_SPI_CHIP
+static int emulate_spi_chip_response(unsigned int writecnt, unsigned int readcnt,
+		      const unsigned char *writearr, unsigned char *readarr)
+{
+	int offs;
+	static int aai_offs;
+	static int aai_active = 0;
+
+	if (writecnt == 0) {
+		msg_cerr("No command sent to the chip!\n");
+		return 1;
+	}
+	switch (writearr[0]) {
+	case JEDEC_RES:
+		if (emu_chip != EMULATE_ST_M25P10_RES)
+			break;
+		/* Respond with ST_M25P10_RES. */
+		if (readcnt > 0)
+			readarr[0] = 0x10;
+		break;
+	case JEDEC_REMS:
+		if (emu_chip != EMULATE_SST_SST25VF040_REMS)
+			break;
+		/* Respond with SST_SST25VF040_REMS. */
+		if (readcnt > 0)
+			readarr[0] = 0xbf;
+		if (readcnt > 1)
+			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;
+		break;
+	case JEDEC_RDSR:
+		memset(readarr, 0, readcnt);
+		if (aai_active)
+			memset(readarr, 1 << 6, readcnt);
+		break;
+	case JEDEC_READ:
+		offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+		/* Truncate to emu_chip_size. */
+		offs %= emu_chip_size;
+		if (readcnt > 0)
+			memcpy(readarr, flashchip_contents + offs, readcnt);
+		break;
+	case JEDEC_BYTE_PROGRAM:
+		offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+		/* Truncate to emu_chip_size. */
+		offs %= emu_chip_size;
+		if (writecnt < 5) {
+			msg_cerr("BYTE PROGRAM size too short!\n");
+			return 1;
+		}
+		if (writecnt - 4 > emu_max_byteprogram_size) {
+			msg_cerr("Max BYTE PROGRAM size exceeded!\n");
+			return 1;
+		}
+		memcpy(flashchip_contents + offs, writearr + 4, writecnt - 4);
+		break;
+	case JEDEC_AAI_WORD_PROGRAM:
+		switch (emu_chip) {
+		case EMULATE_SST_SST25VF032B:
+			break;
+		default:
+			return 0;
+		}
+		if (!aai_active) {
+			if (writecnt < JEDEC_AAI_WORD_PROGRAM_OUTSIZE) {
+				msg_cerr("Initial AAI WORD PROGRAM size too "
+					 "short!\n");
+				return 1;
+			}
+			if (writecnt > JEDEC_AAI_WORD_PROGRAM_OUTSIZE) {
+				msg_cerr("Initial AAI WORD PROGRAM size too "
+					 "long!\n");
+				return 1;
+			}
+			aai_active = 1;
+			aai_offs = writearr[1] << 16 | writearr[2] << 8 |
+				   writearr[3];
+			/* Truncate to emu_chip_size. */
+			aai_offs %= emu_chip_size;
+			memcpy(flashchip_contents + aai_offs, writearr + 4, 2);
+			aai_offs += 2;
+		} else {
+			if (writecnt < JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE) {
+				msg_cerr("Continuation AAI WORD PROGRAM size "
+					 "too short!\n");
+				return 1;
+			}
+			if (writecnt > JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE) {
+				msg_cerr("Continuation AAI WORD PROGRAM size "
+					 "too long!\n");
+				return 1;
+			}
+			memcpy(flashchip_contents + aai_offs, writearr + 1, 2);
+			aai_offs += 2;
+		}
+		break;
+	case JEDEC_WRDI:
+		switch (emu_chip) {
+		case EMULATE_SST_SST25VF032B:
+			break;
+		default:
+			return 0;
+		}
+		aai_active = 0;
+		break;
+	case JEDEC_SE:
+		/* SECTOR ERASE 0x20 is supported by SST_SST25VF040_REMS and
+		 * SST_SST25VF032B.
+		 */
+		switch (emu_chip) {
+		case EMULATE_SST_SST25VF040_REMS:
+		case EMULATE_SST_SST25VF032B:
+			break;
+		default:
+			return 0;
+		}
+		if (writecnt != JEDEC_SE_OUTSIZE) {
+			msg_cerr("SECTOR ERASE 0x20 outsize invalid!\n");
+			return 1;
+		}
+		if (readcnt != JEDEC_SE_INSIZE) {
+			msg_cerr("SECTOR ERASE 0x20 insize invalid!\n");
+			return 1;
+		}
+		offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+		if (offs & (emu_jedec_se_size - 1))
+			msg_cdbg("Unaligned SECTOR ERASE 0x20\n");
+		offs &= ~(emu_jedec_se_size - 1);
+		memset(flashchip_contents + offs, 0xff, emu_jedec_se_size);
+		break;
+	case JEDEC_BE_52:
+		/* BLOCK ERASE 0x52 is supported by SST_SST25VF040_REMS and
+		 * SST_SST25VF032B.
+		 */
+		switch (emu_chip) {
+		case EMULATE_SST_SST25VF040_REMS:
+		case EMULATE_SST_SST25VF032B:
+			break;
+		default:
+			return 0;
+		}
+		if (writecnt != JEDEC_BE_52_OUTSIZE) {
+			msg_cerr("BLOCK ERASE 0x52 outsize invalid!\n");
+			return 1;
+		}
+		if (readcnt != JEDEC_BE_52_INSIZE) {
+			msg_cerr("BLOCK ERASE 0x52 insize invalid!\n");
+			return 1;
+		}
+		offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+		if (offs & (emu_jedec_be_52_size - 1))
+			msg_cdbg("Unaligned BLOCK ERASE 0x52\n");
+		offs &= ~(emu_jedec_be_52_size - 1);
+		memset(flashchip_contents + offs, 0xff, emu_jedec_be_52_size);
+		break;
+	case JEDEC_BE_D8:
+		/* BLOCK ERASE 0xd8 is supported by ST_M25P10_RES and
+		 * SST_SST25VF032B.
+		 */
+		switch (emu_chip) {
+		case EMULATE_ST_M25P10_RES:
+		case EMULATE_SST_SST25VF032B:
+			break;
+		default:
+			return 0;
+		}
+		if (writecnt != JEDEC_BE_D8_OUTSIZE) {
+			msg_cerr("BLOCK ERASE 0xd8 outsize invalid!\n");
+			return 1;
+		}
+		if (readcnt != JEDEC_BE_D8_INSIZE) {
+			msg_cerr("BLOCK ERASE 0xd8 insize invalid!\n");
+			return 1;
+		}
+		offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+		if (offs & (emu_jedec_be_d8_size - 1))
+			msg_cdbg("Unaligned BLOCK ERASE 0xd8\n");
+		offs &= ~(emu_jedec_be_d8_size - 1);
+		memset(flashchip_contents + offs, 0xff, emu_jedec_be_d8_size);
+		break;
+	case JEDEC_CE_60:
+		/* CHIP ERASE 0x60 is supported by SST_SST25VF040_REMS and
+		 * SST_SST25VF032B.
+		 */
+		switch (emu_chip) {
+		case EMULATE_SST_SST25VF040_REMS:
+		case EMULATE_SST_SST25VF032B:
+			break;
+		default:
+			return 0;
+		}
+		if (writecnt != JEDEC_CE_60_OUTSIZE) {
+			msg_cerr("CHIP ERASE 0x60 outsize invalid!\n");
+			return 1;
+		}
+		if (readcnt != JEDEC_CE_60_INSIZE) {
+			msg_cerr("CHIP ERASE 0x60 insize invalid!\n");
+			return 1;
+		}
+		offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+		if (offs & (emu_jedec_ce_60_size - 1))
+			msg_cdbg("Unaligned CHIP ERASE 0x60\n");
+		offs &= ~(emu_jedec_ce_60_size - 1);
+		/* emu_jedec_ce_60_size is emu_chip_size. */
+		memset(flashchip_contents + offs, 0xff, emu_jedec_ce_60_size);
+		break;
+	case JEDEC_CE_C7:
+		/* BLOCK ERASE 0xd8 is supported by ST_M25P10_RES and
+		 * SST_SST25VF032B.
+		 */
+		switch (emu_chip) {
+		case EMULATE_ST_M25P10_RES:
+		case EMULATE_SST_SST25VF032B:
+			break;
+		default:
+			return 0;
+		}
+		if (writecnt != JEDEC_CE_C7_OUTSIZE) {
+			msg_cerr("CHIP ERASE 0xc7 outsize invalid!\n");
+			return 1;
+		}
+		if (readcnt != JEDEC_CE_C7_INSIZE) {
+			msg_cerr("CHIP ERASE 0xc7 insize invalid!\n");
+			return 1;
+		}
+		offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+		if (offs & (emu_jedec_ce_c7_size - 1))
+			msg_cdbg("Unaligned CHIP ERASE 0xc7\n");
+		offs &= ~(emu_jedec_ce_c7_size - 1);
+		/* emu_jedec_ce_c7_size is emu_chip_size. */
+		memset(flashchip_contents, 0xff, emu_jedec_ce_c7_size);
+		break;
+	default:
+		/* No special response. */
+		break;
+	}
+	return 0;
+}
+#endif
+
 int dummy_spi_send_command(unsigned int writecnt, unsigned int readcnt,
 		      const unsigned char *writearr, unsigned char *readarr)
 {
@@ -151,12 +541,20 @@
 	for (i = 0; i < writecnt; i++)
 		msg_pspew(" 0x%02x", writearr[i]);
 
+	/* Response for unknown commands and missing chip is 0xff. */
+	memset(readarr, 0xff, readcnt);
+#if EMULATE_SPI_CHIP
+	if (emu_chip != EMULATE_NONE)
+		if (emulate_spi_chip_response(writecnt, readcnt, writearr,
+					      readarr)) {
+			msg_cerr("Insane command sent to flash chip!\n");
+			return 1;
+		}
+#endif
 	msg_pspew(" reading %u bytes:", readcnt);
 	for (i = 0; i < readcnt; i++) {
-		msg_pspew(" 0xff");
-		readarr[i] = 0xff;
+		msg_pspew(" 0x%02x", readarr[i]);
 	}
-
 	msg_pspew("\n");
 	return 0;
 }
@@ -173,5 +571,6 @@
  */
 int dummy_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
 {
-	return spi_write_chunked(flash, buf, start, len, 256);
+	return spi_write_chunked(flash, buf, start, len,
+				 spi_write_256_chunksize);
 }


-- 
http://www.hailfinger.org/





More information about the flashrom mailing list