aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergii Dmytruk <sergii.dmytruk@3mdeb.com>2021-11-08 01:38:52 +0200
committerAnastasia Klimchuk <aklm@chromium.org>2022-05-12 03:05:18 +0000
commit3f4b62b444b01800ac07451f506986d7e612d708 (patch)
tree6bb2bdb1fbcb9d9db8b63a79e33426635d99dc30
parent8245b57e471963d9b3a48b30d3b7f7f26ef8ab57 (diff)
downloadflashrom-3f4b62b444b01800ac07451f506986d7e612d708.tar.gz
flashrom-3f4b62b444b01800ac07451f506986d7e612d708.tar.bz2
flashrom-3f4b62b444b01800ac07451f506986d7e612d708.zip
dummyflasher: enforce write protection for W25Q128FV
Start taking bits related to write protection into account. Also add "hwwp" parameter for dummy programmer that sets state of WP pin (not inverted value). TEST=use command-line interface to run WP-related commands dummyflasher doesn't store state of the chip between runs and flashrom allows running only one command, so testing WP in this way is limited. However, WP options can be combined with other operations and are executed prior to them, so certain scenarios can be checked. List possible ranges: flashrom -p dummy:emulate=W25Q128FV,hwwp=yes --wp-list Set a particular range and check status is correct: flashrom -p dummy:emulate=W25Q128FV,hwwp=yes \ --wp-enable \ --wp-range=0x00100000,0x00f00000 \ --wp-status Enable write protection and try erasing/writing (erasing here): # this fails flashrom -p dummy:emulate=W25Q128FV,hwwp=yes \ --wp-range=0,0x00c00000 \ --wp-enable \ --erase Write protecting empty range has no effect: # this succeeds flashrom -p dummy:emulate=W25Q128FV,hwwp=yes \ --wp-range=0,0 \ --wp-enable \ --erase Disabling WP is possible if hwwp is off: # this fails flashrom -p dummy:emulate=W25Q128FV,spi_status=0x80,hwwp=yes \ --wp-disable # this succeeds flashrom -p dummy:emulate=W25Q128FV,spi_status=0x80,hwwp=no \ --wp-disable Change-Id: I9fd1417f941186391bd213bd355530143c8f04a0 Signed-off-by: Sergii Dmytruk <sergii.dmytruk@3mdeb.com> Reviewed-on: https://review.coreboot.org/c/flashrom/+/59074 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Anastasia Klimchuk <aklm@chromium.org> Reviewed-by: Thomas Heijligen <src@posteo.de> Reviewed-by: Edward O'Callaghan <quasisec@chromium.org>
-rw-r--r--dummyflasher.c160
-rw-r--r--flashrom.8.tmpl15
2 files changed, 157 insertions, 18 deletions
diff --git a/dummyflasher.c b/dummyflasher.c
index 63231392..1ee9d3ea 100644
--- a/dummyflasher.c
+++ b/dummyflasher.c
@@ -26,6 +26,7 @@
#include "programmer.h"
#include "flashchips.h"
#include "spi.h"
+#include "writeprotect.h"
enum emu_chip {
EMULATE_NONE,
@@ -64,6 +65,11 @@ struct emu_data {
unsigned int spi_blacklist_size;
unsigned int spi_ignorelist_size;
+ bool hwwp; /* 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;
};
@@ -173,7 +179,14 @@ static uint8_t get_reg_ro_bit_mask(const struct emu_data *data, enum flash_reg r
uint8_t ro_bits = reg == STATUS1 ? SPI_SR_WIP : 0;
if (data->emu_chip == EMULATE_WINBOND_W25Q128FV) {
- if (reg == STATUS2) {
+ const bool srp0 = (data->emu_status[0] >> 7);
+ const bool srp1 = (data->emu_status[1] & 1);
+
+ const bool wp_active = (srp1 || (srp0 && data->hwwp));
+
+ if (wp_active) {
+ ro_bits = 0xff;
+ } else if (reg == STATUS2) {
/* SUS (bit_7) and (R) (bit_2). */
ro_bits = 0x84;
/* Once any of the lock bits (LB[1..3]) are set, they
@@ -190,6 +203,79 @@ static uint8_t get_reg_ro_bit_mask(const struct emu_data *data, enum flash_reg r
return ro_bits;
}
+static void update_write_protection(struct emu_data *data)
+{
+ if (data->emu_chip != EMULATE_WINBOND_W25Q128FV)
+ return;
+
+ const struct wp_bits bits = {
+ .srp = data->emu_status[0] >> 7,
+ .srl = data->emu_status[1] & 1,
+
+ .bp_bit_count = 3,
+ .bp =
+ {
+ (data->emu_status[0] >> 2) & 1,
+ (data->emu_status[0] >> 3) & 1,
+ (data->emu_status[0] >> 4) & 1
+ },
+
+ .tb_bit_present = true,
+ .tb = (data->emu_status[0] >> 5) & 1,
+
+ .sec_bit_present = true,
+ .sec = (data->emu_status[0] >> 6) & 1,
+
+ .cmp_bit_present = true,
+ .cmp = (data->emu_status[1] >> 6) & 1,
+ };
+
+ size_t start;
+ size_t len;
+ decode_range_spi25(&start, &len, &bits, data->emu_chip_size);
+
+ data->wp_start = start;
+ data->wp_end = start + 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;
+
+ const uint32_t last = start + len - 1;
+ return (start < data->wp_end && last >= data->wp_start);
+}
+
+/* Returns non-zero on error. */
+static int write_flash_data(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_data(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;
+ }
+
+ /* FIXME: take data->erase_to_zero into account. */
+ 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,
@@ -376,6 +462,8 @@ static int emulate_spi_chip_response(unsigned int writecnt,
msg_pdbg2("WRSR wrote 0x%02x%02x.\n", data->emu_status[1], data->emu_status[0]);
else
msg_pdbg2("WRSR wrote 0x%02x.\n", data->emu_status[0]);
+
+ update_write_protection(data);
break;
case JEDEC_WRSR2:
if (data->emu_status_len < 2)
@@ -390,6 +478,8 @@ static int emulate_spi_chip_response(unsigned int writecnt,
data->emu_status[1] |= (writearr[1] & ~ro_bits);
msg_pdbg2("WRSR2 wrote 0x%02x.\n", data->emu_status[1]);
+
+ update_write_protection(data);
break;
case JEDEC_WRSR3:
if (data->emu_status_len < 3)
@@ -431,8 +521,10 @@ static int emulate_spi_chip_response(unsigned int writecnt,
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_data(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];
@@ -446,8 +538,10 @@ static int emulate_spi_chip_response(unsigned int writecnt,
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_data(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)
@@ -468,7 +562,10 @@ static int emulate_spi_chip_response(unsigned int writecnt,
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_data(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) {
@@ -481,10 +578,12 @@ static int emulate_spi_chip_response(unsigned int writecnt,
"too long!\n");
return 1;
}
- memcpy(data->flashchip_contents + aai_offs, writearr + 1, 2);
+ if (write_flash_data(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)
@@ -505,8 +604,10 @@ static int emulate_spi_chip_response(unsigned int writecnt,
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_data(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)
@@ -523,8 +624,10 @@ static int emulate_spi_chip_response(unsigned int writecnt,
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_data(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)
@@ -541,8 +644,10 @@ static int emulate_spi_chip_response(unsigned int writecnt,
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_data(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)
@@ -557,8 +662,10 @@ static int emulate_spi_chip_response(unsigned int writecnt,
}
/* 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_data(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)
@@ -573,8 +680,10 @@ static int emulate_spi_chip_response(unsigned int writecnt,
}
/* 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_data(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)
@@ -884,6 +993,21 @@ static int init_data(struct emu_data *data, enum chipbustype *dummy_buses_suppor
free(tmp);
}
+ tmp = extract_programmer_param("hwwp");
+ if (tmp) {
+ if (!strcmp(tmp, "yes")) {
+ msg_pdbg("Emulated chip will have hardware WP enabled\n");
+ data->hwwp = true;
+ } else if (!strcmp(tmp, "no")) {
+ msg_pdbg("Emulated chip will have hardware WP disabled\n");
+ } else {
+ msg_perr("hwwp 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 0f7bc974..58f5ec0c 100644
--- a/flashrom.8.tmpl
+++ b/flashrom.8.tmpl
@@ -820,6 +820,21 @@ is a hexadecimal value of up to 24 bits. For example, 0x332211 assigns 0x11 to
SR1, 0x22 to SR2 and 0x33 to SR3. Shorter value is padded to 24 bits with
zeroes on the left. See datasheet for chosen chip for details about the
registers content.
+.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:hwwp=state"
+.sp
+syntax where
+.B state
+is "yes" or "no" (default value). "yes" means active state of the pin implies
+that chip is write-protected (on real hardware the pin is usually negated, but
+not here).
.SS
.BR "nic3com" , " nicrealtek" , " nicnatsemi" , " nicintel", " nicintel_eeprom"\
, " nicintel_spi" , " gfxnvidia" , " ogp_spi" , " drkaiser" , " satasii"\