diff options
author | Vadim Bendebury <vbendeb@chromium.org> | 2019-08-21 14:26:31 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-08-23 23:50:07 +0000 |
commit | 481da547186bc9989068ceef73ccd1556a911dad (patch) | |
tree | aac2de2f325752c2860e5a1e62f8a71e7445b8d8 /common | |
parent | fe402aec3c8014e16828fee2bfa921eeffbc39fe (diff) | |
download | chrome-ec-481da547186bc9989068ceef73ccd1556a911dad.tar.gz |
nvmem: recover from failure saving object spanning flash pages
In case power was lost when saving an object spanning two pages, the
initialization process does not return the second flash page into the
pages pool, leaving NVMEM in an inconsistent state.
Proper recovery should reinitialize the second page and return it into
the pool of available flash pages.
If a failure like this happens, to recover the initialization sequence
will have to run one extra cycle, first one bringing the NVMEM state
to the previously covered state of the last object in NVMEM corrupted,
but fitting into a page.
A unit test added to verify proper behavior in this situation.
BRANCH=cr50, cr50-mp
BUG=b:139326267
TEST='make run-nvmem -j' passes. Also added temporary code cause reset
when saving the second part of an object spanning two flash
pages. Observed the device properly recover from this failure.
Change-Id: I76ebb6fc73ffc0b07bce34370302f3787914bfb2
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1766092
Reviewed-by: Andrey Pronin <apronin@chromium.org>
Diffstat (limited to 'common')
-rw-r--r-- | common/new_nvmem.c | 48 |
1 files changed, 43 insertions, 5 deletions
diff --git a/common/new_nvmem.c b/common/new_nvmem.c index a9e0435893..aeef5ccf85 100644 --- a/common/new_nvmem.c +++ b/common/new_nvmem.c @@ -1007,6 +1007,12 @@ static enum ec_error_list save_object(const struct nn_container *cont) save_data = (const void *)((uintptr_t)save_data + top_room); save_size -= top_room; start_new_flash_page(save_size); +#if defined(NVMEM_TEST_BUILD) + if (save_size && (failure_mode == TEST_SPANNING_PAGES)) { + ccprintf("%s:%d corrupting...\n", __func__, __LINE__); + return EC_SUCCESS; + } +#endif } if (save_size) { @@ -2134,8 +2140,10 @@ static enum ec_error_list verify_delimiter(struct nn_container *nc) uint8_t i; for (i = 0; i < master_at.list_index; i++) - if (list_element_to_ph(i) == dpt.mt.ph) + if (list_element_to_ph(i) == dpt.mt.ph) { dpt.list_index = i; + break; + } } while ((rv = get_next_object(&dpt, nc, 0)) == EC_SUCCESS) @@ -2150,8 +2158,35 @@ static enum ec_error_list verify_delimiter(struct nn_container *nc) size_t remainder_size; const void *p = page_cursor(&master_at.ct); - remainder_size = - CONFIG_FLASH_BANK_SIZE - master_at.ct.data_offset; + if (dpt.ct.ph != dpt.mt.ph) { + /* + * The last retrieved object is spanning flash page + * boundary. + * + * If this is not the last object in the flash, this + * is an unrecoverable init failure. + */ + if ((dpt.mt.ph != master_at.mt.ph) || + (list_element_to_ph(dpt.list_index - 1) != + dpt.ct.ph)) + report_no_payload_failure( + NVMEMF_CORRUPTED_INIT); + /* + * Let's erase the page where the last object spilled + * into. + */ + flash_physical_erase((uintptr_t)dpt.mt.ph - + CONFIG_PROGRAM_MEMORY_BASE, + CONFIG_FLASH_BANK_SIZE); + /* + * And move it to the available pages part of the + * pages list. + */ + master_at.list_index -= 1; + master_at.mt = dpt.ct; + } + + remainder_size = CONFIG_FLASH_BANK_SIZE - dpt.ct.data_offset; memset(nc, 0, remainder_size); write_to_flash(p, nc, remainder_size); /* Make sure compaction starts with the new page. */ @@ -2181,8 +2216,11 @@ static enum ec_error_list retrieve_nvmem_contents(void) /* No saved object will exceed CONFIG_FLASH_BANK_SIZE in size. */ nc = get_scratch_buffer(CONFIG_FLASH_BANK_SIZE); - /* Depending on the state of flash, we might have to do this twice. */ - for (tries = 0; tries < 2; tries++) { + /* + * Depending on the state of flash, we might have to do this three + * times. + */ + for (tries = 0; tries < 3; tries++) { memset(&master_at, 0, sizeof(master_at)); memset(nvmem_cache_base(NVMEM_TPM), 0, nvmem_user_sizes[NVMEM_TPM]); |