From 7469710d05677c7bc050621cae90ecc044a41231 Mon Sep 17 00:00:00 2001 From: Anastasia Klimchuk Date: Mon, 15 Feb 2021 14:57:56 +1100 Subject: dummyflasher.c: Remove forward-declarations Reorder functions to avoid forward-declarations BUG=b:140394053 TEST=builds Change-Id: Ibfe9f556316ed509cbec522b4c9cb4c9041e5fdd Signed-off-by: Anastasia Klimchuk Reviewed-on: https://review.coreboot.org/c/flashrom/+/50712 Tested-by: build bot (Jenkins) Reviewed-by: Edward O'Callaghan Reviewed-by: Sam McNally Reviewed-by: Angel Pons --- dummyflasher.c | 1522 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 755 insertions(+), 767 deletions(-) diff --git a/dummyflasher.c b/dummyflasher.c index 92c30ee27..5190282c8 100644 --- a/dummyflasher.c +++ b/dummyflasher.c @@ -102,542 +102,121 @@ static const uint8_t sfdp_table[] = { #endif static unsigned int spi_write_256_chunksize = 256; +static enum chipbustype dummy_buses_supported = BUS_NONE; -static int dummy_spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, - const unsigned char *writearr, unsigned char *readarr); -static int dummy_spi_write_256(struct flashctx *flash, const uint8_t *buf, - unsigned int start, unsigned int len); -static void dummy_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr); -static void dummy_chip_writew(const struct flashctx *flash, uint16_t val, chipaddr addr); -static void dummy_chip_writel(const struct flashctx *flash, uint32_t val, chipaddr addr); -static void dummy_chip_writen(const struct flashctx *flash, const uint8_t *buf, chipaddr addr, size_t len); -static uint8_t dummy_chip_readb(const struct flashctx *flash, const chipaddr addr); -static uint16_t dummy_chip_readw(const struct flashctx *flash, const chipaddr addr); -static uint32_t dummy_chip_readl(const struct flashctx *flash, const chipaddr addr); -static void dummy_chip_readn(const struct flashctx *flash, uint8_t *buf, const chipaddr addr, size_t len); +void *dummy_map(const char *descr, uintptr_t phys_addr, size_t len) +{ + msg_pspew("%s: Mapping %s, 0x%zx bytes at 0x%0*" PRIxPTR "\n", + __func__, descr, len, PRIxPTR_WIDTH, phys_addr); + return (void *)phys_addr; +} -static struct spi_master spi_master_dummyflasher = { - .features = SPI_MASTER_4BA, - .max_data_read = MAX_DATA_READ_UNLIMITED, - .max_data_write = MAX_DATA_UNSPECIFIED, - .command = dummy_spi_send_command, - .multicommand = default_spi_send_multicommand, - .read = default_spi_read, - .write_256 = dummy_spi_write_256, - .write_aai = default_spi_write_aai, -}; +void dummy_unmap(void *virt_addr, size_t len) +{ + msg_pspew("%s: Unmapping 0x%zx bytes at %p\n", __func__, len, virt_addr); +} -static struct par_master par_master_dummy = { - .chip_readb = dummy_chip_readb, - .chip_readw = dummy_chip_readw, - .chip_readl = dummy_chip_readl, - .chip_readn = dummy_chip_readn, - .chip_writeb = dummy_chip_writeb, - .chip_writew = dummy_chip_writew, - .chip_writel = dummy_chip_writel, - .chip_writen = dummy_chip_writen, -}; +static int dummy_spi_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len) +{ + return spi_write_chunked(flash, buf, start, len, + spi_write_256_chunksize); +} -static enum chipbustype dummy_buses_supported = BUS_NONE; +static void dummy_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr) +{ + msg_pspew("%s: addr=0x%" PRIxPTR ", val=0x%02x\n", __func__, addr, val); +} -static int dummy_shutdown(void *data) +static void dummy_chip_writew(const struct flashctx *flash, uint16_t val, chipaddr addr) { - msg_pspew("%s\n", __func__); -#if EMULATE_CHIP - struct emu_data *emu_data = (struct emu_data *)data; - if (emu_data->emu_chip != EMULATE_NONE) { - if (emu_data->emu_persistent_image && emu_data->emu_modified) { - msg_pdbg("Writing %s\n", emu_data->emu_persistent_image); - write_buf_to_file(flashchip_contents, - emu_data->emu_chip_size, - emu_data->emu_persistent_image); - free(emu_data->emu_persistent_image); - emu_data->emu_persistent_image = NULL; - } - free(flashchip_contents); - } -#endif - return 0; + msg_pspew("%s: addr=0x%" PRIxPTR ", val=0x%04x\n", __func__, addr, val); } -int dummy_init(void) +static void dummy_chip_writel(const struct flashctx *flash, uint32_t val, chipaddr addr) { - char *bustext = NULL; - char *tmp = NULL; - unsigned int i; -#if EMULATE_SPI_CHIP - char *status = NULL; - int size = -1; /* size for VARIABLE_SIZE chip device */ -#endif -#if EMULATE_CHIP - struct stat image_stat; -#endif + msg_pspew("%s: addr=0x%" PRIxPTR ", val=0x%08x\n", __func__, addr, val); +} - struct emu_data *data = calloc(1, sizeof(struct emu_data)); - if (!data) { - msg_perr("Out of memory!\n"); - return 1; +static void dummy_chip_writen(const struct flashctx *flash, const uint8_t *buf, chipaddr addr, size_t len) +{ + size_t i; + msg_pspew("%s: addr=0x%" PRIxPTR ", len=0x%zx, writing data (hex):", __func__, addr, len); + for (i = 0; i < len; i++) { + if ((i % 16) == 0) + msg_pspew("\n"); + msg_pspew("%02x ", buf[i]); } - data->emu_chip = EMULATE_NONE; - data->delay_us = 0; - spi_master_dummyflasher.data = data; - par_master_dummy.data = data; +} - msg_pspew("%s\n", __func__); +static uint8_t dummy_chip_readb(const struct flashctx *flash, const chipaddr addr) +{ + msg_pspew("%s: addr=0x%" PRIxPTR ", returning 0xff\n", __func__, addr); + return 0xff; +} - bustext = extract_programmer_param("bus"); - msg_pdbg("Requested buses are: %s\n", bustext ? bustext : "default"); - if (!bustext) - bustext = strdup("parallel+lpc+fwh+spi"); - /* Convert the parameters to lowercase. */ - tolower_string(bustext); +static uint16_t dummy_chip_readw(const struct flashctx *flash, const chipaddr addr) +{ + msg_pspew("%s: addr=0x%" PRIxPTR ", returning 0xffff\n", __func__, addr); + return 0xffff; +} - dummy_buses_supported = BUS_NONE; - if (strstr(bustext, "parallel")) { - dummy_buses_supported |= BUS_PARALLEL; - msg_pdbg("Enabling support for %s flash.\n", "parallel"); - } - if (strstr(bustext, "lpc")) { - dummy_buses_supported |= BUS_LPC; - msg_pdbg("Enabling support for %s flash.\n", "LPC"); - } - if (strstr(bustext, "fwh")) { - dummy_buses_supported |= BUS_FWH; - msg_pdbg("Enabling support for %s flash.\n", "FWH"); - } - if (strstr(bustext, "spi")) { - dummy_buses_supported |= BUS_SPI; - msg_pdbg("Enabling support for %s flash.\n", "SPI"); - } - if (dummy_buses_supported == BUS_NONE) - msg_pdbg("Support for all flash bus types disabled.\n"); - free(bustext); +static uint32_t dummy_chip_readl(const struct flashctx *flash, const chipaddr addr) +{ + msg_pspew("%s: addr=0x%" PRIxPTR ", returning 0xffffffff\n", __func__, addr); + return 0xffffffff; +} - 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; - } - } +static void dummy_chip_readn(const struct flashctx *flash, uint8_t *buf, const chipaddr addr, size_t len) +{ + msg_pspew("%s: addr=0x%" PRIxPTR ", len=0x%zx, returning array of 0xff\n", __func__, addr, len); + memset(buf, 0xff, len); + return; +} - tmp = extract_programmer_param("spi_blacklist"); - if (tmp) { - i = strlen(tmp); - if (!strncmp(tmp, "0x", 2)) { - i -= 2; - memmove(tmp, tmp + 2, i + 1); - } - if ((i > 512) || (i % 2)) { - msg_perr("Invalid SPI command blacklist length\n"); - free(tmp); - return 1; - } - data->spi_blacklist_size = i / 2; - for (i = 0; i < data->spi_blacklist_size * 2; i++) { - if (!isxdigit((unsigned char)tmp[i])) { - msg_perr("Invalid char \"%c\" in SPI command " - "blacklist\n", tmp[i]); - free(tmp); - return 1; - } - } - for (i = 0; i < data->spi_blacklist_size; i++) { - unsigned int tmp2; - /* SCNx8 is apparently not supported by MSVC (and thus - * MinGW), so work around it with an extra variable - */ - sscanf(tmp + i * 2, "%2x", &tmp2); - data->spi_blacklist[i] = (uint8_t)tmp2; - } - msg_pdbg("SPI blacklist is "); - for (i = 0; i < data->spi_blacklist_size; i++) - msg_pdbg("%02x ", data->spi_blacklist[i]); - msg_pdbg(", size %u\n", data->spi_blacklist_size); - } - free(tmp); +static struct emu_data* get_data_from_context(const struct flashctx *flash) +{ + if (dummy_buses_supported & (BUS_PARALLEL | BUS_LPC | BUS_FWH)) + return (struct emu_data *)flash->mst->par.data; + else if (dummy_buses_supported & BUS_SPI) + return (struct emu_data *)flash->mst->spi.data; - tmp = extract_programmer_param("spi_ignorelist"); - if (tmp) { - i = strlen(tmp); - if (!strncmp(tmp, "0x", 2)) { - i -= 2; - memmove(tmp, tmp + 2, i + 1); - } - if ((i > 512) || (i % 2)) { - msg_perr("Invalid SPI command ignorelist length\n"); - free(tmp); - return 1; - } - data->spi_ignorelist_size = i / 2; - for (i = 0; i < data->spi_ignorelist_size * 2; i++) { - if (!isxdigit((unsigned char)tmp[i])) { - msg_perr("Invalid char \"%c\" in SPI command " - "ignorelist\n", tmp[i]); - free(tmp); - return 1; - } + return NULL; /* buses was set to BUS_NONE. */ +} + +#if EMULATE_SPI_CHIP +static int emulate_spi_chip_response(unsigned int writecnt, + unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr, + struct emu_data *data) +{ + unsigned int offs, i, toread; + static int unsigned aai_offs; + const unsigned char sst25vf040_rems_response[2] = {0xbf, 0x44}; + const unsigned char sst25vf032b_rems_response[2] = {0xbf, 0x4a}; + const unsigned char mx25l6436_rems_response[2] = {0xc2, 0x16}; + const unsigned char w25q128fv_rems_response[2] = {0xef, 0x17}; + + if (writecnt == 0) { + msg_perr("No command sent to the chip!\n"); + return 1; + } + /* spi_blacklist has precedence over spi_ignorelist. */ + for (i = 0; i < data->spi_blacklist_size; i++) { + if (writearr[0] == data->spi_blacklist[i]) { + msg_pdbg("Refusing blacklisted SPI command 0x%02x\n", + data->spi_blacklist[i]); + return SPI_INVALID_OPCODE; } - for (i = 0; i < data->spi_ignorelist_size; i++) { - unsigned int tmp2; - /* SCNx8 is apparently not supported by MSVC (and thus - * MinGW), so work around it with an extra variable + } + for (i = 0; i < data->spi_ignorelist_size; i++) { + if (writearr[0] == data->spi_ignorelist[i]) { + msg_cdbg("Ignoring ignorelisted SPI command 0x%02x\n", + data->spi_ignorelist[i]); + /* Return success because the command does not fail, + * it is simply ignored. */ - sscanf(tmp + i * 2, "%2x", &tmp2); - data->spi_ignorelist[i] = (uint8_t)tmp2; - } - msg_pdbg("SPI ignorelist is "); - for (i = 0; i < data->spi_ignorelist_size; i++) - msg_pdbg("%02x ", data->spi_ignorelist[i]); - msg_pdbg(", size %u\n", data->spi_ignorelist_size); - } - free(tmp); - - /* frequency to emulate in Hz (default), KHz, or MHz */ - tmp = extract_programmer_param("freq"); - if (tmp) { - unsigned long int freq; - char *units = tmp; - char *end = tmp + strlen(tmp); - - errno = 0; - freq = strtoul(tmp, &units, 0); - if (errno) { - msg_perr("Invalid frequency \"%s\", %s\n", - tmp, strerror(errno)); - free(tmp); - return 1; - } - - if ((units > tmp) && (units < end)) { - int units_valid = 0; - - if (units < end - 3) { - ; - } else if (units == end - 2) { - if (!strcasecmp(units, "hz")) - units_valid = 1; - } else if (units == end - 3) { - if (!strcasecmp(units, "khz")) { - freq *= 1000; - units_valid = 1; - } else if (!strcasecmp(units, "mhz")) { - freq *= 1000000; - units_valid = 1; - } - } - - if (!units_valid) { - msg_perr("Invalid units: %s\n", units); - free(tmp); - return 1; - } - } - - /* Assume we only work with bytes and transfer at 1 bit/Hz */ - data->delay_us = (1000000 * 8) / freq; - } - free(tmp); - -#if EMULATE_CHIP -#if EMULATE_SPI_CHIP - tmp = extract_programmer_param("size"); - if (tmp) { - size = strtol(tmp, NULL, 10); - if (size <= 0 || (size % 1024 != 0)) { - msg_perr("%s: Chip size is not a multipler of 1024: %s\n", - __func__, tmp); - free(tmp); - return 1; - } - free(tmp); - } -#endif - - tmp = extract_programmer_param("emulate"); - if (!tmp) { - msg_pdbg("Not emulating any flash chip.\n"); - /* Nothing else to do. */ - goto dummy_init_out; - } -#if EMULATE_SPI_CHIP - if (!strcmp(tmp, "M25P10.RES")) { - data->emu_chip = EMULATE_ST_M25P10_RES; - data->emu_chip_size = 128 * 1024; - data->emu_max_byteprogram_size = 128; - data->emu_max_aai_size = 0; - data->emu_jedec_se_size = 0; - data->emu_jedec_be_52_size = 0; - data->emu_jedec_be_d8_size = 32 * 1024; - data->emu_jedec_ce_60_size = 0; - data->emu_jedec_ce_c7_size = data->emu_chip_size; - msg_pdbg("Emulating ST M25P10.RES SPI flash chip (RES, page " - "write)\n"); - } - if (!strcmp(tmp, "SST25VF040.REMS")) { - data->emu_chip = EMULATE_SST_SST25VF040_REMS; - data->emu_chip_size = 512 * 1024; - data->emu_max_byteprogram_size = 1; - data->emu_max_aai_size = 0; - data->emu_jedec_se_size = 4 * 1024; - data->emu_jedec_be_52_size = 32 * 1024; - data->emu_jedec_be_d8_size = 0; - data->emu_jedec_ce_60_size = data->emu_chip_size; - data->emu_jedec_ce_c7_size = 0; - msg_pdbg("Emulating SST SST25VF040.REMS SPI flash chip (REMS, " - "byte write)\n"); - } - if (!strcmp(tmp, "SST25VF032B")) { - data->emu_chip = EMULATE_SST_SST25VF032B; - data->emu_chip_size = 4 * 1024 * 1024; - data->emu_max_byteprogram_size = 1; - data->emu_max_aai_size = 2; - data->emu_jedec_se_size = 4 * 1024; - data->emu_jedec_be_52_size = 32 * 1024; - data->emu_jedec_be_d8_size = 64 * 1024; - data->emu_jedec_ce_60_size = data->emu_chip_size; - data->emu_jedec_ce_c7_size = data->emu_chip_size; - msg_pdbg("Emulating SST SST25VF032B SPI flash chip (RDID, AAI " - "write)\n"); - } - if (!strcmp(tmp, "MX25L6436")) { - data->emu_chip = EMULATE_MACRONIX_MX25L6436; - data->emu_chip_size = 8 * 1024 * 1024; - data->emu_max_byteprogram_size = 256; - data->emu_max_aai_size = 0; - data->emu_jedec_se_size = 4 * 1024; - data->emu_jedec_be_52_size = 32 * 1024; - data->emu_jedec_be_d8_size = 64 * 1024; - data->emu_jedec_ce_60_size = data->emu_chip_size; - data->emu_jedec_ce_c7_size = data->emu_chip_size; - msg_pdbg("Emulating Macronix MX25L6436 SPI flash chip (RDID, " - "SFDP)\n"); - } - if (!strcmp(tmp, "W25Q128FV")) { - data->emu_chip = EMULATE_WINBOND_W25Q128FV; - data->emu_chip_size = 16 * 1024 * 1024; - data->emu_max_byteprogram_size = 256; - data->emu_max_aai_size = 0; - data->emu_jedec_se_size = 4 * 1024; - data->emu_jedec_be_52_size = 32 * 1024; - data->emu_jedec_be_d8_size = 64 * 1024; - data->emu_jedec_ce_60_size = data->emu_chip_size; - data->emu_jedec_ce_c7_size = data->emu_chip_size; - msg_pdbg("Emulating Winbond W25Q128FV SPI flash chip (RDID)\n"); - } - - /* The name of variable-size virtual chip. A 4 MiB flash example: - * flashrom -p dummy:emulate=VARIABLE_SIZE,size=4194304 - */ - if (!strcmp(tmp, "VARIABLE_SIZE")) { - if (size == -1) { - msg_perr("%s: the size parameter is not given.\n", __func__); - free(tmp); - return 1; - } - data->emu_chip = EMULATE_VARIABLE_SIZE; - data->emu_chip_size = size; - data->emu_max_byteprogram_size = 256; - data->emu_max_aai_size = 0; - data->emu_jedec_se_size = 4 * 1024; - data->emu_jedec_be_52_size = 32 * 1024; - data->emu_jedec_be_d8_size = 64 * 1024; - data->emu_jedec_ce_60_size = data->emu_chip_size; - data->emu_jedec_ce_c7_size = data->emu_chip_size; - msg_pdbg("Emulating generic SPI flash chip (size=%d bytes)\n", - data->emu_chip_size); - } -#endif - if (data->emu_chip == EMULATE_NONE) { - msg_perr("Invalid chip specified for emulation: %s\n", tmp); - free(tmp); - return 1; - } - free(tmp); - - /* Should emulated flash erase to zero (yes/no)? */ - tmp = extract_programmer_param("erase_to_zero"); - if (tmp) { - if (!strcmp(tmp, "yes")) { - msg_pdbg("Emulated chip will erase to 0x00\n"); - data->erase_to_zero = 1; - } else if (!strcmp(tmp, "no")) { - msg_pdbg("Emulated chip will erase to 0xff\n"); - } else { - msg_perr("erase_to_zero can be \"yes\" or \"no\"\n"); - free(tmp); - return 1; - } - } - free(tmp); - - flashchip_contents = malloc(data->emu_chip_size); - if (!flashchip_contents) { - msg_perr("Out of memory!\n"); - return 1; - } - -#ifdef EMULATE_SPI_CHIP - status = extract_programmer_param("spi_status"); - if (status) { - char *endptr; - errno = 0; - data->emu_status = strtoul(status, &endptr, 0); - free(status); - if (errno != 0 || status == endptr) { - msg_perr("Error: initial status register specified, " - "but the value could not be converted.\n"); - return 1; - } - msg_pdbg("Initial status register is set to 0x%02x.\n", - data->emu_status); - } -#endif - - msg_pdbg("Filling fake flash chip with 0x%02x, size %i\n", - data->erase_to_zero ? 0x00 : 0xff, data->emu_chip_size); - memset(flashchip_contents, data->erase_to_zero ? 0x00 : 0xff, data->emu_chip_size); - - /* Will be freed by shutdown function if necessary. */ - data->emu_persistent_image = extract_programmer_param("image"); - if (!data->emu_persistent_image) { - /* Nothing else to do. */ - goto dummy_init_out; - } - /* We will silently (in default verbosity) ignore the file if it does not exist (yet) or the size does - * not match the emulated chip. */ - if (!stat(data->emu_persistent_image, &image_stat)) { - msg_pdbg("Found persistent image %s, %jd B ", - data->emu_persistent_image, (intmax_t)image_stat.st_size); - if ((uintmax_t)image_stat.st_size == data->emu_chip_size) { - msg_pdbg("matches.\n"); - msg_pdbg("Reading %s\n", data->emu_persistent_image); - if (read_buf_from_file(flashchip_contents, data->emu_chip_size, - data->emu_persistent_image)) { - msg_perr("Unable to read %s\n", data->emu_persistent_image); - free(flashchip_contents); - return 1; - } - } else { - msg_pdbg("doesn't match.\n"); - } - } -#endif - -dummy_init_out: - if (register_shutdown(dummy_shutdown, data)) { - free(flashchip_contents); - free(data); - return 1; - } - if (dummy_buses_supported & (BUS_PARALLEL | BUS_LPC | BUS_FWH)) - register_par_master(&par_master_dummy, - dummy_buses_supported & (BUS_PARALLEL | BUS_LPC | BUS_FWH)); - if (dummy_buses_supported & BUS_SPI) - register_spi_master(&spi_master_dummyflasher); - - return 0; -} - -void *dummy_map(const char *descr, uintptr_t phys_addr, size_t len) -{ - msg_pspew("%s: Mapping %s, 0x%zx bytes at 0x%0*" PRIxPTR "\n", - __func__, descr, len, PRIxPTR_WIDTH, phys_addr); - return (void *)phys_addr; -} - -void dummy_unmap(void *virt_addr, size_t len) -{ - msg_pspew("%s: Unmapping 0x%zx bytes at %p\n", __func__, len, virt_addr); -} - -static void dummy_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr) -{ - msg_pspew("%s: addr=0x%" PRIxPTR ", val=0x%02x\n", __func__, addr, val); -} - -static void dummy_chip_writew(const struct flashctx *flash, uint16_t val, chipaddr addr) -{ - msg_pspew("%s: addr=0x%" PRIxPTR ", val=0x%04x\n", __func__, addr, val); -} - -static void dummy_chip_writel(const struct flashctx *flash, uint32_t val, chipaddr addr) -{ - msg_pspew("%s: addr=0x%" PRIxPTR ", val=0x%08x\n", __func__, addr, val); -} - -static void dummy_chip_writen(const struct flashctx *flash, const uint8_t *buf, chipaddr addr, size_t len) -{ - size_t i; - msg_pspew("%s: addr=0x%" PRIxPTR ", len=0x%zx, writing data (hex):", __func__, addr, len); - for (i = 0; i < len; i++) { - if ((i % 16) == 0) - msg_pspew("\n"); - msg_pspew("%02x ", buf[i]); - } -} - -static uint8_t dummy_chip_readb(const struct flashctx *flash, const chipaddr addr) -{ - msg_pspew("%s: addr=0x%" PRIxPTR ", returning 0xff\n", __func__, addr); - return 0xff; -} - -static uint16_t dummy_chip_readw(const struct flashctx *flash, const chipaddr addr) -{ - msg_pspew("%s: addr=0x%" PRIxPTR ", returning 0xffff\n", __func__, addr); - return 0xffff; -} - -static uint32_t dummy_chip_readl(const struct flashctx *flash, const chipaddr addr) -{ - msg_pspew("%s: addr=0x%" PRIxPTR ", returning 0xffffffff\n", __func__, addr); - return 0xffffffff; -} - -static void dummy_chip_readn(const struct flashctx *flash, uint8_t *buf, const chipaddr addr, size_t len) -{ - msg_pspew("%s: addr=0x%" PRIxPTR ", len=0x%zx, returning array of 0xff\n", __func__, addr, len); - memset(buf, 0xff, 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, - struct emu_data *data) -{ - unsigned int offs, i, toread; - static int unsigned aai_offs; - const unsigned char sst25vf040_rems_response[2] = {0xbf, 0x44}; - const unsigned char sst25vf032b_rems_response[2] = {0xbf, 0x4a}; - const unsigned char mx25l6436_rems_response[2] = {0xc2, 0x16}; - const unsigned char w25q128fv_rems_response[2] = {0xef, 0x17}; - - if (writecnt == 0) { - msg_perr("No command sent to the chip!\n"); - return 1; - } - /* spi_blacklist has precedence over spi_ignorelist. */ - for (i = 0; i < data->spi_blacklist_size; i++) { - if (writearr[0] == data->spi_blacklist[i]) { - msg_pdbg("Refusing blacklisted SPI command 0x%02x\n", - data->spi_blacklist[i]); - return SPI_INVALID_OPCODE; - } - } - for (i = 0; i < data->spi_ignorelist_size; i++) { - if (writearr[0] == data->spi_ignorelist[i]) { - msg_cdbg("Ignoring ignorelisted SPI command 0x%02x\n", - data->spi_ignorelist[i]); - /* Return success because the command does not fail, - * it is simply ignored. - */ - return 0; + return 0; } } @@ -750,300 +329,709 @@ static int emulate_spi_chip_response(unsigned int writecnt, break; } break; - case JEDEC_RDSR: - memset(readarr, data->emu_status, readcnt); - break; - /* FIXME: this should be chip-specific. */ - case JEDEC_EWSR: - case JEDEC_WREN: - data->emu_status |= SPI_SR_WEL; + case JEDEC_RDSR: + memset(readarr, data->emu_status, readcnt); + break; + /* FIXME: this should be chip-specific. */ + case JEDEC_EWSR: + case JEDEC_WREN: + data->emu_status |= SPI_SR_WEL; + break; + case JEDEC_WRSR: + if (!(data->emu_status & SPI_SR_WEL)) { + msg_perr("WRSR attempted, but WEL is 0!\n"); + break; + } + /* FIXME: add some reasonable simulation of the busy flag */ + data->emu_status = writearr[1] & ~SPI_SR_WIP; + msg_pdbg2("WRSR wrote 0x%02x.\n", data->emu_status); + break; + case JEDEC_READ: + offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3]; + /* Truncate to emu_chip_size. */ + offs %= data->emu_chip_size; + if (readcnt > 0) + memcpy(readarr, flashchip_contents + offs, readcnt); + break; + case JEDEC_READ_4BA: + offs = writearr[1] << 24 | writearr[2] << 16 | writearr[3] << 8 | writearr[4]; + /* Truncate to emu_chip_size. */ + offs %= data->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 %= data->emu_chip_size; + if (writecnt < 5) { + msg_perr("BYTE PROGRAM size too short!\n"); + return 1; + } + if (writecnt - 4 > data->emu_max_byteprogram_size) { + msg_perr("Max BYTE PROGRAM size exceeded!\n"); + return 1; + } + memcpy(flashchip_contents + offs, writearr + 4, writecnt - 4); + data->emu_modified = 1; + break; + case JEDEC_BYTE_PROGRAM_4BA: + offs = writearr[1] << 24 | writearr[2] << 16 | writearr[3] << 8 | writearr[4]; + /* Truncate to emu_chip_size. */ + offs %= data->emu_chip_size; + if (writecnt < 6) { + msg_perr("BYTE PROGRAM size too short!\n"); + return 1; + } + if (writecnt - 5 > data->emu_max_byteprogram_size) { + msg_perr("Max BYTE PROGRAM size exceeded!\n"); + return 1; + } + memcpy(flashchip_contents + offs, writearr + 5, writecnt - 5); + data->emu_modified = 1; + break; + case JEDEC_AAI_WORD_PROGRAM: + if (!data->emu_max_aai_size) + break; + if (!(data->emu_status & SPI_SR_AAI)) { + 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; + } + data->emu_status |= SPI_SR_AAI; + aai_offs = writearr[1] << 16 | writearr[2] << 8 | + writearr[3]; + /* Truncate to emu_chip_size. */ + aai_offs %= data->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; + } + data->emu_modified = 1; + break; + case JEDEC_WRDI: + if (data->emu_max_aai_size) + data->emu_status &= ~SPI_SR_AAI; + break; + case JEDEC_SE: + if (!data->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 & (data->emu_jedec_se_size - 1)) + msg_pdbg("Unaligned SECTOR ERASE 0x20: 0x%x\n", offs); + offs &= ~(data->emu_jedec_se_size - 1); + memset(flashchip_contents + offs, 0xff, data->emu_jedec_se_size); + data->emu_modified = 1; + break; + case JEDEC_BE_52: + if (!data->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 & (data->emu_jedec_be_52_size - 1)) + msg_pdbg("Unaligned BLOCK ERASE 0x52: 0x%x\n", offs); + offs &= ~(data->emu_jedec_be_52_size - 1); + memset(flashchip_contents + offs, 0xff, data->emu_jedec_be_52_size); + data->emu_modified = 1; + break; + case JEDEC_BE_D8: + if (!data->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 & (data->emu_jedec_be_d8_size - 1)) + msg_pdbg("Unaligned BLOCK ERASE 0xd8: 0x%x\n", offs); + offs &= ~(data->emu_jedec_be_d8_size - 1); + memset(flashchip_contents + offs, 0xff, data->emu_jedec_be_d8_size); + data->emu_modified = 1; + break; + case JEDEC_CE_60: + if (!data->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; + } + /* JEDEC_CE_60_OUTSIZE is 1 (no address) -> no offset. */ + /* emu_jedec_ce_60_size is emu_chip_size. */ + memset(flashchip_contents, 0xff, data->emu_jedec_ce_60_size); + data->emu_modified = 1; break; - case JEDEC_WRSR: - if (!(data->emu_status & SPI_SR_WEL)) { - msg_perr("WRSR attempted, but WEL is 0!\n"); + case JEDEC_CE_C7: + if (!data->emu_jedec_ce_c7_size) break; + if (writecnt != JEDEC_CE_C7_OUTSIZE) { + msg_perr("CHIP ERASE 0xc7 outsize invalid!\n"); + return 1; } - /* FIXME: add some reasonable simulation of the busy flag */ - data->emu_status = writearr[1] & ~SPI_SR_WIP; - msg_pdbg2("WRSR wrote 0x%02x.\n", data->emu_status); + if (readcnt != JEDEC_CE_C7_INSIZE) { + msg_perr("CHIP ERASE 0xc7 insize invalid!\n"); + return 1; + } + /* JEDEC_CE_C7_OUTSIZE is 1 (no address) -> no offset. */ + /* emu_jedec_ce_c7_size is emu_chip_size. */ + memset(flashchip_contents, 0xff, data->emu_jedec_ce_c7_size); + data->emu_modified = 1; break; - case JEDEC_READ: + case JEDEC_SFDP: + if (data->emu_chip != EMULATE_MACRONIX_MX25L6436) + break; + if (writecnt < 4) + break; offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3]; - /* Truncate to emu_chip_size. */ - offs %= data->emu_chip_size; - if (readcnt > 0) - memcpy(readarr, flashchip_contents + offs, readcnt); + + /* SFDP expects one dummy byte after the address. */ + if (writecnt == 4) { + /* The dummy byte was not written, make sure it is read instead. + * Shifting and shortening the read array does achieve this goal. + */ + readarr++; + readcnt--; + } else { + /* The response is shifted if more than 5 bytes are written, because SFDP data is + * already shifted out by the chip while those superfluous bytes are written. */ + offs += writecnt - 5; + } + + /* The SFDP spec implies that the start address of an SFDP read may be truncated to fit in the + * SFDP table address space, i.e. the start address may be wrapped around at SFDP table size. + * This is a reasonable implementation choice in hardware because it saves a few gates. */ + if (offs >= sizeof(sfdp_table)) { + msg_pdbg("Wrapping the start address around the SFDP table boundary (using 0x%x " + "instead of 0x%x).\n", (unsigned int)(offs % sizeof(sfdp_table)), offs); + offs %= sizeof(sfdp_table); + } + toread = min(sizeof(sfdp_table) - offs, readcnt); + memcpy(readarr, sfdp_table + offs, toread); + if (toread < readcnt) + msg_pdbg("Crossing the SFDP table boundary in a single " + "continuous chunk produces undefined results " + "after that point.\n"); break; - case JEDEC_READ_4BA: - offs = writearr[1] << 24 | writearr[2] << 16 | writearr[3] << 8 | writearr[4]; - /* Truncate to emu_chip_size. */ - offs %= data->emu_chip_size; - if (readcnt > 0) - memcpy(readarr, flashchip_contents + offs, readcnt); + default: + /* No special response. */ break; - case JEDEC_BYTE_PROGRAM: - offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3]; - /* Truncate to emu_chip_size. */ - offs %= data->emu_chip_size; - if (writecnt < 5) { - msg_perr("BYTE PROGRAM size too short!\n"); + } + if (writearr[0] != JEDEC_WREN && writearr[0] != JEDEC_EWSR) + data->emu_status &= ~SPI_SR_WEL; + return 0; +} +#endif + +static int dummy_spi_send_command(const struct flashctx *flash, unsigned int writecnt, + unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr) +{ + unsigned int i; + struct emu_data *emu_data = get_data_from_context(flash); + if (!emu_data) { + msg_perr("No data in flash context!\n"); + return 1; + } + + msg_pspew("%s:", __func__); + + msg_pspew(" writing %u bytes:", writecnt); + 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_data->emu_chip) { + case EMULATE_ST_M25P10_RES: + case EMULATE_SST_SST25VF040_REMS: + case EMULATE_SST_SST25VF032B: + case EMULATE_MACRONIX_MX25L6436: + case EMULATE_WINBOND_W25Q128FV: + case EMULATE_VARIABLE_SIZE: + if (emulate_spi_chip_response(writecnt, readcnt, writearr, + readarr, emu_data)) { + msg_pdbg("Invalid command sent to flash chip!\n"); return 1; } - if (writecnt - 4 > data->emu_max_byteprogram_size) { - msg_perr("Max BYTE PROGRAM size exceeded!\n"); + break; + default: + break; + } +#endif + msg_pspew(" reading %u bytes:", readcnt); + for (i = 0; i < readcnt; i++) + msg_pspew(" 0x%02x", readarr[i]); + msg_pspew("\n"); + + programmer_delay((writecnt + readcnt) * emu_data->delay_us); + return 0; +} + + + +static struct spi_master spi_master_dummyflasher = { + .features = SPI_MASTER_4BA, + .max_data_read = MAX_DATA_READ_UNLIMITED, + .max_data_write = MAX_DATA_UNSPECIFIED, + .command = dummy_spi_send_command, + .multicommand = default_spi_send_multicommand, + .read = default_spi_read, + .write_256 = dummy_spi_write_256, + .write_aai = default_spi_write_aai, +}; + +static struct par_master par_master_dummy = { + .chip_readb = dummy_chip_readb, + .chip_readw = dummy_chip_readw, + .chip_readl = dummy_chip_readl, + .chip_readn = dummy_chip_readn, + .chip_writeb = dummy_chip_writeb, + .chip_writew = dummy_chip_writew, + .chip_writel = dummy_chip_writel, + .chip_writen = dummy_chip_writen, +}; + +static int dummy_shutdown(void *data) +{ + msg_pspew("%s\n", __func__); +#if EMULATE_CHIP + struct emu_data *emu_data = (struct emu_data *)data; + if (emu_data->emu_chip != EMULATE_NONE) { + if (emu_data->emu_persistent_image && emu_data->emu_modified) { + msg_pdbg("Writing %s\n", emu_data->emu_persistent_image); + write_buf_to_file(flashchip_contents, + emu_data->emu_chip_size, + emu_data->emu_persistent_image); + free(emu_data->emu_persistent_image); + emu_data->emu_persistent_image = NULL; + } + free(flashchip_contents); + } +#endif + return 0; +} + +int dummy_init(void) +{ + char *bustext = NULL; + char *tmp = NULL; + unsigned int i; +#if EMULATE_SPI_CHIP + char *status = NULL; + int size = -1; /* size for VARIABLE_SIZE chip device */ +#endif +#if EMULATE_CHIP + struct stat image_stat; +#endif + + struct emu_data *data = calloc(1, sizeof(struct emu_data)); + if (!data) { + msg_perr("Out of memory!\n"); + return 1; + } + data->emu_chip = EMULATE_NONE; + data->delay_us = 0; + spi_master_dummyflasher.data = data; + par_master_dummy.data = data; + + msg_pspew("%s\n", __func__); + + bustext = extract_programmer_param("bus"); + msg_pdbg("Requested buses are: %s\n", bustext ? bustext : "default"); + if (!bustext) + bustext = strdup("parallel+lpc+fwh+spi"); + /* Convert the parameters to lowercase. */ + tolower_string(bustext); + + dummy_buses_supported = BUS_NONE; + if (strstr(bustext, "parallel")) { + dummy_buses_supported |= BUS_PARALLEL; + msg_pdbg("Enabling support for %s flash.\n", "parallel"); + } + if (strstr(bustext, "lpc")) { + dummy_buses_supported |= BUS_LPC; + msg_pdbg("Enabling support for %s flash.\n", "LPC"); + } + if (strstr(bustext, "fwh")) { + dummy_buses_supported |= BUS_FWH; + msg_pdbg("Enabling support for %s flash.\n", "FWH"); + } + if (strstr(bustext, "spi")) { + dummy_buses_supported |= BUS_SPI; + msg_pdbg("Enabling support for %s flash.\n", "SPI"); + } + if (dummy_buses_supported == BUS_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; } - memcpy(flashchip_contents + offs, writearr + 4, writecnt - 4); - data->emu_modified = 1; - break; - case JEDEC_BYTE_PROGRAM_4BA: - offs = writearr[1] << 24 | writearr[2] << 16 | writearr[3] << 8 | writearr[4]; - /* Truncate to emu_chip_size. */ - offs %= data->emu_chip_size; - if (writecnt < 6) { - msg_perr("BYTE PROGRAM size too short!\n"); - return 1; + } + + tmp = extract_programmer_param("spi_blacklist"); + if (tmp) { + i = strlen(tmp); + if (!strncmp(tmp, "0x", 2)) { + i -= 2; + memmove(tmp, tmp + 2, i + 1); } - if (writecnt - 5 > data->emu_max_byteprogram_size) { - msg_perr("Max BYTE PROGRAM size exceeded!\n"); + if ((i > 512) || (i % 2)) { + msg_perr("Invalid SPI command blacklist length\n"); + free(tmp); return 1; } - memcpy(flashchip_contents + offs, writearr + 5, writecnt - 5); - data->emu_modified = 1; - break; - case JEDEC_AAI_WORD_PROGRAM: - if (!data->emu_max_aai_size) - break; - if (!(data->emu_status & SPI_SR_AAI)) { - 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; - } - data->emu_status |= SPI_SR_AAI; - aai_offs = writearr[1] << 16 | writearr[2] << 8 | - writearr[3]; - /* Truncate to emu_chip_size. */ - aai_offs %= data->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"); + data->spi_blacklist_size = i / 2; + for (i = 0; i < data->spi_blacklist_size * 2; i++) { + if (!isxdigit((unsigned char)tmp[i])) { + msg_perr("Invalid char \"%c\" in SPI command " + "blacklist\n", tmp[i]); + free(tmp); return 1; } - memcpy(flashchip_contents + aai_offs, writearr + 1, 2); - aai_offs += 2; - } - data->emu_modified = 1; - break; - case JEDEC_WRDI: - if (data->emu_max_aai_size) - data->emu_status &= ~SPI_SR_AAI; - break; - case JEDEC_SE: - if (!data->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 & (data->emu_jedec_se_size - 1)) - msg_pdbg("Unaligned SECTOR ERASE 0x20: 0x%x\n", offs); - offs &= ~(data->emu_jedec_se_size - 1); - memset(flashchip_contents + offs, 0xff, data->emu_jedec_se_size); - data->emu_modified = 1; - break; - case JEDEC_BE_52: - if (!data->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 & (data->emu_jedec_be_52_size - 1)) - msg_pdbg("Unaligned BLOCK ERASE 0x52: 0x%x\n", offs); - offs &= ~(data->emu_jedec_be_52_size - 1); - memset(flashchip_contents + offs, 0xff, data->emu_jedec_be_52_size); - data->emu_modified = 1; - break; - case JEDEC_BE_D8: - if (!data->emu_jedec_be_d8_size) - break; - if (writecnt != JEDEC_BE_D8_OUTSIZE) { - msg_perr("BLOCK ERASE 0xd8 outsize invalid!\n"); - return 1; + for (i = 0; i < data->spi_blacklist_size; i++) { + unsigned int tmp2; + /* SCNx8 is apparently not supported by MSVC (and thus + * MinGW), so work around it with an extra variable + */ + sscanf(tmp + i * 2, "%2x", &tmp2); + data->spi_blacklist[i] = (uint8_t)tmp2; } - if (readcnt != JEDEC_BE_D8_INSIZE) { - msg_perr("BLOCK ERASE 0xd8 insize invalid!\n"); - return 1; + msg_pdbg("SPI blacklist is "); + for (i = 0; i < data->spi_blacklist_size; i++) + msg_pdbg("%02x ", data->spi_blacklist[i]); + msg_pdbg(", size %u\n", data->spi_blacklist_size); + } + free(tmp); + + tmp = extract_programmer_param("spi_ignorelist"); + if (tmp) { + i = strlen(tmp); + if (!strncmp(tmp, "0x", 2)) { + i -= 2; + memmove(tmp, tmp + 2, i + 1); } - offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3]; - if (offs & (data->emu_jedec_be_d8_size - 1)) - msg_pdbg("Unaligned BLOCK ERASE 0xd8: 0x%x\n", offs); - offs &= ~(data->emu_jedec_be_d8_size - 1); - memset(flashchip_contents + offs, 0xff, data->emu_jedec_be_d8_size); - data->emu_modified = 1; - break; - case JEDEC_CE_60: - if (!data->emu_jedec_ce_60_size) - break; - if (writecnt != JEDEC_CE_60_OUTSIZE) { - msg_perr("CHIP ERASE 0x60 outsize invalid!\n"); + if ((i > 512) || (i % 2)) { + msg_perr("Invalid SPI command ignorelist length\n"); + free(tmp); return 1; } - if (readcnt != JEDEC_CE_60_INSIZE) { - msg_perr("CHIP ERASE 0x60 insize invalid!\n"); - return 1; + data->spi_ignorelist_size = i / 2; + for (i = 0; i < data->spi_ignorelist_size * 2; i++) { + if (!isxdigit((unsigned char)tmp[i])) { + msg_perr("Invalid char \"%c\" in SPI command " + "ignorelist\n", tmp[i]); + free(tmp); + return 1; + } } - /* JEDEC_CE_60_OUTSIZE is 1 (no address) -> no offset. */ - /* emu_jedec_ce_60_size is emu_chip_size. */ - memset(flashchip_contents, 0xff, data->emu_jedec_ce_60_size); - data->emu_modified = 1; - break; - case JEDEC_CE_C7: - if (!data->emu_jedec_ce_c7_size) - break; - if (writecnt != JEDEC_CE_C7_OUTSIZE) { - msg_perr("CHIP ERASE 0xc7 outsize invalid!\n"); - return 1; + for (i = 0; i < data->spi_ignorelist_size; i++) { + unsigned int tmp2; + /* SCNx8 is apparently not supported by MSVC (and thus + * MinGW), so work around it with an extra variable + */ + sscanf(tmp + i * 2, "%2x", &tmp2); + data->spi_ignorelist[i] = (uint8_t)tmp2; } - if (readcnt != JEDEC_CE_C7_INSIZE) { - msg_perr("CHIP ERASE 0xc7 insize invalid!\n"); + msg_pdbg("SPI ignorelist is "); + for (i = 0; i < data->spi_ignorelist_size; i++) + msg_pdbg("%02x ", data->spi_ignorelist[i]); + msg_pdbg(", size %u\n", data->spi_ignorelist_size); + } + free(tmp); + + /* frequency to emulate in Hz (default), KHz, or MHz */ + tmp = extract_programmer_param("freq"); + if (tmp) { + unsigned long int freq; + char *units = tmp; + char *end = tmp + strlen(tmp); + + errno = 0; + freq = strtoul(tmp, &units, 0); + if (errno) { + msg_perr("Invalid frequency \"%s\", %s\n", + tmp, strerror(errno)); + free(tmp); return 1; } - /* JEDEC_CE_C7_OUTSIZE is 1 (no address) -> no offset. */ - /* emu_jedec_ce_c7_size is emu_chip_size. */ - memset(flashchip_contents, 0xff, data->emu_jedec_ce_c7_size); - data->emu_modified = 1; - break; - case JEDEC_SFDP: - if (data->emu_chip != EMULATE_MACRONIX_MX25L6436) - break; - if (writecnt < 4) - break; - offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3]; - /* SFDP expects one dummy byte after the address. */ - if (writecnt == 4) { - /* The dummy byte was not written, make sure it is read instead. - * Shifting and shortening the read array does achieve this goal. - */ - readarr++; - readcnt--; - } else { - /* The response is shifted if more than 5 bytes are written, because SFDP data is - * already shifted out by the chip while those superfluous bytes are written. */ - offs += writecnt - 5; + if ((units > tmp) && (units < end)) { + int units_valid = 0; + + if (units < end - 3) { + ; + } else if (units == end - 2) { + if (!strcasecmp(units, "hz")) + units_valid = 1; + } else if (units == end - 3) { + if (!strcasecmp(units, "khz")) { + freq *= 1000; + units_valid = 1; + } else if (!strcasecmp(units, "mhz")) { + freq *= 1000000; + units_valid = 1; + } + } + + if (!units_valid) { + msg_perr("Invalid units: %s\n", units); + free(tmp); + return 1; + } } - /* The SFDP spec implies that the start address of an SFDP read may be truncated to fit in the - * SFDP table address space, i.e. the start address may be wrapped around at SFDP table size. - * This is a reasonable implementation choice in hardware because it saves a few gates. */ - if (offs >= sizeof(sfdp_table)) { - msg_pdbg("Wrapping the start address around the SFDP table boundary (using 0x%x " - "instead of 0x%x).\n", (unsigned int)(offs % sizeof(sfdp_table)), offs); - offs %= sizeof(sfdp_table); + /* Assume we only work with bytes and transfer at 1 bit/Hz */ + data->delay_us = (1000000 * 8) / freq; + } + free(tmp); + +#if EMULATE_CHIP +#if EMULATE_SPI_CHIP + tmp = extract_programmer_param("size"); + if (tmp) { + size = strtol(tmp, NULL, 10); + if (size <= 0 || (size % 1024 != 0)) { + msg_perr("%s: Chip size is not a multipler of 1024: %s\n", + __func__, tmp); + free(tmp); + return 1; } - toread = min(sizeof(sfdp_table) - offs, readcnt); - memcpy(readarr, sfdp_table + offs, toread); - if (toread < readcnt) - msg_pdbg("Crossing the SFDP table boundary in a single " - "continuous chunk produces undefined results " - "after that point.\n"); - break; - default: - /* No special response. */ - break; + free(tmp); } - if (writearr[0] != JEDEC_WREN && writearr[0] != JEDEC_EWSR) - data->emu_status &= ~SPI_SR_WEL; - return 0; -} #endif -static struct emu_data* get_data_from_context(const struct flashctx *flash) -{ - if (dummy_buses_supported & (BUS_PARALLEL | BUS_LPC | BUS_FWH)) - return (struct emu_data *)flash->mst->par.data; - else if (dummy_buses_supported & BUS_SPI) - return (struct emu_data *)flash->mst->spi.data; - - return NULL; /* buses was set to BUS_NONE. */ -} + tmp = extract_programmer_param("emulate"); + if (!tmp) { + msg_pdbg("Not emulating any flash chip.\n"); + /* Nothing else to do. */ + goto dummy_init_out; + } +#if EMULATE_SPI_CHIP + if (!strcmp(tmp, "M25P10.RES")) { + data->emu_chip = EMULATE_ST_M25P10_RES; + data->emu_chip_size = 128 * 1024; + data->emu_max_byteprogram_size = 128; + data->emu_max_aai_size = 0; + data->emu_jedec_se_size = 0; + data->emu_jedec_be_52_size = 0; + data->emu_jedec_be_d8_size = 32 * 1024; + data->emu_jedec_ce_60_size = 0; + data->emu_jedec_ce_c7_size = data->emu_chip_size; + msg_pdbg("Emulating ST M25P10.RES SPI flash chip (RES, page " + "write)\n"); + } + if (!strcmp(tmp, "SST25VF040.REMS")) { + data->emu_chip = EMULATE_SST_SST25VF040_REMS; + data->emu_chip_size = 512 * 1024; + data->emu_max_byteprogram_size = 1; + data->emu_max_aai_size = 0; + data->emu_jedec_se_size = 4 * 1024; + data->emu_jedec_be_52_size = 32 * 1024; + data->emu_jedec_be_d8_size = 0; + data->emu_jedec_ce_60_size = data->emu_chip_size; + data->emu_jedec_ce_c7_size = 0; + msg_pdbg("Emulating SST SST25VF040.REMS SPI flash chip (REMS, " + "byte write)\n"); + } + if (!strcmp(tmp, "SST25VF032B")) { + data->emu_chip = EMULATE_SST_SST25VF032B; + data->emu_chip_size = 4 * 1024 * 1024; + data->emu_max_byteprogram_size = 1; + data->emu_max_aai_size = 2; + data->emu_jedec_se_size = 4 * 1024; + data->emu_jedec_be_52_size = 32 * 1024; + data->emu_jedec_be_d8_size = 64 * 1024; + data->emu_jedec_ce_60_size = data->emu_chip_size; + data->emu_jedec_ce_c7_size = data->emu_chip_size; + msg_pdbg("Emulating SST SST25VF032B SPI flash chip (RDID, AAI " + "write)\n"); + } + if (!strcmp(tmp, "MX25L6436")) { + data->emu_chip = EMULATE_MACRONIX_MX25L6436; + data->emu_chip_size = 8 * 1024 * 1024; + data->emu_max_byteprogram_size = 256; + data->emu_max_aai_size = 0; + data->emu_jedec_se_size = 4 * 1024; + data->emu_jedec_be_52_size = 32 * 1024; + data->emu_jedec_be_d8_size = 64 * 1024; + data->emu_jedec_ce_60_size = data->emu_chip_size; + data->emu_jedec_ce_c7_size = data->emu_chip_size; + msg_pdbg("Emulating Macronix MX25L6436 SPI flash chip (RDID, " + "SFDP)\n"); + } + if (!strcmp(tmp, "W25Q128FV")) { + data->emu_chip = EMULATE_WINBOND_W25Q128FV; + data->emu_chip_size = 16 * 1024 * 1024; + data->emu_max_byteprogram_size = 256; + data->emu_max_aai_size = 0; + data->emu_jedec_se_size = 4 * 1024; + data->emu_jedec_be_52_size = 32 * 1024; + data->emu_jedec_be_d8_size = 64 * 1024; + data->emu_jedec_ce_60_size = data->emu_chip_size; + data->emu_jedec_ce_c7_size = data->emu_chip_size; + msg_pdbg("Emulating Winbond W25Q128FV SPI flash chip (RDID)\n"); + } -static int dummy_spi_send_command(const struct flashctx *flash, unsigned int writecnt, - unsigned int readcnt, - const unsigned char *writearr, - unsigned char *readarr) -{ - unsigned int i; - struct emu_data *emu_data = get_data_from_context(flash); - if (!emu_data) { - msg_perr("No data in flash context!\n"); + /* The name of variable-size virtual chip. A 4 MiB flash example: + * flashrom -p dummy:emulate=VARIABLE_SIZE,size=4194304 + */ + if (!strcmp(tmp, "VARIABLE_SIZE")) { + if (size == -1) { + msg_perr("%s: the size parameter is not given.\n", __func__); + free(tmp); + return 1; + } + data->emu_chip = EMULATE_VARIABLE_SIZE; + data->emu_chip_size = size; + data->emu_max_byteprogram_size = 256; + data->emu_max_aai_size = 0; + data->emu_jedec_se_size = 4 * 1024; + data->emu_jedec_be_52_size = 32 * 1024; + data->emu_jedec_be_d8_size = 64 * 1024; + data->emu_jedec_ce_60_size = data->emu_chip_size; + data->emu_jedec_ce_c7_size = data->emu_chip_size; + msg_pdbg("Emulating generic SPI flash chip (size=%d bytes)\n", + data->emu_chip_size); + } +#endif + if (data->emu_chip == EMULATE_NONE) { + msg_perr("Invalid chip specified for emulation: %s\n", tmp); + free(tmp); return 1; } + free(tmp); - msg_pspew("%s:", __func__); + /* Should emulated flash erase to zero (yes/no)? */ + tmp = extract_programmer_param("erase_to_zero"); + if (tmp) { + if (!strcmp(tmp, "yes")) { + msg_pdbg("Emulated chip will erase to 0x00\n"); + data->erase_to_zero = 1; + } else if (!strcmp(tmp, "no")) { + msg_pdbg("Emulated chip will erase to 0xff\n"); + } else { + msg_perr("erase_to_zero can be \"yes\" or \"no\"\n"); + free(tmp); + return 1; + } + } + free(tmp); - msg_pspew(" writing %u bytes:", writecnt); - for (i = 0; i < writecnt; i++) - msg_pspew(" 0x%02x", writearr[i]); + flashchip_contents = malloc(data->emu_chip_size); + if (!flashchip_contents) { + msg_perr("Out of memory!\n"); + return 1; + } - /* Response for unknown commands and missing chip is 0xff. */ - memset(readarr, 0xff, readcnt); -#if EMULATE_SPI_CHIP - switch (emu_data->emu_chip) { - case EMULATE_ST_M25P10_RES: - case EMULATE_SST_SST25VF040_REMS: - case EMULATE_SST_SST25VF032B: - case EMULATE_MACRONIX_MX25L6436: - case EMULATE_WINBOND_W25Q128FV: - case EMULATE_VARIABLE_SIZE: - if (emulate_spi_chip_response(writecnt, readcnt, writearr, - readarr, emu_data)) { - msg_pdbg("Invalid command sent to flash chip!\n"); +#ifdef EMULATE_SPI_CHIP + status = extract_programmer_param("spi_status"); + if (status) { + char *endptr; + errno = 0; + data->emu_status = strtoul(status, &endptr, 0); + free(status); + if (errno != 0 || status == endptr) { + msg_perr("Error: initial status register specified, " + "but the value could not be converted.\n"); return 1; } - break; - default: - break; + msg_pdbg("Initial status register is set to 0x%02x.\n", + data->emu_status); } #endif - msg_pspew(" reading %u bytes:", readcnt); - for (i = 0; i < readcnt; i++) - msg_pspew(" 0x%02x", readarr[i]); - msg_pspew("\n"); - programmer_delay((writecnt + readcnt) * emu_data->delay_us); - return 0; -} + msg_pdbg("Filling fake flash chip with 0x%02x, size %i\n", + data->erase_to_zero ? 0x00 : 0xff, data->emu_chip_size); + memset(flashchip_contents, data->erase_to_zero ? 0x00 : 0xff, data->emu_chip_size); -static int dummy_spi_write_256(struct flashctx *flash, const uint8_t *buf, unsigned int start, unsigned int len) -{ - return spi_write_chunked(flash, buf, start, len, - spi_write_256_chunksize); + /* Will be freed by shutdown function if necessary. */ + data->emu_persistent_image = extract_programmer_param("image"); + if (!data->emu_persistent_image) { + /* Nothing else to do. */ + goto dummy_init_out; + } + /* We will silently (in default verbosity) ignore the file if it does not exist (yet) or the size does + * not match the emulated chip. */ + if (!stat(data->emu_persistent_image, &image_stat)) { + msg_pdbg("Found persistent image %s, %jd B ", + data->emu_persistent_image, (intmax_t)image_stat.st_size); + if ((uintmax_t)image_stat.st_size == data->emu_chip_size) { + msg_pdbg("matches.\n"); + msg_pdbg("Reading %s\n", data->emu_persistent_image); + if (read_buf_from_file(flashchip_contents, data->emu_chip_size, + data->emu_persistent_image)) { + msg_perr("Unable to read %s\n", data->emu_persistent_image); + free(flashchip_contents); + return 1; + } + } else { + msg_pdbg("doesn't match.\n"); + } + } +#endif + +dummy_init_out: + if (register_shutdown(dummy_shutdown, data)) { + free(flashchip_contents); + free(data); + return 1; + } + if (dummy_buses_supported & (BUS_PARALLEL | BUS_LPC | BUS_FWH)) + register_par_master(&par_master_dummy, + dummy_buses_supported & (BUS_PARALLEL | BUS_LPC | BUS_FWH)); + if (dummy_buses_supported & BUS_SPI) + register_spi_master(&spi_master_dummyflasher); + + return 0; } #if EMULATE_CHIP && EMULATE_SPI_CHIP -- cgit v1.2.3