summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/new_nvmem.c48
-rw-r--r--include/flash_log.h3
-rw-r--r--test/nvmem.c22
-rw-r--r--test/nvmem_test.h3
4 files changed, 69 insertions, 7 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]);
diff --git a/include/flash_log.h b/include/flash_log.h
index bb4baf651c..64ee2703e3 100644
--- a/include/flash_log.h
+++ b/include/flash_log.h
@@ -64,7 +64,8 @@ enum nvmem_failure_type {
NVMEMF_SECTION_VERIFY = 9,
NVMEMF_PRE_ERASE_MISMATCH = 10,
NVMEMF_PAGE_LIST_OVERFLOW = 11,
- NVMEMF_CIPHER_ERROR = 12
+ NVMEMF_CIPHER_ERROR = 12,
+ NVMEMF_CORRUPTED_INIT = 13
};
/* Not all nvmem failures require payload. */
diff --git a/test/nvmem.c b/test/nvmem.c
index 7bac2f9dd7..4e7c3600be 100644
--- a/test/nvmem.c
+++ b/test/nvmem.c
@@ -834,6 +834,7 @@ static int test_nvmem_incomplete_transaction(void)
size_t num_objects;
uint8_t buf[nvmem_user_sizes[NVMEM_TPM]];
uint8_t *p;
+ size_t object_size;
TEST_ASSERT(prepare_post_migration_nvmem() == EC_SUCCESS);
num_objects = fill_obj_offsets(offsets, ARRAY_SIZE(offsets));
@@ -878,6 +879,27 @@ static int test_nvmem_incomplete_transaction(void)
/* And verify that nvmem can still successfully initialize. */
TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+ /*
+ * Now let's interrupt saving an object spanning two pages.
+ *
+ * First, fill up the current page to get close to the limit such that
+ * the next save will have to span two flash pages.
+ */
+ object_size = offsets[4] - offsets[3];
+ p = (uint8_t *)evictable_offs_to_addr(offsets[3]) + object_size - 10;
+ while ((master_at.mt.data_offset + object_size +
+ sizeof(struct nn_container)) <= CONFIG_FLASH_BANK_SIZE) {
+ (*p)++;
+ new_nvmem_save();
+ }
+
+ /* This will trigger spilling over the page boundary. */
+ (*p)++;
+ failure_mode = TEST_SPANNING_PAGES;
+ new_nvmem_save();
+ failure_mode = TEST_NO_FAILURE;
+ TEST_ASSERT(nvmem_init() == EC_SUCCESS);
+
return EC_SUCCESS;
}
diff --git a/test/nvmem_test.h b/test/nvmem_test.h
index 6aba855bfe..58775d4bcb 100644
--- a/test/nvmem_test.h
+++ b/test/nvmem_test.h
@@ -20,7 +20,8 @@ enum test_failure_mode {
TEST_FAIL_WHEN_COMPACTING,
TEST_FAIL_SAVING_VAR,
TEST_FAIL_FINALIZING_VAR,
- TEST_FAILED_HASH
+ TEST_FAILED_HASH,
+ TEST_SPANNING_PAGES
};
extern enum test_failure_mode failure_mode;