From 38866f7299d8ecb9a37b52141e9f661384a8c2a9 Mon Sep 17 00:00:00 2001 From: hailfinger Date: Mon, 1 Nov 2010 22:07:04 +0000 Subject: 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 Acked-by: David Hendricks git-svn-id: https://code.coreboot.org/svn/flashrom/trunk@1220 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1 --- dummyflasher.c | 378 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 367 insertions(+), 11 deletions(-) (limited to 'dummyflasher.c') 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 +#include +#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); } -- cgit v1.2.1