From patchwork Tue Aug 23 02:57:09 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hatim Kanchwala X-Patchwork-Id: 661696 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail.coreboot.org (mail.coreboot.org [80.81.252.135]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3sJFWW3lRyz9sR9 for ; Tue, 23 Aug 2016 12:58:46 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=hatimak-me.20150623.gappssmtp.com header.i=@hatimak-me.20150623.gappssmtp.com header.b=YwESd37K; dkim-atps=neutral Received: from [127.0.0.1] (helo=ra.coresystems.de) by mail.coreboot.org with esmtp (Exim 4.86_2) (envelope-from ) id 1bc1u9-0005dO-HY; Tue, 23 Aug 2016 04:57:33 +0200 Received: from mail-pf0-f176.google.com ([209.85.192.176]) by mail.coreboot.org with esmtps (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.86_2) (envelope-from ) id 1bc1tu-0005cw-8c for flashrom@flashrom.org; Tue, 23 Aug 2016 04:57:30 +0200 Received: by mail-pf0-f176.google.com with SMTP id p64so38503092pfb.1 for ; Mon, 22 Aug 2016 19:57:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hatimak-me.20150623.gappssmtp.com; s=20150623; h=to:from:subject:message-id:date:user-agent:mime-version :content-transfer-encoding; bh=6LW51iiYD/ziOi9BHzIMsI2sbikPj1IdJawwRly5P1s=; b=YwESd37KtXacielOw2aI7GRc00XXJSbVxq2NSGBcuHcGdK9Ppw0ruS98aCA01k+qY0 RvThpjztft1G0588iEncypUGwej4kEgAn8mKTJ0aiobW5p4GDJA7g2riZhB8ySilvglo xkIoRfV+fw8uCcTGHa+RykQSiAJPz+lFylwyBCCmpcxVpmAyK532+0Z72+mPiM1tOA4s jKEkQXIkXHZLMbofse6zPkCTqtVncE/HNwlJ+kkNc40Q6S/Q0OiifjqnuVRZQ3TXKwYt AWtuQtyIKwmUeKtJHrBALLQfvYVvNojLC5h1ptQSHQPgVH4Lir8jpLGre6YFEn6mhcqs NPjA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:to:from:subject:message-id:date:user-agent :mime-version:content-transfer-encoding; bh=6LW51iiYD/ziOi9BHzIMsI2sbikPj1IdJawwRly5P1s=; b=f4P7DCwDBzALKLLn/1daIaWUwhtKKGCr1JbuPNKb4CEx1hB+MfxprbrQ9QYqQMRBJq wXsmnmkhrNoVRn9+C1I2G/jE8EDJRM1jvVHEwm5OwQzFaGppZCmn+SQ1917klHtnLjgo qmsG4D9EqqnMgN6v9az5/Nu/qUB4fDpLFcNSl6GvV2/ZDOEOxYGzpztUhF6OkvtHaspE MJTWnP/jU9byZwDIN2Ft9I92r7Pb8DnlKdNRsOai3SNftQ19lv4zjkbPv0eBtDUs0m/Y sbP6Zhe+Q2dCWlBKYNQ55QxqzoAUbkiWlUMd7pVlT9suwQa0y8fgSKtEQ+TiEcfx3wHT odYg== X-Gm-Message-State: AEkooutl5tpgZ6a367u1oS0pZpwtLd71cYu60h7lioF7nMj37mH2eZw+ywZsZ3jVAg0mnw== X-Received: by 10.98.68.1 with SMTP id r1mr49419988pfa.161.1471921034889; Mon, 22 Aug 2016 19:57:14 -0700 (PDT) Received: from [172.16.151.156] ([14.139.194.12]) by smtp.gmail.com with ESMTPSA id e68sm864397pfk.1.2016.08.22.19.57.12 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 22 Aug 2016 19:57:14 -0700 (PDT) To: flashrom From: Hatim Kanchwala Message-ID: Date: Tue, 23 Aug 2016 08:27:09 +0530 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.2.0 MIME-Version: 1.0 X-Spam-Score: 3.5 (+++) Subject: [flashrom] [PATCH] Add dummy chips GD25Q128C, EN25QH128 and W25Q40.V X-BeenThere: flashrom@flashrom.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: flashrom discussion and development mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: flashrom-bounces@flashrom.org Sender: "flashrom" X-Duff: Orig. Duff, Duff Lite, Duff Dry, Duff Dark, Raspberry Duff, Lady Duff, Red Duff, Tartar Control Duff 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 --- 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 * * 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 #include #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;