aboutsummaryrefslogtreecommitdiffstats
path: root/writeprotect.c
diff options
context:
space:
mode:
Diffstat (limited to 'writeprotect.c')
-rw-r--r--writeprotect.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/writeprotect.c b/writeprotect.c
index ca9d9ecf..b619a242 100644
--- a/writeprotect.c
+++ b/writeprotect.c
@@ -158,6 +158,162 @@ static enum flashrom_wp_result get_wp_range(struct wp_range *range, struct flash
return FLASHROM_WP_OK;
}
+/** Write protect bit values and the range they will activate. */
+struct wp_range_and_bits {
+ struct wp_bits bits;
+ struct wp_range range;
+};
+
+/**
+ * Comparator used for sorting ranges in get_ranges_and_wp_bits().
+ *
+ * Ranges are ordered by these attributes, in decreasing significance:
+ * (range length, range start, cmp bit, sec bit, tb bit, bp bits)
+ */
+static int compare_ranges(const void *aa, const void *bb)
+{
+ const struct wp_range_and_bits
+ *a = (const struct wp_range_and_bits *)aa,
+ *b = (const struct wp_range_and_bits *)bb;
+
+ int ord = 0;
+
+ if (ord == 0)
+ ord = a->range.len - b->range.len;
+
+ if (ord == 0)
+ ord = a->range.start - b->range.start;
+
+ if (ord == 0)
+ ord = a->bits.cmp - b->bits.cmp;
+
+ if (ord == 0)
+ ord = a->bits.sec - b->bits.sec;
+
+ if (ord == 0)
+ ord = a->bits.tb - b->bits.tb;
+
+ for (int i = a->bits.bp_bit_count - 1; i >= 0; i--) {
+ if (ord == 0)
+ ord = a->bits.bp[i] - b->bits.bp[i];
+ }
+
+ return ord;
+}
+
+static bool can_write_bit(const struct reg_bit_info bit)
+{
+ /*
+ * TODO: check if the programmer supports writing the register that the
+ * bit is in. For example, some chipsets may only allow SR1 to be
+ * written.
+ */
+
+ return bit.reg != INVALID_REG && bit.writability == RW;
+}
+
+/**
+ * Enumerate all protection ranges that the chip supports and that are able to
+ * be activated, given limitations such as OTP bits or programmer-enforced
+ * restrictions. Returns a list of deduplicated wp_range_and_bits structures.
+ *
+ * Allocates a buffer that must be freed by the caller with free().
+ */
+static enum flashrom_wp_result get_ranges_and_wp_bits(struct flashctx *flash, struct wp_bits bits, struct wp_range_and_bits **ranges, size_t *count)
+{
+ const struct reg_bit_map *reg_bits = &flash->chip->reg_bits;
+ /*
+ * Create a list of bits that affect the chip's protection range in
+ * range_bits. Each element is a pointer to a member of the wp_bits
+ * structure that will be modified.
+ *
+ * Some chips have range bits that cannot be changed (e.g. MX25L6473E
+ * has a one-time programmable TB bit). Rather than enumerating all
+ * possible values for unwritable bits, just read their values from the
+ * chip to ensure we only enumerate ranges that are actually available.
+ */
+ uint8_t *range_bits[ARRAY_SIZE(bits.bp) + 1 /* TB */ + 1 /* SEC */ + 1 /* CMP */];
+ size_t bit_count = 0;
+
+ for (size_t i = 0; i < ARRAY_SIZE(bits.bp); i++) {
+ if (can_write_bit(reg_bits->bp[i]))
+ range_bits[bit_count++] = &bits.bp[i];
+ }
+
+ if (can_write_bit(reg_bits->tb))
+ range_bits[bit_count++] = &bits.tb;
+
+ if (can_write_bit(reg_bits->sec))
+ range_bits[bit_count++] = &bits.sec;
+
+ if (can_write_bit(reg_bits->cmp))
+ range_bits[bit_count++] = &bits.cmp;
+
+ /* Allocate output buffer */
+ *count = 1 << bit_count;
+ *ranges = calloc(*count, sizeof(struct wp_range_and_bits));
+
+ for (size_t range_index = 0; range_index < *count; range_index++) {
+ /*
+ * Extract bits from the range index and assign them to members
+ * of the wp_bits structure. The loop bounds ensure that all
+ * bit combinations will be enumerated.
+ */
+ for (size_t i = 0; i < bit_count; i++)
+ *range_bits[i] = (range_index >> i) & 1;
+
+ struct wp_range_and_bits *output = &(*ranges)[range_index];
+
+ output->bits = bits;
+ enum flashrom_wp_result ret = get_wp_range(&output->range, flash, &bits);
+ if (ret != FLASHROM_WP_OK) {
+ free(*ranges);
+ return ret;
+ }
+
+ /* Debug: print range bits and range */
+ msg_gspew("Enumerated range: ");
+ if (bits.cmp_bit_present)
+ msg_gspew("CMP=%u ", bits.cmp);
+ if (bits.sec_bit_present)
+ msg_gspew("SEC=%u ", bits.sec);
+ if (bits.tb_bit_present)
+ msg_gspew("TB=%u ", bits.tb);
+ for (size_t i = 0; i < bits.bp_bit_count; i++) {
+ size_t j = bits.bp_bit_count - i - 1;
+ msg_gspew("BP%zu=%u ", j, bits.bp[j]);
+ }
+ msg_gspew(" start=0x%08zx length=0x%08zx ",
+ output->range.start, output->range.len);
+ }
+
+ /* Sort ranges. Ensures consistency if there are duplicate ranges. */
+ qsort(*ranges, *count, sizeof(struct wp_range_and_bits), compare_ranges);
+
+ /* Remove duplicates */
+ size_t output_index = 0;
+ struct wp_range *last_range = NULL;
+
+ for (size_t i = 0; i < *count; i++) {
+ bool different_to_last =
+ (last_range == NULL) ||
+ ((*ranges)[i].range.start != last_range->start) ||
+ ((*ranges)[i].range.len != last_range->len);
+
+ if (different_to_last) {
+ /* Move range to the next free position */
+ (*ranges)[output_index] = (*ranges)[i];
+ output_index++;
+ /* Keep track of last non-duplicate range */
+ last_range = &(*ranges)[i].range;
+ }
+ }
+ /* Reduce count to only include non-duplicate ranges */
+ *count = output_index;
+
+ return FLASHROM_WP_OK;
+}
+
static bool chip_supported(struct flashctx *flash)
{
return (flash->chip != NULL) && (flash->chip->decode_range != NULL);
@@ -218,4 +374,41 @@ enum flashrom_wp_result wp_write_cfg(struct flashctx *flash, const struct flashr
return ret;
}
+enum flashrom_wp_result wp_get_available_ranges(struct flashrom_wp_ranges **list, struct flashrom_flashctx *flash)
+{
+ struct wp_bits bits;
+ struct wp_range_and_bits *range_pairs = NULL;
+ size_t count;
+
+ if (!chip_supported(flash))
+ return FLASHROM_WP_ERR_CHIP_UNSUPPORTED;
+
+ enum flashrom_wp_result ret = read_wp_bits(&bits, flash);
+ if (ret != FLASHROM_WP_OK)
+ return ret;
+
+ ret = get_ranges_and_wp_bits(flash, bits, &range_pairs, &count);
+ if (ret != FLASHROM_WP_OK)
+ return ret;
+
+ *list = calloc(1, sizeof(struct flashrom_wp_ranges));
+ struct wp_range *ranges = calloc(count, sizeof(struct wp_range));
+
+ if (!(*list) || !ranges) {
+ free(*list);
+ free(ranges);
+ ret = FLASHROM_WP_ERR_OTHER;
+ goto out;
+ }
+ (*list)->count = count;
+ (*list)->ranges = ranges;
+
+ for (size_t i = 0; i < count; i++)
+ ranges[i] = range_pairs[i].range;
+
+out:
+ free(range_pairs);
+ return ret;
+}
+
/** @} */ /* end flashrom-wp */