From 994de3ae946820dd6e03eb600e2e623e917845a4 Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Mon, 25 Jul 2022 00:23:25 +0300 Subject: writeprotect_ranges.c: add more range functions Not all chips follow the same pattern. There are differences in how CMP bit is treated or in block size used. Change-Id: Ied7b27be2ee2426af8f473432e2b01a290de2365 Signed-off-by: Sergii Dmytruk Reviewed-on: https://review.coreboot.org/c/flashrom/+/66212 Reviewed-by: Nikolai Artemiev Reviewed-by: Anastasia Klimchuk Tested-by: build bot (Jenkins) --- include/chipdrivers.h | 3 +++ include/flash.h | 5 +++- writeprotect.c | 3 +++ writeprotect_ranges.c | 74 +++++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 73 insertions(+), 12 deletions(-) diff --git a/include/chipdrivers.h b/include/chipdrivers.h index 3b07afe31..34e3af8d8 100644 --- a/include/chipdrivers.h +++ b/include/chipdrivers.h @@ -216,5 +216,8 @@ int spi_block_erase_emulation(struct flashctx *flash, unsigned int addr, unsigne /* writeprotect_ranges.c */ void decode_range_spi25(size_t *start, size_t *len, const struct wp_bits *, size_t chip_len); +void decode_range_spi25_64k_block(size_t *start, size_t *len, const struct wp_bits *, size_t chip_len); +void decode_range_spi25_bit_cmp(size_t *start, size_t *len, const struct wp_bits *, size_t chip_len); +void decode_range_spi25_2x_block(size_t *start, size_t *len, const struct wp_bits *, size_t chip_len); #endif /* !__CHIPDRIVERS_H__ */ diff --git a/include/flash.h b/include/flash.h index 23222c722..b4b56fe00 100644 --- a/include/flash.h +++ b/include/flash.h @@ -224,8 +224,11 @@ struct reg_bit_info { struct wp_bits; enum decode_range_func { - NO_DECODE_RANGE_FUNC = 0, /* 0 indicates no range decode funciton is set. */ + NO_DECODE_RANGE_FUNC = 0, /* 0 indicates no range decode function is set. */ DECODE_RANGE_SPI25 = 1, + DECODE_RANGE_SPI25_64K_BLOCK = 2, + DECODE_RANGE_SPI25_BIT_CMP = 3, + DECODE_RANGE_SPI25_2X_BLOCK = 4, }; typedef void (decode_range_func_t)(size_t *start, size_t *len, const struct wp_bits *, size_t chip_len); diff --git a/writeprotect.c b/writeprotect.c index 1ac0c1ac4..38ac997ef 100644 --- a/writeprotect.c +++ b/writeprotect.c @@ -211,6 +211,9 @@ static decode_range_func_t *lookup_decode_range_func_ptr(const struct flashchip { switch (chip->decode_range) { case DECODE_RANGE_SPI25: return &decode_range_spi25; + case DECODE_RANGE_SPI25_64K_BLOCK: return &decode_range_spi25_64k_block; + case DECODE_RANGE_SPI25_BIT_CMP: return &decode_range_spi25_bit_cmp; + case DECODE_RANGE_SPI25_2X_BLOCK: return &decode_range_spi25_2x_block; /* default: total function, 0 indicates no decode range function set. */ case NO_DECODE_RANGE_FUNC: return NULL; }; diff --git a/writeprotect_ranges.c b/writeprotect_ranges.c index dacce322d..45678f22a 100644 --- a/writeprotect_ranges.c +++ b/writeprotect_ranges.c @@ -17,11 +17,11 @@ #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) +static void decode_range_generic(size_t *start, size_t *len, const struct wp_bits *bits, size_t chip_len, + bool fixed_block_len, bool apply_cmp_to_bp, int coeff_offset) { + const bool cmp = bits->cmp_bit_present && bits->cmp == 1; + /* Interpret BP bits as an integer */ size_t bp = 0; size_t bp_max = 0; @@ -31,6 +31,15 @@ void decode_range_spi25(size_t *start, size_t *len, const struct wp_bits *bits, bp_max |= 1 << i; } + /* + * Most chips: the CMP bit only negates the range. + * + * Some MX chips: the CMP bit negates the BP bits and the range. + * (CMP bit is often the MSB BP bit in such chips.) + */ + if (cmp && apply_cmp_to_bp) + bp ^= bp_max; + if (bp == 0) { /* Special case: all BP bits are 0 => no write protection */ *len = 0; @@ -40,14 +49,14 @@ void decode_range_spi25(size_t *start, size_t *len, const struct wp_bits *bits, } else { /* * Usual case: the BP bits encode a coefficient in the form - * `coeff = 2 ** (bp - 1)`. + * `coeff = 2 ** (bp - offset)` where `offset == 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 coeff = 1 << (bp - coeff_offset); + size_t max_coeff = 1 << (bp_max - coeff_offset - 1); size_t sector_len = 4 * KiB; size_t default_block_len = 64 * KiB; @@ -62,14 +71,19 @@ void decode_range_spi25(size_t *start, size_t *len, const struct wp_bits *bits, } else { /* * SEC=0 or is not present, protect blocks. - * + */ + size_t block_len = default_block_len; + + /* * 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); + if (!fixed_block_len) { + size_t min_block_len = chip_len / 2 / max_coeff; + block_len = max(min_block_len, default_block_len); + } *len = min(block_len * coeff, chip_len); } @@ -79,7 +93,7 @@ void decode_range_spi25(size_t *start, size_t *len, const struct wp_bits *bits, bool protect_top = bits->tb_bit_present ? (bits->tb == 0) : 1; /* Apply CMP bit */ - if (bits->cmp_bit_present && bits->cmp == 1) { + if (cmp) { *len = chip_len - *len; protect_top = !protect_top; } @@ -90,3 +104,41 @@ void decode_range_spi25(size_t *start, size_t *len, const struct wp_bits *bits, else *start = 0; } + +/* + * 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) +{ + decode_range_generic(start, len, bits, chip_len, + /*fixed_block_len=*/false, /*apply_cmp_to_bp=*/false, /*coeff_offset=*/1); +} + +/* + * Do not adjust block size to be able to fill half of the chip. + */ +void decode_range_spi25_64k_block(size_t *start, size_t *len, const struct wp_bits *bits, size_t chip_len) +{ + decode_range_generic(start, len, bits, chip_len, + /*fixed_block_len=*/true, /*apply_cmp_to_bp=*/false, /*coeff_offset=*/1); +} + +/* + * Inverts BP bits when CMP is set and treats all ones in BP bits as a request to protect whole chip regardless + * of the CMP bit. + */ +void decode_range_spi25_bit_cmp(size_t *start, size_t *len, const struct wp_bits *bits, size_t chip_len) +{ + decode_range_generic(start, len, bits, chip_len, + /*fixed_block_len=*/false, /*apply_cmp_to_bp=*/true, /*coeff_offset=*/1); +} + +/* + * This multiplies coefficient by 2. To be used with chips which have more BP bits than needed, such that the + * most significant BP bit effectively acts as "protect whole chip" flag. + */ +void decode_range_spi25_2x_block(size_t *start, size_t *len, const struct wp_bits *bits, size_t chip_len) +{ + decode_range_generic(start, len, bits, chip_len, + /*fixed_block_len=*/false, /*apply_cmp_to_bp=*/false, /*coeff_offset=*/0); +} -- cgit v1.2.3