summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorVadim Bendebury <vbendeb@chromium.org>2019-08-21 14:26:31 -0700
committerCommit Bot <commit-bot@chromium.org>2019-08-23 23:50:07 +0000
commit481da547186bc9989068ceef73ccd1556a911dad (patch)
treeaac2de2f325752c2860e5a1e62f8a71e7445b8d8 /common
parentfe402aec3c8014e16828fee2bfa921eeffbc39fe (diff)
downloadchrome-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.c48
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]);