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 c-d.hailfinger.devel.2006@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,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); }