Hello,
In addition to new dummy chip definitions, this squashes a few bugs left over in CLI (--read-otp, --write-otp and
--lock-otp) and in otp.c (Eon specific). These 3 chips should completely emulate all kinds of new functionality added.
Thanks
Hatim
P.S. Sorry I had to send the email in this form, there seems to be a problem with my mail configuration which I was
sadly unable to resolve.
--
GD25Q128C -
- 3 status registers each with separate read and write opcodes for corresponding SRs
- 3 security registers each of 512 bytes
EN25QH128 -
- Single status register
- OTP sector of 512 bytes
W25Q40.V (W25Q40BL) -
- 2 status registers each with separate read but with single write opcode
- 3 security registers each of 256 bytes
Signed-off-by: Hatim Kanchwala <hatim(a)hatimak.me>
---
cli_classic.c | 10 +-
dummyflasher.c | 618 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
otp.c | 4 +-
3 files changed, 615 insertions(+), 17 deletions(-)
diff --git a/cli_classic.c b/cli_classic.c
index 146750e..300560c 100644
--- a/cli_classic.c
+++ b/cli_classic.c
@@ -459,36 +459,38 @@ int main(int argc, char *argv[])
"specified. Aborting.\n");
cli_classic_abort_usage();
}
write_otp_opt = strdup(optarg);
filename = extract_param(&write_otp_opt, "file", ",");
write_otp = 1;
break;
case LONGOPT_ERASE_OTP:
if (++operation_specified > 1) {
fprintf(stderr, "More than one operation "
"specified. Aborting.\n");
cli_classic_abort_usage();
}
- erase_otp_opt = strdup(optarg);
+ if (optarg)
+ erase_otp_opt = strdup(optarg);
erase_otp = 1;
break;
case LONGOPT_LOCK_OTP:
if (++operation_specified > 1) {
fprintf(stderr, "More than one operation "
"specified. Aborting.\n");
cli_classic_abort_usage();
}
- lock_otp_opt = strdup(optarg);
+ if (optarg)
+ lock_otp_opt = strdup(optarg);
lock_otp = 1;
break;
default:
cli_classic_abort_usage();
break;
}
}
if (optind < argc) {
fprintf(stderr, "Error: Extra parameter found.\n");
cli_classic_abort_usage();
}
@@ -909,52 +911,52 @@ int main(int argc, char *argv[])
msg_gdbg("Writing OTP memory done\n");
// FIXME(hatim): Verify written contents
} else
cli_infra_support(fill_flash, "OTP");
goto out_shutdown;
}
if (erase_otp) {
verbose_screen++;
if (fill_flash->chip->otp) {
char *otp_region_opt = NULL;
enum otp_region otp_region = OTP_REG_1;
- if (!erase_otp_opt && (otp_region_opt = extract_param(&read_otp_opt, "reg", ","))) {
+ if (!erase_otp_opt && (otp_region_opt = extract_param(&erase_otp_opt, "reg", ","))) {
char *endptr = NULL;
// FIXME(hatim): Implement error-checking (?)
otp_region = (uint8_t)strtoul(otp_region_opt, &endptr, 0) - 1;
} else
msg_gdbg("OTP region not specified, using default region 1\n");
msg_gdbg("Erasing OTP memory ...\n");
ret = fill_flash->chip->otp->erase(fill_flash, otp_region);
if (ret) {
msg_gerr("Erasing OTP memory failed\n");
goto out_shutdown;
}
msg_gdbg("Erasing OTP memory done\n");
} else
cli_infra_support(fill_flash, "OTP");
goto out_shutdown;
}
if (lock_otp) {
verbose_screen++;
if (fill_flash->chip->otp) {
char *otp_region_opt = NULL;
enum otp_region otp_region = OTP_REG_1;
- if (!lock_otp_opt && (otp_region_opt = extract_param(&read_otp_opt, "reg", ","))) {
+ if (!lock_otp_opt && (otp_region_opt = extract_param(&lock_otp_opt, "reg", ","))) {
char *endptr = NULL;
// FIXME(hatim): Implement error-checking (?)
otp_region = (uint8_t)strtoul(otp_region_opt, &endptr, 0) - 1;
} else
msg_gdbg("OTP region not specified, using default region 1\n");
msg_gdbg("Trying to lock OTP memory...\n");
ret = fill_flash->chip->otp->lock(fill_flash, otp_region);
if (ret) {
msg_gerr("Failed to lock\n");
goto out_shutdown;
}
msg_gdbg("OTP memory locked\n");
diff --git a/dummyflasher.c b/dummyflasher.c
index f171128..20f6ba5 100644
--- a/dummyflasher.c
+++ b/dummyflasher.c
@@ -1,17 +1,18 @@
/*
* This file is part of the flashrom project.
*
* Copyright (C) 2009,2010 Carl-Daniel Hailfinger
+ * Copyright (C) 2016 Hatim Kanchwala <hatim(a)hatimak.me>
*
* 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; 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
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
@@ -31,49 +32,68 @@
#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;
+static uint8_t *gd25q128c_otp_1_contents = NULL;
+static uint8_t *gd25q128c_otp_2_contents = NULL;
+static uint8_t *gd25q128c_otp_3_contents = NULL;
+static uint8_t *en25qh128_otp_contents = NULL;
+static uint8_t *w25q40v_otp_1_contents = NULL;
+static uint8_t *w25q40v_otp_2_contents = NULL;
+static uint8_t *w25q40v_otp_3_contents = NULL;
enum emu_chip {
EMULATE_NONE,
EMULATE_ST_M25P10_RES,
EMULATE_SST_SST25VF040_REMS,
EMULATE_SST_SST25VF032B,
EMULATE_MACRONIX_MX25L6436,
+ EMULATE_GIGADEVICE_GD25Q128C,
+ EMULATE_EON_EN25QH128,
+ EMULATE_WINBOND_W25Q40_V,
};
static enum emu_chip emu_chip = EMULATE_NONE;
static char *emu_persistent_image = NULL;
static unsigned int emu_chip_size = 0;
#if EMULATE_SPI_CHIP
static unsigned int emu_max_byteprogram_size = 0;
static unsigned int emu_max_aai_size = 0;
static unsigned int emu_jedec_se_size = 0;
static unsigned int emu_jedec_be_52_size = 0;
static unsigned int emu_jedec_be_d8_size = 0;
static unsigned int emu_jedec_ce_60_size = 0;
static unsigned int emu_jedec_ce_c7_size = 0;
unsigned char spi_blacklist[256];
unsigned char spi_ignorelist[256];
int spi_blacklist_size = 0;
int spi_ignorelist_size = 0;
-static uint8_t emu_status = 0;
+static uint32_t emu_status = 0;
+static char *emu_persistent_gd25_otp_1_image = NULL;
+static char *emu_persistent_gd25_otp_2_image = NULL;
+static char *emu_persistent_gd25_otp_3_image = NULL;
+static char *emu_persistent_en25_otp_image = NULL;
+static char *emu_persistent_w25_otp_1_image = NULL;
+static char *emu_persistent_w25_otp_2_image = NULL;
+static char *emu_persistent_w25_otp_3_image = NULL;
+static uint8_t read_only_bits = 0;
+static uint8_t en25qh128_otp_mode = 0, en25qh128_otp_bit = 0;
/* A legit complete SFDP table based on the MX25L6436E (rev. 1.8) datasheet. */
static const uint8_t sfdp_table[] = {
0x53, 0x46, 0x44, 0x50, // @0x00: SFDP signature
0x00, 0x01, 0x01, 0xFF, // @0x04: revision 1.0, 2 headers
0x00, 0x00, 0x01, 0x09, // @0x08: JEDEC SFDP header rev. 1.0, 9 DW long
0x1C, 0x00, 0x00, 0xFF, // @0x0C: PTP0 = 0x1C (instead of 0x30)
0xC2, 0x00, 0x01, 0x04, // @0x10: Macronix header rev. 1.0, 4 DW long
0x48, 0x00, 0x00, 0xFF, // @0x14: PTP1 = 0x48 (instead of 0x60)
0xFF, 0xFF, 0xFF, 0xFF, // @0x18: hole.
0xE5, 0x20, 0xC9, 0xFF, // @0x1C: SFDP parameter table start
0xFF, 0xFF, 0xFF, 0x03, // @0x20
0x00, 0xFF, 0x08, 0x6B, // @0x24
@@ -81,26 +101,28 @@ static const uint8_t sfdp_table[] = {
0xEE, 0xFF, 0xFF, 0xFF, // @0x2C
0xFF, 0xFF, 0x00, 0x00, // @0x30
0xFF, 0xFF, 0x00, 0xFF, // @0x34
0x0C, 0x20, 0x0F, 0x52, // @0x38
0x10, 0xD8, 0x00, 0xFF, // @0x3C: SFDP parameter table end
0xFF, 0xFF, 0xFF, 0xFF, // @0x40: hole.
0xFF, 0xFF, 0xFF, 0xFF, // @0x44: hole.
0x00, 0x36, 0x00, 0x27, // @0x48: Macronix parameter table start
0xF4, 0x4F, 0xFF, 0xFF, // @0x4C
0xD9, 0xC8, 0xFF, 0xFF, // @0x50
0xFF, 0xFF, 0xFF, 0xFF, // @0x54: Macronix parameter table end
};
+// TODO(hatim): Add SFDP table for GD25Q128C, EN25QH128 and W25Q40.V
+
#endif
#endif
static unsigned int spi_write_256_chunksize = 256;
static int dummy_spi_send_command(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);
@@ -135,26 +157,81 @@ enum chipbustype dummy_buses_supported = BUS_NONE;
static int dummy_shutdown(void *data)
{
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(emu_persistent_image);
emu_persistent_image = NULL;
}
free(flashchip_contents);
+ if (emu_chip == EMULATE_GIGADEVICE_GD25Q128C) {
+ if (emu_persistent_gd25_otp_1_image) {
+ msg_pdbg("Writing %s\n", emu_persistent_gd25_otp_1_image);
+ write_buf_to_file(gd25q128c_otp_1_contents, 512, emu_persistent_gd25_otp_1_image);
+ free(emu_persistent_gd25_otp_1_image);
+ emu_persistent_gd25_otp_1_image = NULL;
+ }
+ if (emu_persistent_gd25_otp_2_image) {
+ msg_pdbg("Writing %s\n", emu_persistent_gd25_otp_2_image);
+ write_buf_to_file(gd25q128c_otp_2_contents, 512, emu_persistent_gd25_otp_2_image);
+ free(emu_persistent_gd25_otp_2_image);
+ emu_persistent_gd25_otp_2_image = NULL;
+ }
+ if (emu_persistent_gd25_otp_3_image) {
+ msg_pdbg("Writing %s\n", emu_persistent_gd25_otp_3_image);
+ write_buf_to_file(gd25q128c_otp_3_contents, 512, emu_persistent_gd25_otp_3_image);
+ free(emu_persistent_gd25_otp_3_image);
+ emu_persistent_gd25_otp_3_image = NULL;
+ }
+ free(gd25q128c_otp_1_contents);
+ free(gd25q128c_otp_2_contents);
+ free(gd25q128c_otp_3_contents);
+ }
+ if (emu_chip == EMULATE_EON_EN25QH128) {
+ if (emu_persistent_en25_otp_image) {
+ msg_pdbg("Writing %s\n", emu_persistent_en25_otp_image);
+ write_buf_to_file(en25qh128_otp_contents, 512, emu_persistent_en25_otp_image);
+ free(emu_persistent_en25_otp_image);
+ emu_persistent_en25_otp_image = NULL;
+ }
+ free(en25qh128_otp_contents);
+ }
+ if (emu_chip == EMULATE_WINBOND_W25Q40_V) {
+ if (emu_persistent_w25_otp_1_image) {
+ msg_pdbg("Writing %s\n", emu_persistent_w25_otp_1_image);
+ write_buf_to_file(w25q40v_otp_1_contents, 256, emu_persistent_w25_otp_1_image);
+ free(emu_persistent_w25_otp_1_image);
+ emu_persistent_w25_otp_1_image = NULL;
+ }
+ if (emu_persistent_w25_otp_2_image) {
+ msg_pdbg("Writing %s\n", emu_persistent_w25_otp_2_image);
+ write_buf_to_file(w25q40v_otp_2_contents, 256, emu_persistent_w25_otp_2_image);
+ free(emu_persistent_w25_otp_2_image);
+ emu_persistent_w25_otp_2_image = NULL;
+ }
+ if (emu_persistent_w25_otp_3_image) {
+ msg_pdbg("Writing %s\n", emu_persistent_w25_otp_3_image);
+ write_buf_to_file(w25q40v_otp_3_contents, 256, emu_persistent_w25_otp_3_image);
+ free(emu_persistent_w25_otp_3_image);
+ emu_persistent_w25_otp_3_image = NULL;
+ }
+ free(w25q40v_otp_1_contents);
+ free(w25q40v_otp_2_contents);
+ free(w25q40v_otp_3_contents);
+ }
}
#endif
return 0;
}
int dummy_init(void)
{
char *bustext = NULL;
char *tmp = NULL;
int i;
#if EMULATE_SPI_CHIP
char *status = NULL;
#endif
@@ -324,84 +401,311 @@ int dummy_init(void)
if (!strcmp(tmp, "MX25L6436")) {
emu_chip = EMULATE_MACRONIX_MX25L6436;
emu_chip_size = 8 * 1024 * 1024;
emu_max_byteprogram_size = 256;
emu_max_aai_size = 0;
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 Macronix MX25L6436 SPI flash chip (RDID, "
"SFDP)\n");
}
+ if (!strcmp(tmp, "GD25Q128C")) {
+ emu_chip = EMULATE_GIGADEVICE_GD25Q128C;
+ emu_chip_size = 16 * 1024 * 1024;
+ emu_max_byteprogram_size = 256;
+ emu_max_aai_size = 0;
+ 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 GigaDevice GD25Q128C SPI flash chip (RDID)\n");
+ }
+ if (!strcmp(tmp, "EN25QH128")) {
+ emu_chip = EMULATE_EON_EN25QH128;
+ emu_chip_size = 16 * 1024 * 1024;
+ emu_max_byteprogram_size = 256;
+ emu_max_aai_size = 0;
+ 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 Eon EN25QH128 SPI flash chip (RDID)\n");
+ }
+ if (!strcmp(tmp, "W25Q40.V")) {
+ emu_chip = EMULATE_WINBOND_W25Q40_V;
+ emu_chip_size = 512 * 1024;
+ emu_max_byteprogram_size = 256;
+ emu_max_aai_size = 0;
+ 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 Winbond W25Q40.V SPI flash chip (RDID)\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;
}
+ /* Allocate memory for each of the 512 bytes Security Register of GD25Q128C. */
+ if (emu_chip == EMULATE_GIGADEVICE_GD25Q128C) {
+ gd25q128c_otp_1_contents = malloc(512);
+ if (!gd25q128c_otp_1_contents) {
+ msg_perr("Out of memory!\n");
+ return 1;
+ }
+ gd25q128c_otp_2_contents = malloc(512);
+ if (!gd25q128c_otp_2_contents) {
+ msg_perr("Out of memory!\n");
+ return 1;
+ }
+ gd25q128c_otp_3_contents = malloc(512);
+ if (!gd25q128c_otp_3_contents) {
+ msg_perr("Out of memory!\n");
+ return 1;
+ }
+ }
+
+ /* Allocate memory for the 512 byte security sector of EN25QH128. */
+ if (emu_chip == EMULATE_EON_EN25QH128) {
+ en25qh128_otp_contents = malloc(512);
+ if (!en25qh128_otp_contents) {
+ msg_perr("Out of memory!\n");
+ return 1;
+ }
+ }
+
+ /* Allocate memory for each of the 256 bytes Security Register of GD25Q128C. */
+ if (emu_chip == EMULATE_WINBOND_W25Q40_V) {
+ w25q40v_otp_1_contents = malloc(256);
+ if (!w25q40v_otp_1_contents) {
+ msg_perr("Out of memory!\n");
+ return 1;
+ }
+ w25q40v_otp_2_contents = malloc(256);
+ if (!w25q40v_otp_2_contents) {
+ msg_perr("Out of memory!\n");
+ return 1;
+ }
+ w25q40v_otp_3_contents = malloc(256);
+ if (!w25q40v_otp_3_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;
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",
+ if (emu_chip == EMULATE_GIGADEVICE_GD25Q128C) {
+ msg_pdbg("Initial status registers -\n"
+ "\tSR1 is set to 0x%02x\n"
+ "\tSR2 is set to 0x%02x\n"
+ "\tSR3 is set to 0x%02x\n",
+ emu_status & 0xff, (emu_status >> 8) & 0xff, (emu_status >> 16) & 0xff);
+ } else if (emu_chip == EMULATE_WINBOND_W25Q40_V) {
+ msg_pdbg("Initial status registers -\n"
+ "\tSR1 is set to 0x%02x\n"
+ "\tSR2 is set to 0x%02x\n",
+ emu_status & 0xff, (emu_status >> 8) & 0xff);
+ } else {
+ msg_pdbg("Initial status register is set to 0x%02x.\n",
emu_status);
+ }
}
#endif
msg_pdbg("Filling fake flash chip with 0xff, size %i\n", emu_chip_size);
memset(flashchip_contents, 0xff, emu_chip_size);
/* Will be freed by shutdown function if necessary. */
emu_persistent_image = extract_programmer_param("image");
- if (!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(emu_persistent_image, &image_stat)) {
+ if (emu_persistent_image && !stat(emu_persistent_image, &image_stat)) {
msg_pdbg("Found persistent image %s, %jd B ",
emu_persistent_image, (intmax_t)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");
}
}
+
+ /* Each OTP image will be freed by shutdown function, if necessary. GD25Q128C has 3 Security Registers
+ * each of 512 bytes.
+ * We will silently (in default verbosity) ignore the file(s) if it does not exist (yet)
+ * or the size does not match the security register size on chip. */
+ if (emu_chip == EMULATE_GIGADEVICE_GD25Q128C) {
+ msg_pdbg("Filling fake security register 1, 2 and 3 with 0xff, size 512 bytes each\n");
+ memset(gd25q128c_otp_1_contents, 0xff, 512);
+ memset(gd25q128c_otp_2_contents, 0xff, 512);
+ memset(gd25q128c_otp_3_contents, 0xff, 512);
+
+ emu_persistent_gd25_otp_1_image = extract_programmer_param("otp_1");
+ emu_persistent_gd25_otp_2_image = extract_programmer_param("otp_2");
+ emu_persistent_gd25_otp_3_image = extract_programmer_param("otp_3");
+
+ if (emu_persistent_gd25_otp_1_image && !stat(emu_persistent_gd25_otp_1_image, &image_stat)) {
+ msg_pdbg("Found persistent image %s, %jd B for security register 1, ",
+ emu_persistent_gd25_otp_1_image, (intmax_t)image_stat.st_size);
+ if (image_stat.st_size == 512) {
+ msg_pdbg("matches.\n");
+ msg_pdbg("Reading %s\n", emu_persistent_gd25_otp_1_image);
+ read_buf_from_file(gd25q128c_otp_1_contents, 512,
+ emu_persistent_gd25_otp_1_image);
+ } else {
+ msg_pdbg("doesn't match.\n");
+ }
+ }
+ if (emu_persistent_gd25_otp_2_image && !stat(emu_persistent_gd25_otp_2_image, &image_stat)) {
+ msg_pdbg("Found persistent image %s, %jd B for security register 2, ",
+ emu_persistent_gd25_otp_2_image, (intmax_t)image_stat.st_size);
+ if (image_stat.st_size == 512) {
+ msg_pdbg("matches.\n");
+ msg_pdbg("Reading %s\n", emu_persistent_gd25_otp_2_image);
+ read_buf_from_file(gd25q128c_otp_2_contents, 512,
+ emu_persistent_gd25_otp_2_image);
+ } else {
+ msg_pdbg("doesn't match.\n");
+ }
+ }
+ if (emu_persistent_gd25_otp_3_image && !stat(emu_persistent_gd25_otp_3_image, &image_stat)) {
+ msg_pdbg("Found persistent image %s, %jd B for security register 3, ",
+ emu_persistent_gd25_otp_3_image, (intmax_t)image_stat.st_size);
+ if (image_stat.st_size == 512) {
+ msg_pdbg("matches.\n");
+ msg_pdbg("Reading %s\n", emu_persistent_gd25_otp_3_image);
+ read_buf_from_file(gd25q128c_otp_3_contents, 512,
+ emu_persistent_gd25_otp_3_image);
+ } else {
+ msg_pdbg("doesn't match.\n");
+ }
+ }
+ }
+
+ /* Eon EN25QH128 has a security sector of 512 bytes. OTP image will be freed by shutdown
+ * function, if necessary. We will silently (in default verbosity) ignore the file(s)
+ * if it does not exist (yet) or the size does not match the security register size on chip. */
+ if (emu_chip == EMULATE_EON_EN25QH128) {
+ msg_pdbg("Filling fake security sector with 0xff, size 512 bytes\n");
+ memset(en25qh128_otp_contents, 0xff, 512);
+ emu_persistent_en25_otp_image = extract_programmer_param("otp");
+ if (emu_persistent_en25_otp_image && !stat(emu_persistent_en25_otp_image, &image_stat)) {
+ msg_pdbg("Found persistent image %s, %jd B for security sector, ",
+ emu_persistent_en25_otp_image, (intmax_t)image_stat.st_size);
+ if (image_stat.st_size == 512) {
+ msg_pdbg("matches.\n");
+ msg_pdbg("Reading %s\n", emu_persistent_en25_otp_image);
+ read_buf_from_file(en25qh128_otp_contents, 512,
+ emu_persistent_en25_otp_image);
+ } else {
+ msg_pdbg("doesn't match.\n");
+ }
+ }
+ }
+
+ /* W25Q40.V has 3 Security Registers each of 256 bytes. */
+ if (emu_chip == EMULATE_WINBOND_W25Q40_V) {
+ msg_pdbg("Filling fake security register 1, 2 and 3 with 0xff, size 256 bytes each\n");
+ memset(w25q40v_otp_1_contents, 0xff, 256);
+ memset(w25q40v_otp_2_contents, 0xff, 256);
+ memset(w25q40v_otp_3_contents, 0xff, 256);
+
+ emu_persistent_w25_otp_1_image = extract_programmer_param("otp_1");
+ emu_persistent_w25_otp_2_image = extract_programmer_param("otp_2");
+ emu_persistent_w25_otp_3_image = extract_programmer_param("otp_3");
+
+ if (emu_persistent_w25_otp_1_image && !stat(emu_persistent_w25_otp_1_image, &image_stat)) {
+ msg_pdbg("Found persistent image %s, %jd B for security register 1, ",
+ emu_persistent_w25_otp_1_image, (intmax_t)image_stat.st_size);
+ if (image_stat.st_size == 256) {
+ msg_pdbg("matches.\n");
+ msg_pdbg("Reading %s\n", emu_persistent_w25_otp_1_image);
+ read_buf_from_file(w25q40v_otp_1_contents, 256,
+ emu_persistent_w25_otp_1_image);
+ } else {
+ msg_pdbg("doesn't match.\n");
+ }
+ }
+ if (emu_persistent_w25_otp_2_image && !stat(emu_persistent_w25_otp_2_image, &image_stat)) {
+ msg_pdbg("Found persistent image %s, %jd B for security register 2, ",
+ emu_persistent_w25_otp_2_image, (intmax_t)image_stat.st_size);
+ if (image_stat.st_size == 256) {
+ msg_pdbg("matches.\n");
+ msg_pdbg("Reading %s\n", emu_persistent_w25_otp_2_image);
+ read_buf_from_file(w25q40v_otp_2_contents, 256,
+ emu_persistent_w25_otp_2_image);
+ } else {
+ msg_pdbg("doesn't match.\n");
+ }
+ }
+ if (emu_persistent_w25_otp_3_image && !stat(emu_persistent_w25_otp_3_image, &image_stat)) {
+ msg_pdbg("Found persistent image %s, %jd B for security register 3, ",
+ emu_persistent_w25_otp_3_image, (intmax_t)image_stat.st_size);
+ if (image_stat.st_size == 256) {
+ msg_pdbg("matches.\n");
+ msg_pdbg("Reading %s\n", emu_persistent_w25_otp_3_image);
+ read_buf_from_file(w25q40v_otp_3_contents, 256,
+ emu_persistent_w25_otp_3_image);
+ } else {
+ msg_pdbg("doesn't match.\n");
+ }
+ }
+ }
#endif
dummy_init_out:
if (register_shutdown(dummy_shutdown, NULL)) {
free(flashchip_contents);
+ if (emu_chip == EMULATE_GIGADEVICE_GD25Q128C) {
+ free(gd25q128c_otp_1_contents);
+ free(gd25q128c_otp_2_contents);
+ free(gd25q128c_otp_3_contents);
+ }
+ if (emu_chip == EMULATE_EON_EN25QH128) {
+ free(en25qh128_otp_contents);
+ }
+ if (emu_chip == EMULATE_WINBOND_W25Q40_V) {
+ free(w25q40v_otp_1_contents);
+ free(w25q40v_otp_2_contents);
+ free(w25q40v_otp_3_contents);
+ }
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)
{
@@ -457,36 +761,40 @@ static uint32_t dummy_chip_readl(const struct flashctx *flash, const chipaddr ad
{
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)
{
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 gd25q128c_rems_response[2] = {0xc8, 0x17};
+ const unsigned char en25qh128_rems_response[2] = {0x1c, 0x17};
+ const unsigned char w25q40v_rems_response[2] = {0xef, 0x12};
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 < spi_blacklist_size; i++) {
if (writearr[0] == spi_blacklist[i]) {
msg_pdbg("Refusing blacklisted SPI command 0x%02x\n",
spi_blacklist[i]);
return SPI_INVALID_OPCODE;
}
}
@@ -525,101 +833,241 @@ static int emulate_spi_chip_response(unsigned int writecnt,
break;
case EMULATE_SST_SST25VF040_REMS:
for (i = 0; i < readcnt; i++)
readarr[i] = sst25vf040_rems_response[(offs + i) % 2];
break;
case EMULATE_SST_SST25VF032B:
for (i = 0; i < readcnt; i++)
readarr[i] = sst25vf032b_rems_response[(offs + i) % 2];
break;
case EMULATE_MACRONIX_MX25L6436:
if (readcnt > 0)
memset(readarr, 0x16, readcnt);
break;
+ case EMULATE_GIGADEVICE_GD25Q128C:
+ if (readcnt > 0)
+ memset(readarr, 0x17, readcnt);
+ break;
+ case EMULATE_EON_EN25QH128:
+ if (readcnt > 0)
+ memset(readarr, 0x17, readcnt);
+ break;
+ case EMULATE_WINBOND_W25Q40_V:
+ if (readcnt > 0)
+ memset(readarr, 0x12, readcnt);
+ break;
default: /* ignore */
break;
}
break;
case JEDEC_REMS:
/* REMS response has wraparound and uses an address parameter. */
if (writecnt < JEDEC_REMS_OUTSIZE)
break;
offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
offs += writecnt - JEDEC_REMS_OUTSIZE;
switch (emu_chip) {
case EMULATE_SST_SST25VF040_REMS:
for (i = 0; i < readcnt; i++)
readarr[i] = sst25vf040_rems_response[(offs + i) % 2];
break;
case EMULATE_SST_SST25VF032B:
for (i = 0; i < readcnt; i++)
readarr[i] = sst25vf032b_rems_response[(offs + i) % 2];
break;
case EMULATE_MACRONIX_MX25L6436:
for (i = 0; i < readcnt; i++)
readarr[i] = mx25l6436_rems_response[(offs + i) % 2];
break;
+ case EMULATE_GIGADEVICE_GD25Q128C:
+ for (i = 0; i < readcnt; i++)
+ readarr[i] = gd25q128c_rems_response[(offs + i) % 2];
+ break;
+ case EMULATE_EON_EN25QH128:
+ for (i = 0; i < readcnt; i++)
+ readarr[i] = en25qh128_rems_response[(offs + i) % 2];
+ break;
+ case EMULATE_WINBOND_W25Q40_V:
+ for (i = 0; i < readcnt; i++)
+ readarr[i] = w25q40v_rems_response[(offs + i) % 2];
+ break;
default: /* ignore */
break;
}
break;
case JEDEC_RDID:
switch (emu_chip) {
case EMULATE_SST_SST25VF032B:
if (readcnt > 0)
readarr[0] = 0xbf;
if (readcnt > 1)
readarr[1] = 0x25;
if (readcnt > 2)
readarr[2] = 0x4a;
break;
case EMULATE_MACRONIX_MX25L6436:
if (readcnt > 0)
readarr[0] = 0xc2;
if (readcnt > 1)
readarr[1] = 0x20;
if (readcnt > 2)
readarr[2] = 0x17;
break;
+ case EMULATE_GIGADEVICE_GD25Q128C:
+ if (readcnt > 0)
+ readarr[0] = 0xc8;
+ if (readcnt > 1)
+ readarr[1] = 0x40;
+ if (readcnt > 2)
+ readarr[2] = 0x18;
+ break;
+ case EMULATE_EON_EN25QH128:
+ if (readcnt > 0)
+ readarr[0] = 0x1c;
+ if (readcnt > 1)
+ readarr[1] = 0x70;
+ if (readcnt > 2)
+ readarr[2] = 0x18;
+ break;
+ case EMULATE_WINBOND_W25Q40_V:
+ if (readcnt > 0)
+ readarr[0] = 0xef;
+ if (readcnt > 1)
+ readarr[1] = 0x40;
+ if (readcnt > 2)
+ readarr[2] = 0x13;
+ break;
default: /* ignore */
break;
}
break;
case JEDEC_RDSR:
+ if (emu_chip == EMULATE_EON_EN25QH128 && en25qh128_otp_mode) {
+ memset(readarr, (emu_status & 0x7F) | (en25qh128_otp_bit << 7), readcnt);
+ break;
+ }
memset(readarr, emu_status, readcnt);
break;
+ case JEDEC_RDSR2:
+ if (emu_chip != EMULATE_GIGADEVICE_GD25Q128C && emu_chip != EMULATE_WINBOND_W25Q40_V)
+ break;
+ memset(readarr, (emu_status >> 8) & 0xff, readcnt);
+ break;
+ case JEDEC_RDSR3:
+ if (emu_chip != EMULATE_GIGADEVICE_GD25Q128C)
+ break;
+ memset(readarr, (emu_status >> 16) & 0xff, readcnt);
+ break;
/* FIXME: this should be chip-specific. */
case JEDEC_EWSR:
case JEDEC_WREN:
emu_status |= SPI_SR_WEL;
break;
- case JEDEC_WRSR:
+ case JEDEC_WRSR1:
+ if (!(emu_status & SPI_SR_WEL)) {
+ msg_perr("WRSR1 attempted, but WEL is 0!\n");
+ break;
+ }
+ if (emu_chip == EMULATE_EON_EN25QH128 && en25qh128_otp_mode) {
+ en25qh128_otp_bit = 1;
+ msg_pdbg("OTP bit set...\n");
+ break;
+ }
+ if (emu_chip == EMULATE_WINBOND_W25Q40_V) {
+ /* Make sure reserved bits and read-only bits are not set.
+ * For W25Q40.V, SUS (bit_7) and reserved (bit_2) bits in SR2 are read-only. */
+ read_only_bits = 0x84;
+ /* If any of the Lock Bits in SR2, LB[1..3] are set, then they are read-only. */
+ if (emu_status & 0x00ff00 & (1 << 3))
+ read_only_bits |= 1 << 3;
+ if (emu_status & 0x00ff00 & (1 << 4))
+ read_only_bits |= 1 << 4;
+ if (emu_status & 0x00ff00 & (1 << 5))
+ read_only_bits |= 1 << 5;
+ if (writecnt == 3)
+ emu_status |= ((writearr[2] & ~read_only_bits) & 0xff) << 8;
+ else
+ emu_status &= 0x00ff;
+ }
+ /* FIXME: add some reasonable simulation of the busy flag */
+ emu_status |= (writearr[1] & ~SPI_SR_WIP) & 0xffff;
+ msg_pdbg2("WRSR1 wrote 0x%02x.\n", emu_status & 0xffff);
+ break;
+ case JEDEC_WRSR2:
+ if (emu_chip != EMULATE_GIGADEVICE_GD25Q128C)
+ break;
if (!(emu_status & SPI_SR_WEL)) {
- msg_perr("WRSR attempted, but WEL is 0!\n");
+ msg_perr("WRSR2 attempted, but WEL is 0!\n");
break;
}
/* FIXME: add some reasonable simulation of the busy flag */
- emu_status = writearr[1] & ~SPI_SR_WIP;
- msg_pdbg2("WRSR wrote 0x%02x.\n", emu_status);
+ /* Make sure reserved bits and read-only bits are not set.
+ * For GD25Q128C, SUS1 (bit_7) and SUS2 (bit_2) bits in SR2 are read-only. */
+ read_only_bits = 0x84;
+ /* If any of the Lock Bits, LB[1..3] are set, then they are read-only. */
+ if (emu_status & 0x00ff00 & (1 << 3))
+ read_only_bits |= 1 << 3;
+ if (emu_status & 0x00ff00 & (1 << 4))
+ read_only_bits |= 1 << 4;
+ if (emu_status & 0x00ff00 & (1 << 5))
+ read_only_bits |= 1 << 5;
+ emu_status |= (writearr[1] & ~read_only_bits) & 0xff00;
+ break;
+ case JEDEC_WRSR3:
+ if (emu_chip != EMULATE_GIGADEVICE_GD25Q128C)
+ break;
+ if (!(emu_status & SPI_SR_WEL)) {
+ msg_perr("WRSR3 attempted, but WEL is 0!\n");
+ break;
+ }
+ /* FIXME: add some reasonable simulation of the busy flag */
+ /* Make sure reserved bits and read-only bits are not set.
+ * For GD25Q128C, bit_{0, 1, 3, 4} in SR3 are reserved. */
+ read_only_bits = 0x1b;
+ emu_status |= (writearr[1] & ~read_only_bits) &0xff0000;
break;
case JEDEC_READ:
offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+ if (emu_chip == EMULATE_EON_EN25QH128 && en25qh128_otp_mode) {
+ if (en25qh128_otp_bit) {
+ msg_perr("OTP bit is set, cannot erase OTP sector anymore\n");
+ break;
+ }
+ if ((~(offs >> 12) & 0xfff) || (offs & 0xfff) >= 0x200) {
+ msg_perr("Address out of range\n");
+ break;
+ }
+ memcpy(readarr, en25qh128_otp_contents + (offs & 0xfff), readcnt);
+ break;
+ }
/* 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];
+ if (emu_chip == EMULATE_EON_EN25QH128 && en25qh128_otp_mode) {
+ if (en25qh128_otp_bit) {
+ msg_perr("OTP bit is set, cannot program OTP sector anymore\n");
+ break;
+ }
+ if ((~(offs >> 12) & 0xfff) || (offs & 0xfff) >= 0x200) {
+ msg_perr("Address out of range\n");
+ break;
+ }
+ memcpy(en25qh128_otp_contents + (offs & 0xfff), writearr + 4, writecnt - 4);
+ break;
+ }
/* 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:
@@ -651,39 +1099,53 @@ static int emulate_spi_chip_response(unsigned int writecnt,
}
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)
emu_status &= ~SPI_SR_AAI;
+ if (emu_chip == EMULATE_EON_EN25QH128)
+ en25qh128_otp_mode = 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 (emu_chip == EMULATE_EON_EN25QH128 && en25qh128_otp_mode) {
+ if (en25qh128_otp_bit) {
+ msg_perr("OTP bit is set, cannot erase OTP sector anymore\n");
+ break;
+ }
+ if ((~(offs >> 12) & 0xfff) || (offs & 0xfff) >= 0x200) {
+ msg_perr("Address out of range\n");
+ break;
+ }
+ memset(en25qh128_otp_contents, 0xff, 512);
+ break;
+ }
if (offs & (emu_jedec_se_size - 1))
msg_pdbg("Unaligned SECTOR ERASE 0x20: 0x%x\n", offs);
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) {
@@ -734,26 +1196,27 @@ static int emulate_spi_chip_response(unsigned int writecnt,
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;
}
/* JEDEC_CE_C7_OUTSIZE is 1 (no address) -> no offset. */
/* emu_jedec_ce_c7_size is emu_chip_size. */
memset(flashchip_contents, 0xff, emu_jedec_ce_c7_size);
break;
case JEDEC_SFDP:
+ // TODO(hatim): SFDP for GD25Q128C, EN25QH128 and W25Q40.V
if (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--;
@@ -768,26 +1231,156 @@ static int emulate_spi_chip_response(unsigned int writecnt,
* 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_SEC_REG:
+ if (emu_chip != EMULATE_GIGADEVICE_GD25Q128C && emu_chip != EMULATE_WINBOND_W25Q40_V)
+ break;
+ if (writecnt != JEDEC_READ_SEC_REG_OUTSIZE) {
+ msg_perr("READ SECURITY REGISTER size not proper!\n");
+ break;
+ }
+ /* writearr[1..3] holds the address, writearr[1] must be 0x00,
+ * (writearr[2..3] & 01ff) holds the byte address pointing to within
+ * the security register range, and (writearr[2] & 0xf0) must be either
+ * of 0x01, 0x02 or 0x03 corresponding to security register 1, 2 or 3 resp. */
+ if (writearr[1] || (writearr[2] & 0x0e))
+ break;
+ offs = (writearr[2] & 0x01) << 8 | writearr[3];
+ /* Truncate to security register size, i.e., 512 bytes for GD25Q128C,
+ * or 256 bytes for W25Q40.V. */
+ offs %= (emu_chip == EMULATE_GIGADEVICE_GD25Q128C) ? 512 : 256;
+ if (readcnt > 0) {
+ switch ((writearr[2] & 0xf0) >> 4) {
+ case 0x01:
+ memcpy(readarr, ((emu_chip == EMULATE_GIGADEVICE_GD25Q128C) ?
+ gd25q128c_otp_1_contents : w25q40v_otp_1_contents) + offs, readcnt);
+ break;
+ case 0x02:
+ memcpy(readarr, ((emu_chip == EMULATE_GIGADEVICE_GD25Q128C) ?
+ gd25q128c_otp_2_contents : w25q40v_otp_2_contents) + offs, readcnt);
+ break;
+ case 0x03:
+ memcpy(readarr, ((emu_chip == EMULATE_GIGADEVICE_GD25Q128C) ?
+ gd25q128c_otp_3_contents : w25q40v_otp_3_contents) + offs, readcnt);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case JEDEC_PROG_BYTE_SEC_REG:
+ if (emu_chip != EMULATE_GIGADEVICE_GD25Q128C && emu_chip != EMULATE_WINBOND_W25Q40_V)
+ break;
+ if (writecnt < JEDEC_PROG_BYTE_SEC_REG_OUTSIZE) {
+ msg_perr("PROGRAM SECURITY REGISTER size too short!\n");
+ break;
+ }
+ /* writearr[1..3] holds the address, writearr[1] must be 0x00,
+ * (writearr[2..3] & 01ff) holds the byte address pointing to within
+ * the security register range, and (writearr[2] & 0xf0) must be either
+ * of 0x01, 0x02 or 0x03 corresponding to security register 1, 2 or 3 resp. */
+ if (writearr[1] || (writearr[2] & 0x0e))
+ break;
+ offs = (writearr[2] & 0x01) << 8 | writearr[3];
+ /* Truncate to security register size, i.e., 512 bytes for GD25Q128C,
+ * or 256 bytes for W25Q40.V. */
+ offs %= (emu_chip == EMULATE_GIGADEVICE_GD25Q128C) ? 512 : 256;
+ /* If corresponding Lock Bits are set then the register is locked against
+ * any further write attempts. */
+ switch ((writearr[2] & 0xf0) >> 4) {
+ case 0x01:
+ /* LB1 is NOT set */
+ if (!(emu_status & 0x00ff00 & (1 << 3)))
+ memcpy(((emu_chip == EMULATE_GIGADEVICE_GD25Q128C) ?
+ gd25q128c_otp_1_contents : w25q40v_otp_1_contents) + offs,
+ writearr + 4, writecnt - 4);
+ break;
+ case 0x02:
+ /* LB2 is NOT set */
+ if (!(emu_status & 0x00ff00 & (1 << 4)))
+ memcpy(((emu_chip == EMULATE_GIGADEVICE_GD25Q128C) ?
+ gd25q128c_otp_2_contents : w25q40v_otp_2_contents) + offs,
+ writearr + 4, writecnt - 4);
+ break;
+ case 0x03:
+ /* LB3 is NOT set */
+ if (!(emu_status & 0x00ff00 & (1 << 5)))
+ memcpy(((emu_chip == EMULATE_GIGADEVICE_GD25Q128C) ?
+ gd25q128c_otp_3_contents : w25q40v_otp_3_contents) + offs,
+ writearr + 4, writecnt - 4);
+ break;
+ default:
+ break;
+ }
+ break;
+ case JEDEC_ERASE_SEC_REG:
+ if (emu_chip != EMULATE_GIGADEVICE_GD25Q128C && emu_chip != EMULATE_WINBOND_W25Q40_V)
+ break;
+ if (writecnt != JEDEC_ERASE_SEC_REG_OUTSIZE) {
+ msg_perr("ERASE SECURITY REGISTER size not proper!\n");
+ break;
+ }
+ /* writearr[1..3] holds the address, writearr[1] must be 0x00,
+ * (writearr[2..3] & 01ff) holds the byte address pointing to within
+ * the security register range, and (writearr[2] & 0xf0) must be either
+ * of 0x01, 0x02 or 0x03 corresponding to security register 1, 2 or 3 resp. */
+ if (writearr[1] || (writearr[2] & 0x0e))
+ break;
+ /* If corresponding Lock Bits are set then the register is locked against
+ * any further erase attempts. */
+ switch ((writearr[2] & 0xf0) >> 4) {
+ case 0x01:
+ /* LB1 is NOT set */
+ if (!(emu_status & 0x00ff00 & (1 << 3)))
+ memset((emu_chip == EMULATE_GIGADEVICE_GD25Q128C) ?
+ gd25q128c_otp_1_contents : w25q40v_otp_1_contents, 0xff,
+ (emu_chip == EMULATE_GIGADEVICE_GD25Q128C) ? 512 : 256);
+ break;
+ case 0x02:
+ /* LB2 is NOT set */
+ if (!(emu_status & 0x00ff00 & (1 << 4)))
+ memset((emu_chip == EMULATE_GIGADEVICE_GD25Q128C) ?
+ gd25q128c_otp_2_contents : w25q40v_otp_2_contents, 0xff,
+ (emu_chip == EMULATE_GIGADEVICE_GD25Q128C) ? 512 : 256);
+ break;
+ case 0x03:
+ /* LB3 is NOT set */
+ if (!(emu_status & 0x00ff00 & (1 << 5)))
+ memset((emu_chip == EMULATE_GIGADEVICE_GD25Q128C) ?
+ gd25q128c_otp_3_contents : w25q40v_otp_3_contents, 0xff,
+ (emu_chip == EMULATE_GIGADEVICE_GD25Q128C) ? 512 : 256);
+ break;
+ default:
+ break;
+ }
+ break;
+ case JEDEC_ENTER_OTP:
+ /* Eon chip specific opcode, not observed in other
+ * manufacturers (yet, please update this when required). */
+ if (emu_chip != EMULATE_EON_EN25QH128)
+ break;
+ en25qh128_otp_mode = 1;
+ msg_pdbg("Entered OTP mode...\n");
+ break;
default:
/* No special response. */
break;
}
if (writearr[0] != JEDEC_WREN && writearr[0] != JEDEC_EWSR)
emu_status &= ~SPI_SR_WEL;
return 0;
}
#endif
static int dummy_spi_send_command(struct flashctx *flash, unsigned int writecnt,
unsigned int readcnt,
const unsigned char *writearr,
@@ -799,26 +1392,29 @@ static int dummy_spi_send_command(struct flashctx *flash, unsigned int writecnt,
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_chip) {
case EMULATE_ST_M25P10_RES:
case EMULATE_SST_SST25VF040_REMS:
case EMULATE_SST_SST25VF032B:
case EMULATE_MACRONIX_MX25L6436:
+ case EMULATE_GIGADEVICE_GD25Q128C:
+ case EMULATE_EON_EN25QH128:
+ case EMULATE_WINBOND_W25Q40_V:
if (emulate_spi_chip_response(writecnt, readcnt, writearr,
readarr)) {
msg_pdbg("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(" 0x%02x", readarr[i]);
diff --git a/otp.c b/otp.c
index 4af4b84..20a8f84 100644
--- a/otp.c
+++ b/otp.c
@@ -124,52 +124,52 @@ int eon_print_status_generic(struct flashctx *flash) {
}
/* Read len bytes of the security register (corresponding to otp_region) into buf,
* starting from start_byte. */
int eon_read_generic(struct flashctx *flash, uint8_t *buf, enum otp_region otp_region,
uint32_t start_byte, uint32_t len) {
int result = otp_error_check(flash, otp_region, start_byte, len);
if (result) {
msg_cerr("%s failed\n", __func__);
return result;
}
enter_otp_mode(flash);
- result = flash->chip->read(flash, buf, start_byte, len);
+ result = flash->chip->read(flash, buf, flash->chip->otp->region[otp_region].addr | start_byte, len);
exit_otp_mode(flash);
if (result)
msg_cerr("%s failed\n", __func__);
return result;
}
/* Write len bytes to the security register (corresponding to otp_region) form buf,
* starting from start_byte. */
int eon_write_generic(struct flashctx *flash, const uint8_t *buf, enum otp_region otp_region,
uint32_t start_byte, uint32_t len) {
int result = otp_error_check(flash, otp_region, start_byte, len);
if (result) {
msg_cerr("%s failed\n", __func__);
return result;
}
if (flash->chip->otp->status(flash, otp_region)) {
msg_cdbg("OTP memory region %d is permanently locked and cannot be written to\n",
otp_region + 1);
msg_cerr("%s failed\n", __func__);
return 1;
}
enter_otp_mode(flash);
- result = flash->chip->write(flash, buf, start_byte, len);
+ result = flash->chip->write(flash, buf, flash->chip->otp->region[otp_region].addr | start_byte, len);
exit_otp_mode(flash);
if (result)
msg_cerr("%s failed\n", __func__);
return result;
}
/* Erase the security register corresponding to otp_region. */
int eon_erase_generic(struct flashctx *flash, enum otp_region otp_region) {
int result = otp_error_check(flash, otp_region, 0x000000, 0);
if (result) {
msg_cerr("%s failed\n", __func__);
return result;
--
2.7.4