aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libflashrom.c73
-rw-r--r--libflashrom.h6
-rw-r--r--writeprotect.c193
-rw-r--r--writeprotect.h9
4 files changed, 281 insertions, 0 deletions
diff --git a/libflashrom.c b/libflashrom.c
index f0b9cfda..f2288fef 100644
--- a/libflashrom.c
+++ b/libflashrom.c
@@ -747,4 +747,77 @@ enum flashrom_wp_result flashrom_wp_read_cfg(struct flashrom_wp_cfg *cfg, struct
return FLASHROM_WP_ERR_OTHER;
}
+/**
+ * @brief Get a list of protection ranges supported by the flash chip.
+ *
+ * @param[out] ranges Points to a pointer of type struct flashrom_wp_ranges
+ * that will be set if available ranges are found. Finding
+ * available ranges may not always be possible, even if the
+ * chip's protection range can be read or modified. *ranges
+ * must be freed using @ref flashrom_wp_ranges_free.
+ * @param[in] flash The flash context used to access the chip.
+ * @return 0 on success
+ * >0 on failure
+ */
+enum flashrom_wp_result flashrom_wp_get_available_ranges(struct flashrom_wp_ranges **list, struct flashrom_flashctx *flash)
+{
+ /*
+ * TODO: Call custom implementation if the programmer is opaque, as
+ * direct WP operations require SPI access. We actually can't implement
+ * this in linux_mtd right now, but we should adopt a proper generic
+ * architechure to match the read and write functions anyway.
+ */
+ if (flash->mst->buses_supported & BUS_SPI)
+ return wp_get_available_ranges(list, flash);
+
+ return FLASHROM_WP_ERR_OTHER;
+}
+
+/**
+ * @brief Get a number of protection ranges in a range list.
+ *
+ * @param[in] ranges The range list to get the count from.
+ * @return Number of ranges in the list.
+ */
+size_t flashrom_wp_ranges_get_count(const struct flashrom_wp_ranges *list)
+{
+ return list->count;
+}
+
+/**
+ * @brief Get a protection range from a range list.
+ *
+ * @param[out] start Points to a size_t to write the range's start to.
+ * @param[out] len Points to a size_t to write the range's length to.
+ * @param[in] ranges The range list to get the range from.
+ * @param[in] index Index of the range to get.
+ * @return 0 on success
+ * >0 on failure
+ */
+enum flashrom_wp_result flashrom_wp_ranges_get_range(size_t *start, size_t *len, const struct flashrom_wp_ranges *list, unsigned int index)
+{
+ if (index >= list->count)
+ return FLASHROM_WP_ERR_OTHER;
+
+ *start = list->ranges[index].start;
+ *len = list->ranges[index].len;
+
+ return 0;
+}
+
+/**
+ * @brief Free a WP range list.
+ *
+ * @param[out] cfg Pointer to the flashrom_wp_ranges to free.
+ */
+void flashrom_wp_ranges_release(struct flashrom_wp_ranges *list)
+{
+ if (!list)
+ return;
+
+ free(list->ranges);
+ free(list);
+}
+
+
/** @} */ /* end flashrom-wp */
diff --git a/libflashrom.h b/libflashrom.h
index 6d02d50f..c9138da2 100644
--- a/libflashrom.h
+++ b/libflashrom.h
@@ -137,6 +137,7 @@ enum flashrom_wp_mode {
FLASHROM_WP_MODE_PERMANENT
};
struct flashrom_wp_cfg;
+struct flashrom_wp_ranges;
enum flashrom_wp_result flashrom_wp_cfg_new(struct flashrom_wp_cfg **);
void flashrom_wp_cfg_release(struct flashrom_wp_cfg *);
@@ -148,4 +149,9 @@ void flashrom_wp_get_range(size_t *start, size_t *len, const struct flashrom_wp_
enum flashrom_wp_result flashrom_wp_read_cfg(struct flashrom_wp_cfg *, struct flashrom_flashctx *);
enum flashrom_wp_result flashrom_wp_write_cfg(struct flashrom_flashctx *, const struct flashrom_wp_cfg *);
+enum flashrom_wp_result flashrom_wp_get_available_ranges(struct flashrom_wp_ranges **, struct flashrom_flashctx *);
+size_t flashrom_wp_ranges_get_count(const struct flashrom_wp_ranges *);
+enum flashrom_wp_result flashrom_wp_ranges_get_range(size_t *start, size_t *len, const struct flashrom_wp_ranges *, unsigned int index);
+void flashrom_wp_ranges_release(struct flashrom_wp_ranges *);
+
#endif /* !__LIBFLASHROM_H__ */
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 */
diff --git a/writeprotect.h b/writeprotect.h
index d54befad..e27403dc 100644
--- a/writeprotect.h
+++ b/writeprotect.h
@@ -37,6 +37,12 @@ struct flashrom_wp_cfg {
struct wp_range range;
};
+/* Collection of multiple write protection ranges. */
+struct flashrom_wp_ranges {
+ struct wp_range *ranges;
+ size_t count;
+};
+
/*
* Description of a chip's write protection configuration.
*
@@ -77,4 +83,7 @@ enum flashrom_wp_result wp_write_cfg(struct flashrom_flashctx *, const struct fl
/* Read WP configuration from the chip */
enum flashrom_wp_result wp_read_cfg(struct flashrom_wp_cfg *, struct flashrom_flashctx *);
+/* Get a list of protection ranges supported by the chip */
+enum flashrom_wp_result wp_get_available_ranges(struct flashrom_wp_ranges **, struct flashrom_flashctx *);
+
#endif /* !__WRITEPROTECT_H__ */