Just in case reviewers are not already overloaded, here is another
version of the SPI flash chip emulator.
Changes in this version: Use msg_p* everywhere, kill a few switch()
statements to improve code readability.

The code is now feature complete.

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 <[email protected]>

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,43 @@
 #include "chipdrivers.h"
 #include "programmer.h"
 
+/* Remove the #define below if you don't want SPI flash chip emulation. */
+#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 +70,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 +105,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 +281,209 @@
        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_perr("No command sent to the chip!\n");
+               return 1;
+       }
+       /* TODO: Implement command blacklists here. */
+       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_perr("BYTE PROGRAM size too short!\n");
+                       return 1;
+               }
+               if (writecnt - 4 > emu_max_byteprogram_size) {
+                       msg_perr("Max BYTE PROGRAM size exceeded!\n");
+                       return 1;
+               }
+               memcpy(flashchip_contents + offs, writearr + 4, writecnt - 4);
+               break;
+       case JEDEC_AAI_WORD_PROGRAM:
+               if (!emu_max_aai_size)
+                       break;
+               if (!aai_active) {
+                       if (writecnt < JEDEC_AAI_WORD_PROGRAM_OUTSIZE) {
+                               msg_perr("Initial AAI WORD PROGRAM size too "
+                                        "short!\n");
+                               return 1;
+                       }
+                       if (writecnt > JEDEC_AAI_WORD_PROGRAM_OUTSIZE) {
+                               msg_perr("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_perr("Continuation AAI WORD PROGRAM size "
+                                        "too short!\n");
+                               return 1;
+                       }
+                       if (writecnt > JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE) {
+                               msg_perr("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:
+               if (!emu_max_aai_size)
+                       break;
+               aai_active = 0;
+               break;
+       case JEDEC_SE:
+               if (!emu_jedec_se_size)
+                       break;
+               if (writecnt != JEDEC_SE_OUTSIZE) {
+                       msg_perr("SECTOR ERASE 0x20 outsize invalid!\n");
+                       return 1;
+               }
+               if (readcnt != JEDEC_SE_INSIZE) {
+                       msg_perr("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_pdbg("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:
+               if (!emu_jedec_be_52_size)
+                       break;
+               if (writecnt != JEDEC_BE_52_OUTSIZE) {
+                       msg_perr("BLOCK ERASE 0x52 outsize invalid!\n");
+                       return 1;
+               }
+               if (readcnt != JEDEC_BE_52_INSIZE) {
+                       msg_perr("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_pdbg("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:
+               if (!emu_jedec_be_d8_size)
+                       break;
+               if (writecnt != JEDEC_BE_D8_OUTSIZE) {
+                       msg_perr("BLOCK ERASE 0xd8 outsize invalid!\n");
+                       return 1;
+               }
+               if (readcnt != JEDEC_BE_D8_INSIZE) {
+                       msg_perr("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_pdbg("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:
+               if (!emu_jedec_ce_60_size)
+                       break;
+               if (writecnt != JEDEC_CE_60_OUTSIZE) {
+                       msg_perr("CHIP ERASE 0x60 outsize invalid!\n");
+                       return 1;
+               }
+               if (readcnt != JEDEC_CE_60_INSIZE) {
+                       msg_perr("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_pdbg("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:
+               if (!emu_jedec_ce_c7_size)
+                       break;
+               if (writecnt != JEDEC_CE_C7_OUTSIZE) {
+                       msg_perr("CHIP ERASE 0xc7 outsize invalid!\n");
+                       return 1;
+               }
+               if (readcnt != JEDEC_CE_C7_INSIZE) {
+                       msg_perr("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_pdbg("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 +495,27 @@
        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
+       switch (emu_chip) {
+       case EMULATE_ST_M25P10_RES:
+       case EMULATE_SST_SST25VF040_REMS:
+       case EMULATE_SST_SST25VF032B:
+               if (emulate_spi_chip_response(writecnt, readcnt, writearr,
+                                             readarr)) {
+                       msg_perr("Invalid command sent to flash chip!\n");
+                       return 1;
+               }
+               break;
+       default:
+               break;
+       }
+#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;
 }
@@ -167,11 +526,8 @@
        return spi_read_chunked(flash, buf, start, len, 64 * 1024);
 }
 
-/* Is is impossible to trigger this code path because dummyflasher probing will
- * never be successful, and the current frontend refuses to write in that case.
- * Other frontends may allow writing even for non-detected chips, though.
- */
 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/


_______________________________________________
flashrom mailing list
[email protected]
http://www.flashrom.org/mailman/listinfo/flashrom

Reply via email to