summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikolai Artemiev <nartemiev@google.com>2021-10-21 01:35:13 +1100
committerAnastasia Klimchuk <aklm@chromium.org>2022-03-01 04:12:19 +0000
commit9fc100f54953dc74603678fc42e74d9b99e61ff0 (patch)
treeabfee6a62c3ad028187e36f97796d099c944a104
parentcff87a8488597b08c8b0ffb7037ed44f840a156b (diff)
downloadflashrom-9fc100f54953dc74603678fc42e74d9b99e61ff0.tar.gz
flashrom-9fc100f54953dc74603678fc42e74d9b99e61ff0.tar.bz2
flashrom-9fc100f54953dc74603678fc42e74d9b99e61ff0.zip
flashchips,writeprotect_ranges: 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 some will require different functions (e.g. MX25L6406 and related chips). 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 BRANCH=none TEST=dumped range tables, checked against datasheets Change-Id: Id163ed80938a946a502ed116e48e8236e36eb203 Signed-off-by: Nikolai Artemiev <nartemiev@google.com> Reviewed-on: https://review.coreboot.org/c/flashrom/+/58480 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Edward O'Callaghan <quasisec@chromium.org> Reviewed-by: Nico Huber <nico.h@gmx.de> Reviewed-by: Anastasia Klimchuk <aklm@chromium.org>
-rw-r--r--Makefile3
-rw-r--r--chipdrivers.h3
-rw-r--r--flash.h6
-rw-r--r--flashchips.c3
-rw-r--r--meson.build1
-rw-r--r--writeprotect_ranges.c92
6 files changed, 107 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 6841552a7..9a9628fd5 100644
--- a/Makefile
+++ b/Makefile
@@ -381,7 +381,8 @@ endif
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 ea8d480d5..c223534a8 100644
--- a/chipdrivers.h
+++ b/chipdrivers.h
@@ -216,4 +216,7 @@ int edi_probe_kb9012(struct flashctx *flash);
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_spi25(size_t *start, size_t *len, const struct wp_bits *, size_t chip_len);
+
#endif /* !__CHIPDRIVERS_H__ */
diff --git a/flash.h b/flash.h
index b935e9bc4..f63aa5d36 100644
--- a/flash.h
+++ b/flash.h
@@ -195,6 +195,8 @@ struct reg_bit_info {
} writability;
};
+struct wp_bits;
+
struct flashchip {
const char *vendor;
const char *name;
@@ -305,6 +307,10 @@ struct flashchip {
/* Complement bit (CMP) */
struct reg_bit_info cmp;
} reg_bits;
+
+ /* Function that takes a set of WP config bits (e.g. BP, SEC, TB, etc) */
+ /* and determines what protection range they select. */
+ void (*decode_range)(size_t *start, size_t *len, const struct wp_bits *, size_t chip_len);
};
typedef int (*chip_restore_fn_cb_t)(struct flashctx *flash, uint8_t status);
diff --git a/flashchips.c b/flashchips.c
index e12c8fceb..acb7718cc 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -6354,6 +6354,7 @@ const struct flashchip flashchips[] = {
.sec = {STATUS1, 6, RW}, /* Called BP4 in datasheet, acts like SEC */
.cmp = {STATUS2, 6, RW},
},
+ .decode_range = decode_range_spi25,
},
{
@@ -6760,6 +6761,7 @@ const struct flashchip flashchips[] = {
.bp = {{STATUS1, 2, RW}, {STATUS1, 3, RW}, {STATUS1, 4, RW}, {STATUS1, 5, RW}},
.tb = {STATUS1, 6, RW},
},
+ .decode_range = decode_range_spi25,
},
{
@@ -6808,6 +6810,7 @@ const struct flashchip flashchips[] = {
.sec = {STATUS1, 6, RW}, /* Called BP4 in datasheet, acts like SEC */
.cmp = {STATUS2, 6, RW},
},
+ .decode_range = decode_range_spi25,
},
{
diff --git a/meson.build b/meson.build
index 367c48abb..cc393a7c1 100644
--- a/meson.build
+++ b/meson.build
@@ -429,6 +429,7 @@ srcs += 'udelay.c'
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_ranges.c b/writeprotect_ranges.c
new file mode 100644
index 000000000..dacce322d
--- /dev/null
+++ b/writeprotect_ranges.c
@@ -0,0 +1,92 @@
+/*
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 "writeprotect.h"
+#include "chipdrivers.h"
+
+/*
+ * Protection range calculation that works with many common SPI flash chips.
+ */
+void decode_range_spi25(size_t *start, size_t *len, const struct wp_bits *bits, size_t chip_len)
+{
+ /* Interpret BP bits as an integer */
+ size_t bp = 0;
+ size_t bp_max = 0;
+
+ for (size_t i = 0; i < bits->bp_bit_count; i++) {
+ bp |= bits->bp[i] << i;
+ bp_max |= 1 << i;
+ }
+
+ if (bp == 0) {
+ /* Special case: all BP bits are 0 => no write protection */
+ *len = 0;
+ } else if (bp == bp_max) {
+ /* Special case: all BP bits are 1 => full write protection */
+ *len = 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.
+ */
+
+ size_t coeff = 1 << (bp - 1);
+ size_t max_coeff = 1 << (bp_max - 2);
+
+ size_t sector_len = 4 * KiB;
+ size_t default_block_len = 64 * KiB;
+
+ if (bits->sec_bit_present && bits->sec == 1) {
+ /*
+ * SEC=1, protect 4K sectors. Flash chips clamp the
+ * protection length at 32K, probably to avoid overlap
+ * with the SEC=0 case.
+ */
+ *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.
+ */
+ size_t min_block_len = chip_len / 2 / max_coeff;
+ size_t block_len = max(min_block_len, default_block_len);
+
+ *len = min(block_len * coeff, chip_len);
+ }
+ }
+
+ /* Apply TB bit */
+ bool protect_top = bits->tb_bit_present ? (bits->tb == 0) : 1;
+
+ /* Apply CMP bit */
+ if (bits->cmp_bit_present && bits->cmp == 1) {
+ *len = chip_len - *len;
+ protect_top = !protect_top;
+ }
+
+ /* Calculate start address, ensuring that empty ranges start at 0 */
+ if (protect_top && *len > 0)
+ *start = chip_len - *len;
+ else
+ *start = 0;
+}