Nikolai Artemiev has uploaded this change for review.
[RFC] writeprotect: add range decoding function
Allow chips to specify functions that map status register bits to
protection ranges. These are used to enumerate available ranges, and
determine the protection state of chips. The patch also adds a range
decoding function for the example chips. Many other chips can also be
handled by it, though others will require different functions (e.g.
MX25L6406).
Another approach that has been tried in cros flashrom is maintaining
tables of range data, but it quickly becomes error prone and hard to
validate.
Using a function to interpret the ranges allows compact encoding with
most chips and is flexible enough to allow chips with less predictable
ranges to be handled as well.
BUG=b:195381327,b:153800563
TEST=dumped range tables, checked against datasheets
BRANCH=none
Change-Id: Id163ed80938a946a502ed116e48e8236e36eb203
Signed-off-by: Nikolai Artemiev <nartemiev@google.com>
---
M Makefile
M chipdrivers.h
M flash.h
M flashchips.c
M meson.build
M writeprotect.c
A writeprotect_ranges.c
7 files changed, 118 insertions(+), 6 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/80/58480/1
diff --git a/Makefile b/Makefile
index bc0047f..df83586 100644
--- a/Makefile
+++ b/Makefile
@@ -283,7 +283,8 @@
CHIP_OBJS = jedec.o stm50.o w39.o w29ee011.o \
sst28sf040.o 82802ab.o \
sst49lfxxxc.o sst_fwhub.o edi.o flashchips.o spi.o spi25.o spi25_statusreg.o \
- spi95.o opaque.o sfdp.o en29lv640b.o at45db.o writeprotect.o s25f.o
+ spi95.o opaque.o sfdp.o en29lv640b.o at45db.o s25f.o \
+ writeprotect.o writeprotect_ranges.o
###############################################################################
# Library code.
diff --git a/chipdrivers.h b/chipdrivers.h
index ea8d480..9ae30d9 100644
--- a/chipdrivers.h
+++ b/chipdrivers.h
@@ -216,4 +216,7 @@
int probe_spi_st95(struct flashctx *flash);
int spi_block_erase_emulation(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
+/* writeprotect_ranges.c */
+void decode_range_w25(const struct wp_chip_state *wpst, struct wp_range *range);
+
#endif /* !__CHIPDRIVERS_H__ */
diff --git a/flash.h b/flash.h
index 8b3899c..1f0031a 100644
--- a/flash.h
+++ b/flash.h
@@ -199,6 +199,11 @@
} writability;
};
+struct wp_chip_state;
+struct wp_range;
+
+typedef void (*range_decode_fn_t)(const struct wp_chip_state *wpst, struct wp_range *range);
+
#define MAX_SRP_BITS 2
#define MAX_BP_BITS 4
@@ -292,6 +297,8 @@
struct reg_bit_info sec;
struct reg_bit_info cmp;
} reg_bits;
+
+ range_decode_fn_t decode_range;
};
typedef int (*chip_restore_fn_cb_t)(struct flashctx *flash, uint8_t status);
diff --git a/flashchips.c b/flashchips.c
index 65c2dcc..b49df7a 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -6354,6 +6354,7 @@
.sec = {STATUS1, 6, RW},
.cmp = {STATUS2, 6, RW},
},
+ .decode_range = decode_range_w25,
},
{
@@ -6800,6 +6801,7 @@
.sec = {STATUS1, 6, RW},
.cmp = {STATUS2, 6, RW},
},
+ .decode_range = decode_range_w25,
},
{
@@ -6921,6 +6923,7 @@
.sec = {STATUS1, 6, RW},
.cmp = {STATUS2, 6, RW},
},
+ .decode_range = decode_range_w25
},
{
.vendor = "GigaDevice",
@@ -8645,6 +8648,8 @@
.srp = {{STATUS1, 7, RW}},
.bp = {{STATUS1, 2, RW}, {STATUS1, 3, RW}},
},
+ .decode_range = decode_range_w25,
+
},
{
@@ -18299,6 +18304,7 @@
.bp = {{STATUS1, 2, RW}, {STATUS1, 3, RW}, {STATUS1, 4, RW}},
.tb = {STATUS1, 5, RW},
},
+ .decode_range = decode_range_w25,
.printlock = spi_prettyprint_status_register_plain, /* TODO: improve */
.unlock = spi_disable_blockprotect,
.write = spi_chip_write_256,
diff --git a/meson.build b/meson.build
index 1a36df8..98470c9 100644
--- a/meson.build
+++ b/meson.build
@@ -372,6 +372,7 @@
srcs += 'w29ee011.c'
srcs += 'w39.c'
srcs += 'writeprotect.c'
+srcs += 'writeprotect_ranges.c'
mapfile = 'libflashrom.map'
vflag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), mapfile)
diff --git a/writeprotect.c b/writeprotect.c
index fa347f8..1ca9439 100644
--- a/writeprotect.c
+++ b/writeprotect.c
@@ -150,21 +150,25 @@
return 1;
}
-
return 0;
}
void *suppress_unused_warning_for_write_wp_chip_state = write_wp_chip_state;
bool wp_supported(struct flashctx *flash)
{
- /* TODO */
- return false;
+ return flash->chip->decode_range != NULL;
}
int wp_get_range(struct flashctx *flash, struct wp_range *range)
{
- /* TODO */
- return 1;
+ struct wp_chip_state wpst;
+ if (read_wp_chip_state(flash, &wpst))
+ return 1;
+
+ range->chip_len = flash->chip->total_size * 1024;
+ flash->chip->decode_range(&wpst, range);
+
+ return 0;
}
int wp_set_range(struct flashctx *flash, const struct wp_range range)
diff --git a/writeprotect_ranges.c b/writeprotect_ranges.c
new file mode 100644
index 0000000..ea34f7b
--- /dev/null
+++ b/writeprotect_ranges.c
@@ -0,0 +1,90 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright 2021 Google LLC
+ *
+ * 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.
+ */
+
+#include "flash.h"
+#include "chipdrivers.h"
+#include "writeprotect.h"
+
+/* Protection range calculation that is applicable to many chips. TODO: Rename
+ * the function when the extent of it's compatibility with various chips is
+ * more fully known. */
+void decode_range_w25(const struct wp_chip_state *wpst, struct wp_range *range)
+{
+ /* Interpret BP bits as an integer */
+ uint32_t bp = 0;
+ uint32_t bp_max = 0;
+ for(size_t i = 0; i < wpst->bp_bit_count; i++) {
+ bp |= wpst->bp[i] << i;
+ bp_max |= 1 << i;
+ }
+
+ if (wpst->bp == 0) {
+ /* Special case: all BP bits are 0 => no write protection */
+ range->len = 0;
+ }
+ else if (bp == bp_max) {
+ /* Special case: all BP bits are 1 => full write protection */
+ range->len = range->chip_len;
+ }
+ else {
+ /* Usual case: the BP bits encode a coefficient in the form
+ * `coeff = 2 ** (bp - 1)`.
+ *
+ * The range's length is given by multiplying the coefficient
+ * by a base unit, usually a 4K sector or a 64K block. */
+
+ uint32_t coeff = 1 << (bp - 1);
+ uint32_t max_coeff = 1 << (bp_max - 2);
+
+ uint32_t sector_len = 4 * 1024;
+ uint32_t default_block_len = 64 * 1024;
+
+ if (wpst->sec_bit_present && wpst->sec == 1) {
+ /* SEC=1, protect 4K sectors. Flash chips clamp the
+ * protection length at 32K, probably to avoid overlap
+ * with the SEC=0 case.
+ */
+ range->len = min(sector_len * coeff, default_block_len / 2);
+ } else {
+ /* SEC=0 or is not present, protect blocks.
+ *
+ * With very large chips, the 'block' size can be
+ * larger than 64K. This occurs when a larger block
+ * size is needed so that half the chip can be
+ * protected by the maximum possible coefficient.
+ */
+ uint32_t min_block_len = range->chip_len / 2 / max_coeff;
+ uint32_t block_len = max(min_block_len, default_block_len);
+
+ range->len = min(block_len * coeff, range->chip_len);
+ }
+ }
+
+ /* Apply TB bit */
+ bool protect_top = wpst->tb_bit_present ? (wpst->tb == 0) : 1;
+
+ /* Apply CMP bit */
+ if (wpst->cmp_bit_present && wpst->cmp == 1) {
+ range->len = range->chip_len - range->len;
+ protect_top = !protect_top;
+ }
+
+ /* Calculate start address, ensuring that empty ranges have start
+ * address of 0. */
+ if (protect_top && range->len > 0)
+ range->start = range->chip_len - range->len;
+ else
+ range->start = 0;
+}
To view, visit change 58480. To unsubscribe, or for help writing mail filters, visit settings.