[sw/silicon_creator] Add flash_ctrl_data_erase_verify Signed-off-by: Alphan Ulusoy <alphan@google.com>
diff --git a/sw/device/silicon_creator/lib/drivers/flash_ctrl.c b/sw/device/silicon_creator/lib/drivers/flash_ctrl.c index 3c15c1e..1141018 100644 --- a/sw/device/silicon_creator/lib/drivers/flash_ctrl.c +++ b/sw/device/silicon_creator/lib/drivers/flash_ctrl.c
@@ -400,6 +400,50 @@ return wait_for_done(kErrorFlashCtrlDataErase); } +rom_error_t flash_ctrl_data_erase_verify(uint32_t addr, + flash_ctrl_erase_type_t erase_type) { + static_assert(__builtin_popcount(FLASH_CTRL_PARAM_BYTES_PER_BANK) == 1, + "Bytes per bank must be a power of two."); + static_assert(__builtin_popcount(FLASH_CTRL_PARAM_BYTES_PER_PAGE) == 1, + "Bytes per page must be a power of two."); + + size_t byte_count; + rom_error_t error = kErrorFlashCtrlDataEraseVerify; + switch (launder32(erase_type)) { + case kFlashCtrlEraseTypeBank: + HARDENED_CHECK_EQ(erase_type, kFlashCtrlEraseTypeBank); + byte_count = FLASH_CTRL_PARAM_BYTES_PER_BANK; + error = kErrorOk ^ (byte_count - 1); + break; + case kFlashCtrlEraseTypePage: + HARDENED_CHECK_EQ(erase_type, kFlashCtrlEraseTypePage); + byte_count = FLASH_CTRL_PARAM_BYTES_PER_PAGE; + error = kErrorOk ^ (byte_count - 1); + break; + default: + HARDENED_UNREACHABLE(); + } + + // Truncate to the closest lower bank/page aligned address. + addr &= ~byte_count + 1; + uint32_t mask = kFlashCtrlErasedWord; + size_t i = 0; + for (; launder32(i) < byte_count; i += sizeof(uint32_t)) { + uint32_t word = + abs_mmio_read32(TOP_EARLGREY_FLASH_CTRL_MEM_BASE_ADDR + addr + i); + mask &= word; + error &= word; + } + HARDENED_CHECK_EQ(i, byte_count); + + if (launder32(mask) == kFlashCtrlErasedWord) { + HARDENED_CHECK_EQ(mask, kFlashCtrlErasedWord); + return error ^ (byte_count - 1); + } + + return kErrorFlashCtrlDataEraseVerify; +} + rom_error_t flash_ctrl_info_erase(flash_ctrl_info_page_t info_page, flash_ctrl_erase_type_t erase_type) { const uint32_t addr = info_page_addr(info_page);
diff --git a/sw/device/silicon_creator/lib/drivers/flash_ctrl.h b/sw/device/silicon_creator/lib/drivers/flash_ctrl.h index b697c07..dea4a41 100644 --- a/sw/device/silicon_creator/lib/drivers/flash_ctrl.h +++ b/sw/device/silicon_creator/lib/drivers/flash_ctrl.h
@@ -314,6 +314,16 @@ flash_ctrl_erase_type_t erase_type); /** + * Verifies that a data partition page or bank was erased. + * + * @param addr Address that falls within the bank or page erased. + * @param erase_type Whether to verify a page or a bank. + * @return Result of the operation. + */ +rom_error_t flash_ctrl_data_erase_verify(uint32_t addr, + flash_ctrl_erase_type_t erase_type); + +/** * Erases an information partition page or bank. * * @param info_page Information page to erase for page erases, or a page within
diff --git a/sw/device/silicon_creator/lib/drivers/flash_ctrl_unittest.cc b/sw/device/silicon_creator/lib/drivers/flash_ctrl_unittest.cc index a03c443..f3acd25 100644 --- a/sw/device/silicon_creator/lib/drivers/flash_ctrl_unittest.cc +++ b/sw/device/silicon_creator/lib/drivers/flash_ctrl_unittest.cc
@@ -605,5 +605,89 @@ flash_ctrl_creator_info_pages_lockdown(); } +struct EraseVerifyCase { + /** + * Address. + */ + uint32_t addr; + /** + * Truncated address aligned to closest lower page/bank. + */ + uint32_t aligned_addr; + /** + * Erase type. + */ + flash_ctrl_erase_type_t erase_type; + /** + * Value of the last word read from flash (for testing failure cases). + */ + uint32_t last_word_val; + /** + * Expected return value. + */ + rom_error_t error; +}; + +class EraseVerifyTest : public FlashCtrlTest, + public testing::WithParamInterface<EraseVerifyCase> {}; + +TEST_P(EraseVerifyTest, DataEraseVerify) { + size_t byte_count; + switch (GetParam().erase_type) { + case kFlashCtrlEraseTypeBank: + byte_count = FLASH_CTRL_PARAM_BYTES_PER_BANK; + break; + case kFlashCtrlEraseTypePage: + byte_count = FLASH_CTRL_PARAM_BYTES_PER_PAGE; + break; + default: + FAIL(); + } + + size_t i = 0; + for (; i < byte_count - sizeof(uint32_t); i += sizeof(uint32_t)) { + EXPECT_ABS_READ32( + TOP_EARLGREY_FLASH_CTRL_MEM_BASE_ADDR + GetParam().aligned_addr + i, + kFlashCtrlErasedWord); + } + EXPECT_ABS_READ32( + TOP_EARLGREY_FLASH_CTRL_MEM_BASE_ADDR + GetParam().aligned_addr + i, + GetParam().last_word_val); + + EXPECT_EQ( + flash_ctrl_data_erase_verify(GetParam().addr, GetParam().erase_type), + GetParam().error); +} + +INSTANTIATE_TEST_SUITE_P( + AllCases, EraseVerifyTest, + testing::Values( + // Verify first page. + EraseVerifyCase{ + .addr = 0, + .aligned_addr = 0, + .erase_type = kFlashCtrlEraseTypePage, + .last_word_val = kFlashCtrlErasedWord, + .error = kErrorOk, + }, + // Verify 10th page, unaligned address. + EraseVerifyCase{ + .addr = 10 * FLASH_CTRL_PARAM_BYTES_PER_PAGE + 128, + .aligned_addr = 10 * FLASH_CTRL_PARAM_BYTES_PER_PAGE, + .erase_type = kFlashCtrlEraseTypePage, + .last_word_val = kFlashCtrlErasedWord, + .error = kErrorOk, + }, + // Fail to verify 10th page, unaligned address. + EraseVerifyCase{ + .addr = 10 * FLASH_CTRL_PARAM_BYTES_PER_PAGE + 128, + .aligned_addr = 10 * FLASH_CTRL_PARAM_BYTES_PER_PAGE, + .erase_type = kFlashCtrlEraseTypePage, + .last_word_val = 0xfffffff0, + .error = kErrorFlashCtrlDataEraseVerify, + } // Note: No cases for bank erases since the test times out due to + // large number of expectations. + )); + } // namespace } // namespace flash_ctrl_unittest
diff --git a/sw/device/silicon_creator/lib/error.h b/sw/device/silicon_creator/lib/error.h index 018dfc6..b385dae 100644 --- a/sw/device/silicon_creator/lib/error.h +++ b/sw/device/silicon_creator/lib/error.h
@@ -100,6 +100,7 @@ X(kErrorFlashCtrlInfoWrite, ERROR_(4, kModuleFlashCtrl, kInternal)), \ X(kErrorFlashCtrlDataErase, ERROR_(5, kModuleFlashCtrl, kInternal)), \ X(kErrorFlashCtrlInfoErase, ERROR_(6, kModuleFlashCtrl, kInternal)), \ + X(kErrorFlashCtrlDataEraseVerify, ERROR_(7, kModuleFlashCtrl, kInternal)), \ X(kErrorSecMmioRegFileSize, ERROR_(0, kModuleSecMmio, kResourceExhausted)), \ X(kErrorSecMmioReadFault, ERROR_(1, kModuleSecMmio, kInternal)), \ X(kErrorSecMmioWriteFault, ERROR_(2, kModuleSecMmio, kInternal)), \