summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dummyflasher.c378
-rw-r--r--flash.h2
2 files changed, 369 insertions, 11 deletions
diff --git a/dummyflasher.c b/dummyflasher.c
index 9b8bb53..21ee9b4 100644
--- a/dummyflasher.c
+++ b/dummyflasher.c
@@ -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 @@ static void tolower_string(char *str)
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 @@ int dummy_init(void)
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 @@ void dummy_chip_readn(uint8_t *buf, const chipaddr addr, size_t len)
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 @@ int dummy_spi_send_command(unsigned int writecnt, unsigned int readcnt,
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 @@ int dummy_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len)
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);
}
diff --git a/flash.h b/flash.h
index 700b3f4..6ac3c1c 100644
--- a/flash.h
+++ b/flash.h
@@ -207,6 +207,8 @@ void print_banner(void);
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 */
OpenPOWER on IntegriCloud