Add SPI flash emulation capability to dummyflasher. You have to choose at compile time 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)
This code helped me find and fix various bugs in the SPI write code (patches forthcoming).
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-emulate_spi_flashchip/dummyflasher.c =================================================================== --- flashrom-emulate_spi_flashchip/dummyflasher.c (Revision 1216) +++ flashrom-emulate_spi_flashchip/dummyflasher.c (Arbeitskopie) @@ -25,6 +25,45 @@ #include "chipdrivers.h" #include "programmer.h"
+//#define EMULATE_ST_M25P10_RES 1 +//#define EMULATE_SST_SST25VF040_REMS 1 +//#define EMULATE_SST_SST25VF032B 1 + +#if EMULATE_ST_M25P10_RES + EMULATE_SST_SST25VF040_REMS + EMULATE_SST_SST25VF032B > 1 +#error Only one chip can be emulated. Please decide which one you want. +#endif + +#if EMULATE_ST_M25P10_RES || EMULATE_SST_SST25VF040_REMS || EMULATE_SST_SST25VF032B +#define EMULATE_SPI_CHIP 1 +#endif + +#if EMULATE_SPI_CHIP +#define EMULATE_CHIP 1 +#include "spi.h" +#endif + +#if EMULATE_ST_M25P10_RES +#define EMU_FLASHCHIP_SIZE 128 * 1024 +#define MAX_BYTEPROGRAM_SIZE 128 +#define MAX_AAI_SIZE 0 +#endif + +#if EMULATE_SST_SST25VF040_REMS +#define EMU_FLASHCHIP_SIZE 512 * 1024 +#define MAX_BYTEPROGRAM_SIZE 1 +#define MAX_AAI_SIZE 0 +#endif + +#if EMULATE_SST_SST25VF032B +#define EMU_FLASHCHIP_SIZE 4 * 1024 * 1024 +#define MAX_BYTEPROGRAM_SIZE 1 +#define MAX_AAI_SIZE 2 +#endif + +#if EMULATE_CHIP +static uint8_t flashchip_contents[EMU_FLASHCHIP_SIZE]; +#endif + static void tolower_string(char *str) { for (; *str != '\0'; str++) @@ -65,6 +104,25 @@ if (buses_supported == CHIP_BUSTYPE_NONE) msg_pdbg("Support for all flash bus types disabled.\n"); free(bustext); + +#if EMULATE_ST_M25P10_RES + msg_pdbg("Emulating ST M25P10.RES SPI flash chip (RES, page write)\n"); +#endif +#if EMULATE_SST_SST25VF040_REMS + msg_pdbg("Emulating SST SST25VF040.REMS SPI flash chip (REMS, byte " + "write)\n"); +#endif +#if EMULATE_SST_SST25VF032B + msg_pdbg("Emulating SST SST25VF032B SPI flash chip (RDID, AAI " + "write)\n"); +#endif +#if EMULATE_CHIP + msg_pdbg("Filling fake flash chip with 0xff, size %i\n", + sizeof(flashchip_contents)); + memset(flashchip_contents, 0xff, sizeof(flashchip_contents)); +#else + msg_pdbg("Not emulating any flash chip.\n"); +#endif return 0; }
@@ -140,6 +198,123 @@ 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; +#if EMULATE_SST_SST25VF032B + static int aai_offs; + static int aai_active = 0; +#endif + if (writecnt == 0) { + msg_cerr("No command sent to the chip!\n"); + return 1; + } + switch (writearr[0]) { +#if EMULATE_ST_M25P10_RES + case JEDEC_RES: + /* Respond with ST_M25P10_RES. */ + if (readcnt > 0) + readarr[0] = 0x10; + break; +#endif +#if EMULATE_SST_SST25VF040_REMS + case JEDEC_REMS: + /* Respond with SST_SST25VF040_REMS. */ + if (readcnt > 0) + readarr[0] = 0xbf; + if (readcnt > 1) + readarr[1] = 0x44; + break; +#endif +#if EMULATE_SST_SST25VF032B + case JEDEC_RDID: + /* Respond with SST_SST25VF032B. */ + if (readcnt > 0) + readarr[0] = 0xbf; + if (readcnt > 1) + readarr[1] = 0x25; + if (readcnt > 2) + readarr[2] = 0x4a; + break; +#endif + case JEDEC_RDSR: + memset(readarr, 0, readcnt); +#if EMULATE_SST_SST25VF032B + if (aai_active) + memset(readarr, 1 << 6, readcnt); +#endif + break; + case JEDEC_READ: + offs = writearr[1] << 16 | writearr[2] << 8 | + writearr[3]; + /* Truncate to EMU_FLASHCHIP_SIZE. */ + offs %= EMU_FLASHCHIP_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_FLASHCHIP_SIZE. */ + offs %= EMU_FLASHCHIP_SIZE; + if (writecnt < 5) { + msg_cerr("BYTE PROGRAM size too short!\n"); + return 1; + } + if (writecnt - 4 > MAX_BYTEPROGRAM_SIZE) { + msg_cerr("Max BYTE PROGRAM size exceeded!\n"); + return 1; + } + memcpy(flashchip_contents + offs, writearr + 4, writecnt - 4); + break; +#if EMULATE_SST_SST25VF032B + case JEDEC_AAI_WORD_PROGRAM: + 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_FLASHCHIP_SIZE. */ + aai_offs %= EMU_FLASHCHIP_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: + aai_active = 0; + break; +#endif + 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) { @@ -157,6 +332,9 @@ readarr[i] = 0xff; }
+#if EMULATE_SPI_CHIP + emulate_spi_chip_response(writecnt, readcnt, writearr, readarr); +#endif msg_pspew("\n"); return 0; }
New version. Choose your favourite chip at runtime, cleanup #ifdef mess, allow erase, set max write chunksize, provide flash image persistence.
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 not compiled in by default because erase functionality is incomplete. The first erase function for ST M25P10.RES and SST SST25VF032B each is implemented, everything else is ignored for now.
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 1217) +++ 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 1217) +++ 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,37 @@ #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; +#endif +#endif + +static int spi_write_256_chunksize = 256; + static void tolower_string(char *str) { for (; *str != '\0'; str++) @@ -34,6 +64,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 +99,95 @@ 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; + 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; + 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; + 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); + /* FIXME: malloc() error checking. */ + flashchip_contents = malloc(emu_chip_size); + 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 %s, size %li ", emu_persistent_image, + 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 +257,158 @@ 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: + if (emu_chip != EMULATE_SST_SST25VF032B) + break; + 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: + if (emu_chip != EMULATE_SST_SST25VF032B) + break; + aai_active = 0; + break; + case JEDEC_SE: + /* SECTOR ERASE 0x20 is only implemented for SST_SST25VF032B. */ + if (emu_chip != EMULATE_SST_SST25VF032B) + break; + 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 & (4096 - 1)) + msg_cdbg("Unaligned SECTOR ERASE 0x20\n"); + offs &= ~(4096 - 1); + memset(flashchip_contents + offs, 0xff, 4096); + break; + case JEDEC_BE_D8: + /* BLOCK ERASE 0xd8 is only implemented for ST_M25P10_RES. */ + if (emu_chip != EMULATE_ST_M25P10_RES) + break; + 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 & (32768 - 1)) + msg_cdbg("Unaligned SECTOR ERASE 0x20\n"); + offs &= ~(32768 - 1); + memset(flashchip_contents + offs, 0xff, 32768); + 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 +420,15 @@ 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 + emulate_spi_chip_response(writecnt, readcnt, writearr, readarr); +#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 +445,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); }
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@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); }
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); }
The patch seems very useful for testing and is benign at worst. I'd like to see it in the tree since it greatly reduced the turnaround time for testing partial writes and deserves some more usage. We can make it pretty later on -- I'm already very happy to see a big mess of #defines gone from the original patchset.
fwiw: Acked-by: David Hendricks dhendrix@google.com
On Sat, Oct 30, 2010 at 6:13 PM, Carl-Daniel Hailfinger < c-d.hailfinger.devel.2006@gmx.net> wrote:
/* 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;
Minor nitpick: These should probably slice/dice the #defines from flashchips.h.
On 01.11.2010 23:01, David Hendricks wrote:
The patch seems very useful for testing and is benign at worst. I'd like to see it in the tree since it greatly reduced the turnaround time for testing partial writes and deserves some more usage. We can make it pretty later on -- I'm already very happy to see a big mess of #defines gone from the original patchset.
fwiw: Acked-by: David Hendricks dhendrix@google.com
Thanks, committed in r1220.
On Sat, Oct 30, 2010 at 6:13 PM, Carl-Daniel Hailfinger wrote:
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;
Minor nitpick: These should probably slice/dice the #defines from flashchips.h.
I hoped to keep this dependency out of dummyflasher.c, but that hope probably won't last.
Regards, Carl-Daniel