Nikolai Artemiev has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/58481 )
Change subject: [RFC] writeprotect: implement wp_list_ranges() ......................................................................
[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)