1 /*
2 * This file is part of the flashrom project.
3 *
4 * Copyright 2021 Google LLC
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17 #include "writeprotect.h"
18 #include "chipdrivers.h"
19
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)20 static void decode_range_generic(size_t *start, size_t *len, const struct wp_bits *bits, size_t chip_len,
21 bool fixed_block_len, bool apply_cmp_to_bp, int coeff_offset)
22 {
23 const bool cmp = bits->cmp_bit_present && bits->cmp == 1;
24
25 /* Interpret BP bits as an integer */
26 size_t bp = 0;
27 size_t bp_max = 0;
28
29 for (size_t i = 0; i < bits->bp_bit_count; i++) {
30 bp |= bits->bp[i] << i;
31 bp_max |= 1 << i;
32 }
33
34 /*
35 * Most chips: the CMP bit only negates the range.
36 *
37 * Some MX chips: the CMP bit negates the BP bits and the range.
38 * (CMP bit is often the MSB BP bit in such chips.)
39 */
40 if (cmp && apply_cmp_to_bp)
41 bp ^= bp_max;
42
43 if (bp == 0) {
44 /* Special case: all BP bits are 0 => no write protection */
45 *len = 0;
46 } else if (bp == bp_max) {
47 /* Special case: all BP bits are 1 => full write protection */
48 *len = chip_len;
49 } else {
50 /*
51 * Usual case: the BP bits encode a coefficient in the form
52 * `coeff = 2 ** (bp - offset)` where `offset == 1`.
53 *
54 * The range's length is given by multiplying the coefficient
55 * by a base unit, usually a 4K sector or a 64K block.
56 */
57
58 size_t coeff = 1 << (bp - coeff_offset);
59 size_t max_coeff = 1 << (bp_max - coeff_offset - 1);
60
61 size_t sector_len = 4 * KiB;
62 size_t default_block_len = 64 * KiB;
63
64 if (bits->sec_bit_present && bits->sec == 1) {
65 /*
66 * SEC=1, protect 4K sectors. Flash chips clamp the
67 * protection length at 32K, probably to avoid overlap
68 * with the SEC=0 case.
69 */
70 *len = min(sector_len * coeff, default_block_len / 2);
71 } else {
72 /*
73 * SEC=0 or is not present, protect blocks.
74 */
75 size_t block_len = default_block_len;
76
77 /*
78 * With very large chips, the 'block' size can be
79 * larger than 64K. This occurs when a larger block
80 * size is needed so that half the chip can be
81 * protected by the maximum possible coefficient.
82 */
83 if (!fixed_block_len) {
84 size_t min_block_len = chip_len / 2 / max_coeff;
85 block_len = max(min_block_len, default_block_len);
86 }
87
88 *len = min(block_len * coeff, chip_len);
89 }
90 }
91
92 /* Apply TB bit */
93 bool protect_top = bits->tb_bit_present ? (bits->tb == 0) : 1;
94
95 /* Apply CMP bit */
96 if (cmp) {
97 *len = chip_len - *len;
98 protect_top = !protect_top;
99 }
100
101 /* Calculate start address, ensuring that empty ranges start at 0 */
102 if (protect_top && *len > 0)
103 *start = chip_len - *len;
104 else
105 *start = 0;
106 }
107
108 /*
109 * Protection range calculation that works with many common SPI flash chips.
110 */
decode_range_spi25(size_t * start,size_t * len,const struct wp_bits * bits,size_t chip_len)111 void decode_range_spi25(size_t *start, size_t *len, const struct wp_bits *bits, size_t chip_len)
112 {
113 decode_range_generic(start, len, bits, chip_len,
114 /*fixed_block_len=*/false, /*apply_cmp_to_bp=*/false, /*coeff_offset=*/1);
115 }
116
117 /*
118 * Do not adjust block size to be able to fill half of the chip.
119 */
decode_range_spi25_64k_block(size_t * start,size_t * len,const struct wp_bits * bits,size_t chip_len)120 void decode_range_spi25_64k_block(size_t *start, size_t *len, const struct wp_bits *bits, size_t chip_len)
121 {
122 decode_range_generic(start, len, bits, chip_len,
123 /*fixed_block_len=*/true, /*apply_cmp_to_bp=*/false, /*coeff_offset=*/1);
124 }
125
126 /*
127 * Inverts BP bits when CMP is set and treats all ones in BP bits as a request to protect whole chip regardless
128 * of the CMP bit.
129 */
decode_range_spi25_bit_cmp(size_t * start,size_t * len,const struct wp_bits * bits,size_t chip_len)130 void decode_range_spi25_bit_cmp(size_t *start, size_t *len, const struct wp_bits *bits, size_t chip_len)
131 {
132 decode_range_generic(start, len, bits, chip_len,
133 /*fixed_block_len=*/false, /*apply_cmp_to_bp=*/true, /*coeff_offset=*/1);
134 }
135
136 /*
137 * This multiplies coefficient by 2. To be used with chips which have more BP bits than needed, such that the
138 * most significant BP bit effectively acts as "protect whole chip" flag.
139 */
decode_range_spi25_2x_block(size_t * start,size_t * len,const struct wp_bits * bits,size_t chip_len)140 void decode_range_spi25_2x_block(size_t *start, size_t *len, const struct wp_bits *bits, size_t chip_len)
141 {
142 decode_range_generic(start, len, bits, chip_len,
143 /*fixed_block_len=*/false, /*apply_cmp_to_bp=*/false, /*coeff_offset=*/0);
144 }
145