aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 6841552a..9a9628fd 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 ea8d480d..c223534a 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 b935e9bc..f63aa5d3 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 e12c8fce..acb7718c 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 367c48ab..cc393a7c 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 00000000..dacce322
--- /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;
+}