vboot/vbnv_flash: Binary search to find last used entry

This improves the previous linear search to O(log n).  No change in
storage format.

BUG=chromium:640656
BRANCH=none
TEST=Manual
	(test empty)
	flashrom -i RW_NVRAM -e
	Reboot; device should boot normally.

	(start using records)
	crossystem kern_nv=0xaab0
	crossystem recovery_request=1 && reboot
	Device should go into recovery mode with reason 1
	Reboot again; it should boot normally.
	crossystem kern_nv (should still contain 0xaab0)
	Repeat steps several times with request=2, 3, etc.

	flashrom -i RW_NVRAM -r nvdata
	Modify nvdata to copy the first record across all valid
	records
	flashrom -i RW_NVRAM -w nvdata
	Reboot; device should boot normally.

Change-Id: Ieb97563ab92bd1d18a4f6a9e1d20157efe311fb4
Signed-off-by: Martin Roth <martinroth@chromium.org>
Original-Commit-Id: db9bb2d392
Original-Change-Id: I1eb5fd9fa6b2ae56833f024bcd3c250147bcc7a1
Original-Signed-off-by: Randall Spangler <rspangler@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/376928
Original-Reviewed-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: https://review.coreboot.org/16577
Tested-by: build bot (Jenkins)
Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
This commit is contained in:
Randall Spangler 2016-08-26 16:01:16 -07:00 committed by Patrick Georgi
parent 57bfbb0508
commit aa1d314ac2

View file

@ -65,6 +65,7 @@ static int init_vbnv(void)
struct region_device *rdev = &ctx->vbnv_dev; struct region_device *rdev = &ctx->vbnv_dev;
uint8_t buf[BLOB_SIZE]; uint8_t buf[BLOB_SIZE];
uint8_t empty_blob[BLOB_SIZE]; uint8_t empty_blob[BLOB_SIZE];
int used_below, empty_above;
int offset; int offset;
int i; int i;
@ -78,25 +79,30 @@ static int init_vbnv(void)
for (i = 0; i < BLOB_SIZE; i++) for (i = 0; i < BLOB_SIZE; i++)
empty_blob[i] = erase_value(); empty_blob[i] = erase_value();
offset = 0;
ctx->top_offset = region_device_sz(rdev) - BLOB_SIZE; ctx->top_offset = region_device_sz(rdev) - BLOB_SIZE;
/* /* Binary search for the border between used and empty */
* after the loop, offset is supposed to point the blob right before used_below = 0;
* the first empty blob, the last blob in the nvram if there is no empty_above = region_device_sz(rdev) / BLOB_SIZE;
* empty blob, or the base of the region if the nvram has never been
* used. while (used_below + 1 < empty_above) {
*/ int guess = (used_below + empty_above) / 2;
for (i = 0; i <= ctx->top_offset; i += BLOB_SIZE) { if (rdev_readat(rdev, buf, guess * BLOB_SIZE, BLOB_SIZE) < 0) {
if (rdev_readat(rdev, buf, i, BLOB_SIZE) < 0) {
printk(BIOS_ERR, "failed to read nvdata\n"); printk(BIOS_ERR, "failed to read nvdata\n");
return 1; return 1;
} }
if (!memcmp(buf, empty_blob, BLOB_SIZE)) if (!memcmp(buf, empty_blob, BLOB_SIZE))
break; empty_above = guess;
offset = i; else
used_below = guess;
} }
/*
* Offset points to the last non-empty blob. Or if all blobs are empty
* (nvram is totally erased), point to the first blob.
*/
offset = used_below * BLOB_SIZE;
/* reread the nvdata and write it to the cache */ /* reread the nvdata and write it to the cache */
if (rdev_readat(rdev, ctx->cache, offset, BLOB_SIZE) < 0) { if (rdev_readat(rdev, ctx->cache, offset, BLOB_SIZE) < 0) {
printk(BIOS_ERR, "failed to read nvdata\n"); printk(BIOS_ERR, "failed to read nvdata\n");