Nikolai Artemiev has uploaded this change for review.

View Change

[RFC] writeprotect: implement wp_list_ranges()

BUG=b:195381327,b:153800563
TEST=flashrom --wp-list
BRANCH=none

Change-Id: Id51f038f03305c8536d80313e52f77d27835f34d
Signed-off-by: Nikolai Artemiev <nartemiev@google.com>
---
M writeprotect.c
1 file changed, 145 insertions(+), 2 deletions(-)

git pull ssh://review.coreboot.org:29418/flashrom refs/changes/81/58481/1
diff --git a/writeprotect.c b/writeprotect.c
index 1ca9439..884e4d6 100644
--- a/writeprotect.c
+++ b/writeprotect.c
@@ -154,6 +154,109 @@
}
void *suppress_unused_warning_for_write_wp_chip_state = write_wp_chip_state;

+struct wp_range_wpst_pair {
+ struct wp_range range;
+ struct wp_chip_state wpst;
+};
+
+static int range_sort_compare_func(const void *aa, const void *bb)
+{
+ const struct wp_range_wpst_pair
+ *a = (const struct wp_range_wpst_pair *)aa,
+ *b = (const struct wp_range_wpst_pair *)bb;
+
+ int ord = 0;
+
+ /* List shorter ranges first. */
+ if (ord == 0) ord = a->range.len - b->range.len;
+
+ /* For equal length ranges, list the one with a lower start address
+ * first. */
+ if (ord == 0) ord = a->range.start - b->range.start;
+
+ /* Ranges a and b are identical, order them by the status/config
+ * register bits that are used to select them. This ensures consistent
+ * ordering when multiple status register settings can select the same
+ * range. */
+ if (ord == 0) ord = a->wpst.cmp - b->wpst.cmp;
+ if (ord == 0) ord = a->wpst.sec - b->wpst.sec;
+ if (ord == 0) ord = a->wpst.tb - b->wpst.tb;
+ if (ord == 0) ord = a->wpst.bp - b->wpst.bp;
+
+ return ord;
+}
+
+static bool can_write_bit(const struct reg_bit_info bit)
+{
+ return bit.reg != INVALID_REG && bit.writability == RW;
+}
+
+static int get_available_ranges(
+ struct flashctx *flash,
+ struct wp_range_wpst_pair **ranges,
+ size_t *range_count)
+{
+ const struct reg_bit_map *bits = &flash->chip->reg_bits;
+ struct wp_chip_state wpst;
+
+ /* Read chip the chip's current configuration. RW bits in the wpst
+ * structure will be overwritten while enumerating ranges, but RO/OTP
+ * bits will be preserved so that ranges are only enumerated if they
+ * can actually be selected on the chip. */
+ if (read_wp_chip_state(flash, &wpst))
+ return 1;
+
+ /* Create a list of RW bits that affect the chip's protection range. */
+ uint8_t *range_bits[20];
+ size_t bit_count = 0;
+ for (size_t i = 0; can_write_bit(bits->bp[i]); i++) {
+ range_bits[bit_count++] = &wpst.bp[i];
+ }
+ if (can_write_bit(bits->tb)) range_bits[bit_count++] = &wpst.tb;
+ if (can_write_bit(bits->sec)) range_bits[bit_count++] = &wpst.sec;
+ if (can_write_bit(bits->cmp)) range_bits[bit_count++] = &wpst.cmp;
+
+ /* Allocate output array */
+ *range_count = (1 << bit_count);
+ *ranges = calloc(*range_count, sizeof(**ranges));
+
+ /* Enumerate all values the range bits can take and find the range
+ * associated with each one. */
+ for(uint32_t range_index = 0; range_index < *range_count; range_index++) {
+ /* Extract bits from the range index and assign them to bits in
+ * the wpst structure. */
+ for (size_t i = 0; i < bit_count; i++) {
+ *range_bits[i] = (range_index >> i) & 1;
+ }
+
+ /* Get range using chip-specific decoding function */
+ (*ranges)[range_index].wpst = wpst;
+ (*ranges)[range_index].range.chip_len = flash->chip->total_size * 1024;
+ flash->chip->decode_range(&wpst, &(*ranges)[range_index].range);
+ }
+
+ /* Sort ranges */
+ qsort(*ranges, *range_count, sizeof(**ranges), range_sort_compare_func);
+
+ /* Remove duplicates */
+ size_t last = 0;
+ for(size_t i = 0; i < *range_count; i++) {
+ struct wp_range *range = &(*ranges)[i].range;
+ struct wp_range *last_range = &(*ranges)[last].range;
+
+ bool duplicate =
+ range->start == last_range->start &&
+ range->len == last_range->len;
+
+ if (!duplicate) {
+ (*ranges)[++last] = (*ranges)[i];
+ }
+ }
+ *range_count = last + 1;
+
+ return 0;
+}
+
bool wp_supported(struct flashctx *flash)
{
return flash->chip->decode_range != NULL;
@@ -189,10 +292,50 @@
return 1;
}

+static void print_range(struct wp_range *range)
+{
+ /* Print start address and length */
+ msg_ginfo("start=0x%08x length=0x%08x ", range->start, range->len);
+
+ /* Print easily readable description like 'none' or 'lower 1/8' */
+ if (range->len == 0) {
+ msg_ginfo("(none)");
+ }
+ else if (range->len == range->chip_len) {
+ msg_ginfo("(all)");
+ }
+ else {
+ uint32_t chip_len = range->chip_len;
+ uint32_t range_len = range->len;
+
+ /* Remove common factors of 2 to simplify range length
+ * fraction. */
+ while ((chip_len % 2) == 0 && (range_len % 2) == 0) {
+ chip_len /= 2;
+ range_len /= 2;
+ }
+
+ const char *location = (range->start == 0) ? "lower" : "upper";
+ msg_ginfo("(%s %d/%d)", location, range_len, chip_len);
+ }
+}
+
int wp_list_ranges(struct flashctx *flash)
{
- /* TODO */
- return 1;
+ struct wp_range_wpst_pair *ranges;
+ size_t range_count = 0;
+
+ if (get_available_ranges(flash, &ranges, &range_count))
+ return 1;
+
+ for (size_t i = 0; i < range_count; i++) {
+ print_range(&ranges[i].range);
+ msg_ginfo("\n");
+ }
+
+ free(ranges);
+
+ return 0;
}

int wp_print_status(struct flashctx *flash)

To view, visit change 58481. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: Id51f038f03305c8536d80313e52f77d27835f34d
Gerrit-Change-Number: 58481
Gerrit-PatchSet: 1
Gerrit-Owner: Nikolai Artemiev <nartemiev@google.com>
Gerrit-MessageType: newchange