Sergii Dmytruk has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/59074 )
Change subject: [RFC] dummyflasher: write protection for W25Q128FV ......................................................................
[RFC] dummyflasher: write protection for W25Q128FV
Start taking bits related to write protection into account.
Also add "wp" parameter for dummy programmer that sets state of WP pin.
Change-Id: I9fd1417f941186391bd213bd355530143c8f04a0 Signed-off-by: Sergii Dmytruk sergii.dmytruk@3mdeb.com --- M dummyflasher.c M flashrom.8.tmpl 2 files changed, 159 insertions(+), 18 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/74/59074/1
diff --git a/dummyflasher.c b/dummyflasher.c index bb47c3c..f355d7e 100644 --- a/dummyflasher.c +++ b/dummyflasher.c @@ -25,6 +25,7 @@ #include "programmer.h" #include "flashchips.h" #include "spi.h" +#include "writeprotect.h"
enum emu_chip { EMULATE_NONE, @@ -60,6 +61,11 @@ unsigned int spi_blacklist_size; unsigned int spi_ignorelist_size;
+ bool wp; /* state of hardware write protection */ + /* wp_start == wp_end when write-protection is disabled */ + uint32_t wp_start; + uint32_t wp_end; + unsigned int spi_write_256_chunksize; uint8_t *flashchip_contents; }; @@ -166,7 +172,14 @@ uint8_t ro_bits = (status_reg == 1 ? SPI_SR_WEL | SPI_SR_WIP : 0);
if (data->emu_chip == EMULATE_WINBOND_W25Q128FV) { - if (status_reg == 2) { + const bool srp0 = (data->emu_status >> 7); + const bool srp1 = (data->emu_status2 & 1); + + const bool wp_active = (srp1 || (srp0 && data->wp)); + + if (wp_active) { + ro_bits = 0xff; + } else if (status_reg == 2) { /* SUS1 (bit_7) and (R) (bit_2). */ ro_bits = 0x84; /* Once any of the lock bits (LB[1..3]) are set, they @@ -180,6 +193,81 @@ return ro_bits; }
+static void update_write_protection(struct emu_data *data) +{ + if (data->emu_chip != EMULATE_WINBOND_W25Q128FV) + return; + + const struct wp_chip_state wpst = { + .srp_bit_count = 2, + .srp = {data->emu_status >> 7, data->emu_status2 & 1}, + + .bp_bit_count = 3, + .bp = + { + (data->emu_status >> 2) & 1, + (data->emu_status >> 3) & 1, + (data->emu_status >> 4) & 1 + }, + + .tb_bit_present = true, + .tb = (data->emu_status >> 5) & 1, + + .sec_bit_present = true, + .sec = (data->emu_status >> 6) & 1, + + .cmp_bit_present = true, + .cmp = (data->emu_status2 >> 6) & 1, + }; + + struct wp_range range = { .chip_len = data->emu_chip_size }; + decode_range_w25(&wpst, &range); + + data->wp_start = range.start; + data->wp_end = range.start + range.len; +} + +/* Checks whether range intersects a write-protected area of the flash if one is + * defined. */ +static bool is_write_protected(const struct emu_data *data, uint32_t start, uint32_t len) +{ + if (len == 0) + return false; + + if (start >= data->wp_start && start < data->wp_end) + return true; + + const uint32_t last = start + len - 1; + return (last >= data->wp_start && last < data->wp_end); +} + +/* Returns non-zero on error. */ +static int write_flash_part(struct emu_data *data, uint32_t start, uint32_t len, const uint8_t *buf) +{ + if (is_write_protected(data, start, len)) { + msg_perr("At least part of the write range is write protected!\n"); + return 1; + } + + memcpy(data->flashchip_contents + start, buf, len); + data->emu_modified = 1; + return 0; +} + +/* Returns non-zero on error. */ +static int erase_flash_part(struct emu_data *data, uint32_t start, uint32_t len) +{ + if (is_write_protected(data, start, len)) { + msg_perr("At least part of the erase range is write protected!\n"); + return 1; + } + + /* XXX: should data->erase_to_zero be taken into account here? */ + memset(data->flashchip_contents + start, 0xff, len); + data->emu_modified = 1; + return 0; +} + static int emulate_spi_chip_response(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, @@ -358,6 +446,8 @@ msg_pdbg2("WRSR wrote 0x%02x%02x.\n", data->emu_status2, data->emu_status); else msg_pdbg2("WRSR wrote 0x%02x.\n", data->emu_status); + + update_write_protection(data); break; case JEDEC_WRSR2: if (data->emu_status_len < 2) @@ -370,6 +460,8 @@ ro_bits = get_status_ro_bits(data, 2); data->emu_status2 &= ro_bits; data->emu_status2 |= (writearr[1] & ~ro_bits); + + update_write_protection(data); break; case JEDEC_READ: offs = writearr[1] << 16 | writearr[2] << 8 | writearr[3]; @@ -397,8 +489,10 @@ msg_perr("Max BYTE PROGRAM size exceeded!\n"); return 1; } - memcpy(data->flashchip_contents + offs, writearr + 4, writecnt - 4); - data->emu_modified = 1; + if (write_flash_part(data, offs, writecnt - 4, writearr + 4)) { + msg_perr("Failed to program flash!\n"); + return 1; + } break; case JEDEC_BYTE_PROGRAM_4BA: offs = writearr[1] << 24 | writearr[2] << 16 | writearr[3] << 8 | writearr[4]; @@ -412,8 +506,10 @@ msg_perr("Max BYTE PROGRAM size exceeded!\n"); return 1; } - memcpy(data->flashchip_contents + offs, writearr + 5, writecnt - 5); - data->emu_modified = 1; + if (write_flash_part(data, offs, writecnt - 5, writearr + 5)) { + msg_perr("Failed to program flash!\n"); + return 1; + } break; case JEDEC_AAI_WORD_PROGRAM: if (!data->emu_max_aai_size) @@ -434,7 +530,10 @@ writearr[3]; /* Truncate to emu_chip_size. */ aai_offs %= data->emu_chip_size; - memcpy(data->flashchip_contents + aai_offs, writearr + 4, 2); + if (write_flash_part(data, aai_offs, 2, writearr + 4)) { + msg_perr("Failed to program flash!\n"); + return 1; + } aai_offs += 2; } else { if (writecnt < JEDEC_AAI_WORD_PROGRAM_CONT_OUTSIZE) { @@ -447,10 +546,12 @@ "too long!\n"); return 1; } - memcpy(data->flashchip_contents + aai_offs, writearr + 1, 2); + if (write_flash_part(data, aai_offs, 2, writearr + 1)) { + msg_perr("Failed to program flash!\n"); + return 1; + } aai_offs += 2; } - data->emu_modified = 1; break; case JEDEC_WRDI: if (data->emu_max_aai_size) @@ -471,8 +572,10 @@ 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(data->flashchip_contents + offs, 0xff, data->emu_jedec_se_size); - data->emu_modified = 1; + if (erase_flash_part(data, offs, data->emu_jedec_se_size)) { + msg_perr("Failed to erase flash!\n"); + return 1; + } break; case JEDEC_BE_52: if (!data->emu_jedec_be_52_size) @@ -489,8 +592,10 @@ 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(data->flashchip_contents + offs, 0xff, data->emu_jedec_be_52_size); - data->emu_modified = 1; + if (erase_flash_part(data, offs, data->emu_jedec_be_52_size)) { + msg_perr("Failed to erase flash!\n"); + return 1; + } break; case JEDEC_BE_D8: if (!data->emu_jedec_be_d8_size) @@ -507,8 +612,10 @@ 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(data->flashchip_contents + offs, 0xff, data->emu_jedec_be_d8_size); - data->emu_modified = 1; + if (erase_flash_part(data, offs, data->emu_jedec_be_d8_size)) { + msg_perr("Failed to erase flash!\n"); + return 1; + } break; case JEDEC_CE_60: if (!data->emu_jedec_ce_60_size) @@ -523,8 +630,10 @@ } /* JEDEC_CE_60_OUTSIZE is 1 (no address) -> no offset. */ /* emu_jedec_ce_60_size is emu_chip_size. */ - memset(data->flashchip_contents, 0xff, data->emu_jedec_ce_60_size); - data->emu_modified = 1; + if (erase_flash_part(data, 0, data->emu_jedec_ce_60_size)) { + msg_perr("Failed to erase flash!\n"); + return 1; + } break; case JEDEC_CE_C7: if (!data->emu_jedec_ce_c7_size) @@ -539,8 +648,10 @@ } /* JEDEC_CE_C7_OUTSIZE is 1 (no address) -> no offset. */ /* emu_jedec_ce_c7_size is emu_chip_size. */ - memset(data->flashchip_contents, 0xff, data->emu_jedec_ce_c7_size); - data->emu_modified = 1; + if (erase_flash_part(data, 0, data->emu_jedec_ce_c7_size)) { + msg_perr("Failed to erase flash!\n"); + return 1; + } break; case JEDEC_SFDP: if (data->emu_chip != EMULATE_MACRONIX_MX25L6436) @@ -850,6 +961,21 @@ free(tmp); }
+ tmp = extract_programmer_param("wp"); + if (tmp) { + if (!strcmp(tmp, "yes")) { + msg_pdbg("Emulated chip will have WP enabled\n"); + data->wp = true; + } else if (!strcmp(tmp, "no")) { + msg_pdbg("Emulated chip will have WP disabled\n"); + } else { + msg_perr("wp can be "yes" or "no"\n"); + free(tmp); + return 1; + } + free(tmp); + } + tmp = extract_programmer_param("emulate"); if (!tmp) { msg_pdbg("Not emulating any flash chip.\n"); diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl index 989d677..000a67a 100644 --- a/flashrom.8.tmpl +++ b/flashrom.8.tmpl @@ -693,6 +693,8 @@ .sp .RB "* Macronix " MX25L6436 " SPI flash chip (8192 kB, RDID, SFDP)" .sp +.RB "* Winbond " W25Q128FV " SPI flash chip (16384 kB, RDID)" +.sp .RB "* Dummy vendor " VARIABLE_SIZE " SPI flash chip (configurable size, page write)" .sp Example: @@ -779,6 +781,19 @@ syntax where .B content is an 8-bit hexadecimal value. +.sp +.TP +.B Write protection +.sp +Chips with emulated WP: W25Q128FV. +.sp +You can simulate state of hardware protection pin (WP) with the +.sp +.B " flashrom -p dummy:wp=state" +.sp +syntax where +.B state +is "yes" or "no" (default value). .SS .BR "nic3com" , " nicrealtek" , " nicnatsemi" , " nicintel", " nicintel_eeprom"\ , " nicintel_spi" , " gfxnvidia" , " ogp_spi" , " drkaiser" , " satasii"\