diff --git a/TESTS/storage_abstraction/basicAPI/basicAPI.cpp b/TESTS/storage_abstraction/basicAPI/basicAPI.cpp index 05fbe6d896f..f6ab4351e08 100644 --- a/TESTS/storage_abstraction/basicAPI/basicAPI.cpp +++ b/TESTS/storage_abstraction/basicAPI/basicAPI.cpp @@ -32,8 +32,8 @@ using namespace utest::v1; -extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0); -ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0); +extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F; +ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_MTD_K64F; /* temporary buffer to hold data for testing. */ static const unsigned BUFFER_SIZE = 16384; @@ -112,6 +112,7 @@ void test_getInfo() TEST_ASSERT_EQUAL(0, info.security.reserved1); TEST_ASSERT_EQUAL(0, info.security.reserved2); + TEST_ASSERT((info.program_cycles == ARM_STORAGE_PROGRAM_CYCLES_INFINITE) || (info.program_cycles > 0)); TEST_ASSERT(info.total_storage > 0); } @@ -373,7 +374,7 @@ control_t test_programDataUsingProgramUnit(const size_t call_count) return (call_count < REPEAT_INSTANCES) ? CaseTimeout(200) + CaseRepeatAll: CaseTimeout(200); } else { TEST_ASSERT_EQUAL(firstBlock.attributes.erase_unit, rc); - verifyBytePattern(addr, firstBlock.attributes.erase_unit, (uint8_t)0xFF); + verifyBytePattern(addr, firstBlock.attributes.erase_unit, info.erased_value ? (uint8_t)0xFF : (uint8_t)0); static const uint32_t BYTE_PATTERN = 0xAA551122; size_t sizeofData = info.program_unit; @@ -495,7 +496,7 @@ control_t test_programDataUsingOptimalProgramUnit(const size_t call_count) return (call_count < REPEAT_INSTANCES) ? CaseTimeout(200) + CaseRepeatAll: CaseTimeout(200); } else { TEST_ASSERT_EQUAL(firstBlock.attributes.erase_unit, rc); - verifyBytePattern(addr, firstBlock.attributes.erase_unit, (uint8_t)0xFF); + verifyBytePattern(addr, firstBlock.attributes.erase_unit, info.erased_value ? (uint8_t)0xFF : (uint8_t)0); static const uint8_t BYTE_PATTERN = 0xAA; size_t sizeofData = info.optimal_program_unit; @@ -579,10 +580,12 @@ void eraseCompleteCallback(int32_t status, ARM_STORAGE_OPERATION operation) const uint64_t addr = firstBlock.addr + eraseIteration * ERASE_UNITS_PER_ITERATION * firstBlock.attributes.erase_unit; ++eraseIteration; -#ifndef __CC_ARM - printf("testing erased sector at addr %lu\n", (uint32_t)addr); -#endif - verifyBytePattern(addr, ERASE_UNITS_PER_ITERATION * firstBlock.attributes.erase_unit, (uint8_t)0xFF); + ARM_STORAGE_INFO info; + int32_t rc = drv->GetInfo(&info); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + + //printf("testing erased sector at addr %lu", (uint32_t)addr); + verifyBytePattern(addr, ERASE_UNITS_PER_ITERATION * firstBlock.attributes.erase_unit, info.erased_value ? (uint8_t)0xFF : (uint8_t)0); Harness::validate_callback(); } @@ -628,6 +631,10 @@ control_t test_erase(const size_t call_count) } else { TEST_ASSERT_EQUAL(ERASE_UNITS_PER_ITERATION * firstBlock.attributes.erase_unit, rc); + ARM_STORAGE_INFO info; + rc = drv->GetInfo(&info); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + /* test that the actual sector has been erased */ printf("testing erased sector at addr %lu\n", (uint32_t)addr); verifyBytePattern(addr, ERASE_UNITS_PER_ITERATION * firstBlock.attributes.erase_unit, (uint8_t)0xFF); @@ -708,8 +715,12 @@ control_t test_eraseAll(const size_t call_count) unsigned index = 0; static const unsigned MAX_VERIFY_ITERATIONS = 5; while ((index < MAX_VERIFY_ITERATIONS) && (addr < (firstBlock.addr + firstBlock.size))) { - printf("testing erased chip at addr %lu\n", (uint32_t)addr); - verifyBytePattern(addr, firstBlock.attributes.erase_unit, (uint8_t)0xFF); + //printf("testing erased chip at addr %lu", (uint32_t)addr); + ARM_STORAGE_INFO info; + rc = drv->GetInfo(&info); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + + verifyBytePattern(addr, firstBlock.attributes.erase_unit, info.erased_value ? (uint8_t)0xFF : (uint8_t)0); index++; addr += firstBlock.attributes.erase_unit; diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/add_del/add_del.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/add_del/add_del.cpp index f55a9998cec..91035333242 100644 --- a/features/storage/FEATURE_STORAGE/TESTS/cfstore/add_del/add_del.cpp +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/add_del/add_del.cpp @@ -279,7 +279,7 @@ control_t cfstore_add_del_test_04(const size_t call_count) return CaseNext; } -/** @brief Delete and attribute after an internal realloc of the cfstore memory area +/** @brief Delete an attribute after an internal realloc of the cfstore memory area * * This test case goes through the following steps: * 1. Creates attribute att_1 of size x, and write some data. This causes an internal diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/example1/example1.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example1/example1.cpp index 6b017b3fcc9..b7c4d0e05a3 100644 --- a/features/storage/FEATURE_STORAGE/TESTS/cfstore/example1/example1.cpp +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example1/example1.cpp @@ -234,7 +234,6 @@ typedef enum cfstore_ex_state_t { CFSTORE_EX_STATE_UNINIT_DONE } cfstore_ex_state_t; - typedef struct cfstore_example1_ctx_t { ARM_CFSTORE_CAPABILITIES caps; @@ -263,7 +262,87 @@ static void cfstore_ex_fms_update(cfstore_example1_ctx_t* ctx); /// @endcond -/* @brief test startup code to reset flash +#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED + +#define CFSTORE_FLASH_START_FORMAT (FLASH_JOURNAL_OPCODE_RESET + 0x00010000) + +typedef enum cfstore_ex_flash_state_t { + CFSTORE_EX_FLASH_STATE_STARTING = 1, + CFSTORE_EX_FLASH_STATE_FORMATTING, + CFSTORE_EX_FLASH_STATE_INITIALIZING, + CFSTORE_EX_FLASH_STATE_RESETTING, + CFSTORE_EX_FLASH_STATE_READY, +} cfstore_ex_flash_state_t; + +typedef struct cfstore_example1_flash_ctx_t +{ + volatile cfstore_ex_flash_state_t state; +} cfstore_example1_flash_ctx_t; + +static cfstore_example1_flash_ctx_t cfstore_example1_flash_ctx_g; + + +static void cfstore_ex_flash_journal_callback(int32_t status, FlashJournal_OpCode_t cmd_code) +{ + static FlashJournal_t jrnl; + extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F; + const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_MTD_K64F; + + + if(cmd_code == (FlashJournal_OpCode_t) CFSTORE_FLASH_START_FORMAT) { + CFSTORE_EX1_LOG("FORMATTING%s", "\n"); + status = flashJournalStrategySequential_format(drv, 4, cfstore_ex_flash_journal_callback); + CFSTORE_EX1_TEST_ASSERT_MSG(status >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_format() failed (status=%d)\r\n", __func__, (int) status); + if(status == 0) { + /* async completion pending */ + return; + } + /* status > 0 implies operation completed synchronously + * intentional fall through */ + } + + switch(cmd_code) + { + case FLASH_JOURNAL_OPCODE_FORMAT: + /* format done */ + CFSTORE_EX1_TEST_ASSERT_MSG(status > JOURNAL_STATUS_OK, "%s:Error: FlashJournal_format() failed (status=%d)\r\n", __func__, (int) status); + cfstore_example1_flash_ctx_g.state = CFSTORE_EX_FLASH_STATE_INITIALIZING; + + CFSTORE_EX1_LOG("FLASH INITIALIZING%s", "\n"); + status = FlashJournal_initialize(&jrnl, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, NULL); + CFSTORE_EX1_TEST_ASSERT_MSG(status >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_initialize() failed (status=%d)\r\n", __func__, (int) status); + if(status == 0) { + /* async completion pending */ + break; + } + /* status > 0 implies operation completed synchronously + * intentional fall through */ + case FLASH_JOURNAL_OPCODE_INITIALIZE: + /* initialize done */ + CFSTORE_EX1_TEST_ASSERT_MSG(status > JOURNAL_STATUS_OK, "%s:Error: FlashJournal_initialize() failed (status=%d)\r\n", __func__, (int) status); + cfstore_example1_flash_ctx_g.state = CFSTORE_EX_FLASH_STATE_RESETTING; + + CFSTORE_EX1_LOG("FLASH RESETTING%s", "\n"); + status = FlashJournal_reset(&jrnl); + CFSTORE_EX1_TEST_ASSERT_MSG(status >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_reset() failed (status=%d)\r\n", __func__, (int) status); + /* intentional fall through */ + case FLASH_JOURNAL_OPCODE_RESET: + /* reset done */ + CFSTORE_EX1_LOG("FLASH RESET DONE%s", "\n"); + cfstore_example1_flash_ctx_g.state = CFSTORE_EX_FLASH_STATE_READY; + break; + + default: + CFSTORE_EX1_LOG("%s:Error: notification of unsupported cmd_code event (status=%d, cmd_code=%d)\n", __func__, (int) status, (int) cmd_code); + return; + } + return; +} +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ + + + +/* @brief test startup code to reset flash for testing purposes. */ static int32_t cfstore_test_startup(void) { @@ -272,16 +351,13 @@ static int32_t cfstore_test_startup(void) #ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED - int32_t ret = ARM_DRIVER_ERROR; - static FlashJournal_t jrnl; - extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0); - const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0); - - ret = FlashJournal_initialize(&jrnl, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, NULL); - CFSTORE_EX1_TEST_ASSERT_MSG(ret >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_initialize() failed (ret=%d)\r\n", __func__, (int) ret); + memset(&cfstore_example1_flash_ctx_g, 0, sizeof(cfstore_example1_flash_ctx_g)); + cfstore_example1_flash_ctx_g.state = CFSTORE_EX_FLASH_STATE_STARTING; + cfstore_ex_flash_journal_callback(JOURNAL_STATUS_OK, (FlashJournal_OpCode_t) CFSTORE_FLASH_START_FORMAT); + while(cfstore_example1_flash_ctx_g.state != CFSTORE_EX_FLASH_STATE_READY) { + /* spin */ + } - ret = FlashJournal_reset(&jrnl); - CFSTORE_EX1_TEST_ASSERT_MSG(ret >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_reset() failed (ret=%d)\r\n", __func__, (int) ret); #endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ return ARM_DRIVER_OK; diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/example3/example3.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example3/example3.cpp index 5373d52f4e5..f94f855f8f3 100644 --- a/features/storage/FEATURE_STORAGE/TESTS/cfstore/example3/example3.cpp +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example3/example3.cpp @@ -268,7 +268,7 @@ static control_t cfstore_example3_app_start(const size_t call_count) ctx->hkey_prev = ctx->hkey_prev_buf; ctx->caps = cfstore_drv->GetCapabilities(); CFSTORE_EX1_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, ctx->caps.asynchronous_ops); - if(ctx->caps.asynchronous_ops == true){ + if(ctx->caps.asynchronous_ops == 1){ /* This is a sync mode only test. If this test is not built for sync mode, then skip testing return true * This means the test will conveniently pass when run in CI as part of async mode testing */ CFSTORE_EX1_LOG("*** Skipping test as binary built for flash journal async mode, and this test is sync-only%s", "\n"); diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/example4/example4.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example4/example4.cpp index 242e3b3ce43..2466dd4cab8 100644 --- a/features/storage/FEATURE_STORAGE/TESTS/cfstore/example4/example4.cpp +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example4/example4.cpp @@ -74,7 +74,7 @@ static control_t cfstore_example4_test_00(const size_t call_count) /* initialise the context */ caps = gCfStoreDriver->GetCapabilities(); CFSTORE_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, caps.asynchronous_ops); - if(caps.asynchronous_ops == true){ + if(caps.asynchronous_ops == 1){ /* This is a sync mode only test. If this test is not built for sync mode, then skip testing return true * This means the test will conveniently pass when run in CI as part of async mode testing */ CFSTORE_LOG("*** Skipping test as binary built for flash journal async mode, and this test is sync-only%s", "\n"); diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/example5/example5.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example5/example5.cpp index 926c1eceb38..267b15acfed 100644 --- a/features/storage/FEATURE_STORAGE/TESTS/cfstore/example5/example5.cpp +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/example5/example5.cpp @@ -167,8 +167,8 @@ int32_t cfstore_test_startup(void) #ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED int32_t ret = ARM_DRIVER_ERROR; static FlashJournal_t jrnl; - extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0); - const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0); + extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F; + const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_MTD_K64F; ret = FlashJournal_initialize(&jrnl, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, NULL); CFSTORE_EX5_TEST_ASSERT_MSG(ret >= JOURNAL_STATUS_OK, "%s:Error: FlashJournal_initialize() failed (ret=%d)\r\n", __func__, (int) ret); @@ -278,7 +278,7 @@ static control_t cfstore_EXAMPLE5_app_start(const size_t call_count) ctx->hkey_prev = ctx->hkey_prev_buf; ctx->caps = cfstore_drv->GetCapabilities(); CFSTORE_EX5_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, ctx->caps.asynchronous_ops); - if(ctx->caps.asynchronous_ops == true){ + if(ctx->caps.asynchronous_ops){ /* This is a sync mode only test. If this test is not built for sync mode, then skip testing return true * This means the test will conveniently pass when run in CI as part of async mode testing */ CFSTORE_EX5_LOG("*** Skipping test as binary built for flash journal async mode, and this test is sync-only%s", "\n"); diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/flash/flash.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/flash/flash.cpp index 5b785b71ef1..26fd4bf219a 100644 --- a/features/storage/FEATURE_STORAGE/TESTS/cfstore/flash/flash.cpp +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/flash/flash.cpp @@ -88,7 +88,8 @@ char cfstore_flash_utest_msg_g[CFSTORE_FLASH_UTEST_MSG_BUF_SIZE]; #ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED uint16_t cfstore_flash_mtd_async_ops_g = 0; -extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0); +extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F; + /* KV data for test_01 */ static cfstore_kv_data_t cfstore_flush_test_01_kv_data[] = { @@ -279,7 +280,7 @@ void cfstore_flash_test_01_callback(int32_t status, FlashJournal_OpCode_t cmd_co static void cfstore_flash_fsm_init_on_entry(void* context) { /* round up cfstore_flash_data_blob_t to nearest k64f program unit size */ - const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0); + const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_MTD_K64F; FlashJournal_Info_t info; FlashJournal_Status_t status = JOURNAL_STATUS_ERROR; cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/flush3/flush3.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/flush3/flush3.cpp index 9313475c315..7e53a782093 100644 --- a/features/storage/FEATURE_STORAGE/TESTS/cfstore/flush3/flush3.cpp +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/flush3/flush3.cpp @@ -844,7 +844,7 @@ static control_t cfstore_flush3_test_00(const size_t call_count) /* initialise the context */ caps = drv->GetCapabilities(); CFSTORE_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, caps.asynchronous_ops); - if(caps.asynchronous_ops == true){ + if(caps.asynchronous_ops == 1){ /* This is a sync mode only test. If this test is not built for sync mode, then skip testing return true * This means the test will conveniently pass when run in CI as part of async mode testing */ CFSTORE_LOG("*** Skipping test as binary built for flash journal async mode, and this test is sync-only%s", "\n"); diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/init/init.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/init/init.cpp index 2f6d3b569c8..025f1af9c06 100644 --- a/features/storage/FEATURE_STORAGE/TESTS/cfstore/init/init.cpp +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/init/init.cpp @@ -103,7 +103,7 @@ static control_t cfstore_init_app_start(const size_t call_count) memset(ctx, 0, sizeof(cfstore_init_ctx_t)); ctx->caps = cfstore_drv->GetCapabilities(); CFSTORE_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, ctx->caps.asynchronous_ops); - if(ctx->caps.asynchronous_ops == true){ + if(ctx->caps.asynchronous_ops == 1){ /* This is a sync mode only test. If this test is not built for sync mode, then skip testing return true * This means the test will conveniently pass when run in CI as part of async mode testing */ CFSTORE_LOG("*** Skipping test as binary built for flash journal async mode, and this test is sync-only%s", "\n"); diff --git a/features/storage/FEATURE_STORAGE/TESTS/cfstore/misc/misc.cpp b/features/storage/FEATURE_STORAGE/TESTS/cfstore/misc/misc.cpp index 20d46533cea..954ba204f24 100644 --- a/features/storage/FEATURE_STORAGE/TESTS/cfstore/misc/misc.cpp +++ b/features/storage/FEATURE_STORAGE/TESTS/cfstore/misc/misc.cpp @@ -272,7 +272,7 @@ control_t cfstore_misc_test_04_start(const size_t call_count) status = drv->GetStatus(); CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: GetStatus() before initialisation should have reported error, but reported no error.\r\n", __func__); - TEST_ASSERT_MESSAGE(status.error == true, cfstore_misc_utest_msg_g); + TEST_ASSERT_MESSAGE(status.error == 1, cfstore_misc_utest_msg_g); ret = drv->Initialize(cfstore_utest_default_callback, NULL); CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialize CFSTORE (ret=%d)\n", __func__, (int) ret); @@ -295,10 +295,10 @@ control_t cfstore_misc_test_04_end(const size_t call_count) status = drv->GetStatus(); CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: GetStatus() but reported error.\r\n", __func__); - TEST_ASSERT_MESSAGE(status.error == false, cfstore_misc_utest_msg_g); + TEST_ASSERT_MESSAGE(status.error == 0, cfstore_misc_utest_msg_g); CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: GetStatus() reported operation in progress.\r\n", __func__); - TEST_ASSERT_MESSAGE(status.in_progress == false, cfstore_misc_utest_msg_g); + TEST_ASSERT_MESSAGE(status.in_progress == 0, cfstore_misc_utest_msg_g); ret = drv->Uninitialize(); CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); diff --git a/features/storage/FEATURE_STORAGE/TESTS/flash_journal/basicAPI/basicAPI.cpp b/features/storage/FEATURE_STORAGE/TESTS/flash_journal/basicAPI/basicAPI.cpp index b6645573548..71a0e4effef 100644 --- a/features/storage/FEATURE_STORAGE/TESTS/flash_journal/basicAPI/basicAPI.cpp +++ b/features/storage/FEATURE_STORAGE/TESTS/flash_journal/basicAPI/basicAPI.cpp @@ -15,10 +15,6 @@ * limitations under the License. */ -#if !DEVICE_STORAGE - #error [NOT_SUPPORTED] Storage not supported for this target -#endif - #ifdef TARGET_LIKE_POSIX #define AVOID_GREENTEA #endif @@ -29,6 +25,7 @@ #include "utest/utest.h" #include "unity/unity.h" +#include "flash-journal-strategy-sequential/flash_journal_crc.h" #include "flash-journal-strategy-sequential/flash_journal_strategy_sequential.h" #include "flash-journal-strategy-sequential/flash_journal_private.h" #include @@ -36,8 +33,8 @@ using namespace utest::v1; -extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0); -ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0); +extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F; +const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_MTD_K64F; FlashJournal_t journal; @@ -54,32 +51,64 @@ void callbackHandler(int32_t status, FlashJournal_OpCode_t cmd_code) switch (cmd_code) { case FLASH_JOURNAL_OPCODE_INITIALIZE: - // printf("journal_callbackHandler: callback for init with status %" PRId32 "\n", status); + //printf("journal_callbackHandler: callback for init with status %" PRId32 "\n", status); break; case FLASH_JOURNAL_OPCODE_READ_BLOB: - // printf("journal_callbackHandler: callback for read with status %" PRId32 "\n", status); + //printf("journal_callbackHandler: callback for read with status %" PRId32 "\n", status); break; case FLASH_JOURNAL_OPCODE_LOG_BLOB: - // printf("journal_callbackHandler: callback for log with status %" PRId32 "\n", status); + //printf("journal_callbackHandler: callback for log with status %" PRId32 "\n", status); break; case FLASH_JOURNAL_OPCODE_COMMIT: - // printf("journal_callbackHandler: callback for commit with status %" PRId32 "\n", status); + //printf("journal_callbackHandler: callback for commit with status %" PRId32 "\n", status); break; case FLASH_JOURNAL_OPCODE_RESET: - // printf("journal_callbackHandler: callback for reset with status %" PRId32 "\n", status); + //printf("journal_callbackHandler: callback for reset with status %" PRId32 "\n", status); + break; + + case FLASH_JOURNAL_OPCODE_FORMAT: + //printf("journal_callbackHandler: callback for format with status %" PRId32 "\n", status); break; default: - // printf("journal_callbackHandler: callback for opcode %u with status %" PRId32 "\n", cmd_code, status); + //printf("journal_callbackHandler: callback for opcode %u with status %" PRId32 "\n", cmd_code, status); break; } Harness::validate_callback(); // Validate the callback } +control_t test_format(const size_t call_count) +{ + int32_t rc; + //printf("test_format: entered with call_count %" PRIu32 "\n", (uint32_t)call_count); + + ARM_STORAGE_INFO mtdInfo; + rc = drv->GetInfo(&mtdInfo); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + TEST_ASSERT(mtdInfo.total_storage > 0); + + if (call_count == 1) { + rc = flashJournalStrategySequential_format(drv, 4 /* numSlots */, callbackHandler); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + return CaseTimeout(200) + CaseRepeatAll; + } + TEST_ASSERT_EQUAL(1, rc); /* synchronous completion is expected to return 1. */ + } + + return CaseNext; +} + +void test_initializeBeforeCreate() +{ + int32_t rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler); + TEST_ASSERT((rc == 1) || (rc == JOURNAL_STATUS_NOT_FORMATTED)); +} + control_t test_initialize() { int32_t rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler); @@ -115,7 +144,7 @@ control_t test_resetAndInitialize(const size_t call_count) NEEDS_VERIFICATION_FOLLOWING_INITIALIZE, } state; - printf("test_resetAndInitialize: entered with call_count %u\n", call_count); + //printf("test_resetAndInitialize: entered with call_count %" PRIu32 "\n", (uint32_t)call_count); if (call_count == 1) { state = NEEDS_INITIAL_RESET; } @@ -127,7 +156,7 @@ control_t test_resetAndInitialize(const size_t call_count) TEST_ASSERT(info.capacity > 0); previousCapacity = info.capacity; - printf("test_resetAndInitialize: calling reset()\n"); + //printf("test_resetAndInitialize: calling reset()\n"); rc = FlashJournal_reset(&journal); TEST_ASSERT_NOT_EQUAL(JOURNAL_STATUS_UNSUPPORTED, rc); TEST_ASSERT(rc >= JOURNAL_STATUS_OK); @@ -152,7 +181,7 @@ control_t test_resetAndInitialize(const size_t call_count) TEST_ASSERT_EQUAL(0, info.sizeofJournaledBlob); /* attempt an initialize following reset() */ - printf("test_resetAndInitialize: calling initialize() after reset\n"); + //printf("test_resetAndInitialize: calling initialize() after reset\n"); rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler); TEST_ASSERT(rc >= JOURNAL_STATUS_OK); state = NEEDS_VERIFICATION_FOLLOWING_INITIALIZE; @@ -164,7 +193,7 @@ control_t test_resetAndInitialize(const size_t call_count) /* fall through */ case NEEDS_VERIFICATION_FOLLOWING_INITIALIZE: default: - printf("test_resetAndInitialize: verification\n"); + //printf("test_resetAndInitialize: verification\n"); TEST_ASSERT_EQUAL(0, sequentialJournal->nextSequenceNumber); TEST_ASSERT_EQUAL((uint32_t)-1, sequentialJournal->currentBlobIndex); TEST_ASSERT_EQUAL(SEQUENTIAL_JOURNAL_STATE_INITIALIZED, sequentialJournal->state); @@ -184,7 +213,7 @@ control_t test_commitWithoutLogs(const size_t call_count) { int32_t rc; - printf("test_commitWithoutLogs: entered with call_count %u\n", call_count); + //printf("test_commitWithoutLogs: entered with call_count %" PRIu32 "\n", (uint32_t)call_count); switch (call_count) { case 1: @@ -199,7 +228,7 @@ control_t test_commitWithoutLogs(const size_t call_count) case 2: rc = FlashJournal_commit(&journal); - // printf("commit returned %" PRId32 "\r\n", rc); + //printf("commit returned %" PRId32 "\r\n", rc); TEST_ASSERT(rc >= JOURNAL_STATUS_OK); if (rc == JOURNAL_STATUS_OK) { TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); @@ -221,7 +250,7 @@ control_t test_logSmallWithoutCommit(const size_t call_count) { int32_t rc; - printf("test_logSmallWithoutCommit: entered with call_count %u\n", call_count); + //printf("test_logSmallWithoutCommit: entered with call_count %" PRIu32 "\n", (uint32_t)call_count); switch (call_count) { case 1: @@ -257,7 +286,7 @@ control_t test_logSmallAndCommit(const size_t call_count) { int32_t rc; - printf("test_logSmallAndCommit: entered with call_count %u\n", call_count); + //printf("test_logSmallAndCommit: entered with call_count %" PRIu32 "\n", (uint32_t)call_count); switch (call_count) { case 1: @@ -311,13 +340,13 @@ control_t test_initializeAfterLogSmallAndCommit(const size_t call_count) { int32_t rc; - printf("test_initializeAfterLogSmallAndCommit: entered with call_count %u\n", call_count); + //printf("test_initializeAfterLogSmallAndCommit: entered with call_count %" PRIu32 "\n", (uint32_t)call_count); if (call_count == 1) { rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler); TEST_ASSERT(rc >= JOURNAL_STATUS_OK); if (rc == JOURNAL_STATUS_OK) { - printf("asynchronous_ops for init\n"); + //printf("asynchronous_ops for init\n"); return CaseTimeout(200) + CaseRepeatAll; } TEST_ASSERT_EQUAL(1, rc); /* synchronous completion of initialize() is expected to return 1 */ @@ -335,7 +364,7 @@ control_t test_logLargeWithoutCommit(const size_t call_count) { int32_t rc; - printf("test_logLargeWithoutCommit: entered with call_count %u\n", call_count); + //printf("test_logLargeWithoutCommit: entered with call_count %" PRIu32 "\n", (uint32_t)call_count); switch (call_count) { case 1: @@ -370,7 +399,7 @@ control_t test_logLargeAndCommit(const size_t call_count) { int32_t rc; - printf("test_logLargeAndCommit: entered with call_count %u\n", call_count); + //printf("test_logLargeAndCommit: entered with call_count %" PRIu32 "\n", (uint32_t)call_count); switch (call_count) { case 1: @@ -424,13 +453,13 @@ control_t test_initializeAfterLogLargeAndCommit(const size_t call_count) { int32_t rc; - printf("test_initializeAfterLogLargeAndCommit: entered with call_count %u\n", call_count); + //printf("test_initializeAfterLogLargeAndCommit: entered with call_count %" PRIu32 "\n", (uint32_t)call_count); if (call_count == 1) { rc = FlashJournal_initialize(&journal, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, callbackHandler); TEST_ASSERT(rc >= JOURNAL_STATUS_OK); if (rc == JOURNAL_STATUS_OK) { - printf("test_initializeAfterLogLargeAndCommit: asynchronous_ops for init\n"); + //printf("test_initializeAfterLogLargeAndCommit: asynchronous_ops for init\n"); return CaseTimeout(200) + CaseRepeatAll; } TEST_ASSERT_EQUAL(1, rc); /* synchronous completion of initialize() is expected to return 1 */ @@ -449,7 +478,7 @@ control_t test_logLargeAndReadSmallChunks(const size_t call_count) { int32_t rc; - printf("test_logLargeAndReadSmallChunks: entered with call_count %u\n", call_count); + //printf("test_logLargeAndReadSmallChunks: entered with call_count %" PRIu32 "\n", (uint32_t)call_count); static const size_t SMALL_CHUNK_COUNT = 4; @@ -501,7 +530,7 @@ control_t test_logLargeAndReadSmallChunks(const size_t call_count) } while ((rc = FlashJournal_read(&journal, buffer, SIZEOF_LARGE_WRITE / SMALL_CHUNK_COUNT)) != JOURNAL_STATUS_EMPTY) { - // printf("read returned %ld\n", rc); + // printf("read returned %" PRId32 "\n", rc); TEST_ASSERT(rc >= JOURNAL_STATUS_OK); if (rc == JOURNAL_STATUS_OK) { TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); @@ -523,7 +552,7 @@ control_t test_readLargeInSmallOddChunks(const size_t call_count) { int32_t rc; - printf("test_readLargeInSmallOddChunks<0x%02x, %u>: entered with call_count %u\n", PATTERN, SIZEOF_READS, call_count); + //printf("test_readLargeInSmallOddChunks<0x%02x, %" PRIu32 ">: entered with call_count %" PRIu32 "\n", PATTERN, (uint32_t)SIZEOF_READS, (uint32_t)call_count); if (call_count == 1) { FlashJournal_Info_t info; @@ -546,7 +575,7 @@ control_t test_readLargeInSmallOddChunks(const size_t call_count) } while ((rc = FlashJournal_read(&journal, buffer, SIZEOF_READS)) != JOURNAL_STATUS_EMPTY) { - // printf("read returned %ld\n", rc); + // printf("read returned %" PRId32 "\n", rc); TEST_ASSERT(rc >= JOURNAL_STATUS_OK); if (rc == JOURNAL_STATUS_OK) { TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); @@ -563,6 +592,205 @@ control_t test_readLargeInSmallOddChunks(const size_t call_count) return CaseNext; } +template +control_t test_logPattern(size_t call_count) +{ + int32_t rc = JOURNAL_STATUS_OK; + + //printf("test_logpattern: entered with call_count %" PRIu32 "\n", (uint32_t)call_count); + + switch (call_count) { + case 1: + for (unsigned index = 0; index < SIZEOF_LARGE_WRITE; index++) { + buffer[index] = (uint8_t)(PATTERN ^ index); + } + rc = FlashJournal_log(&journal, buffer, SIZEOF_LARGE_WRITE); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + /* intentional fall-through */ + call_count = 2; + + case 2: + rc = FlashJournal_commit(&journal); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + callbackStatus = rc; + /* intentional fall-through */ + call_count = 3; + + case 3: + { + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT_EQUAL(SIZEOF_LARGE_WRITE, info.sizeofJournaledBlob); + } + /* intentional fall-through */ + call_count = 4; + + case 4: + TEST_ASSERT_EQUAL(1, callbackStatus); + + rc = FlashJournal_read(&journal, buffer, SIZEOF_LARGE_WRITE); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + callbackStatus = rc; + /* intentional fall-through */ + call_count = 4; + + case 5: + TEST_ASSERT_EQUAL(SIZEOF_LARGE_WRITE, rc); + for (unsigned i = 0; i < SIZEOF_LARGE_WRITE; i++) { + // printf("index %u value %x\n", i, buffer[i]); + TEST_ASSERT_EQUAL((uint8_t)(PATTERN ^ i), buffer[i]); + } + break; + + default: + TEST_ASSERT(false); + break; + } + + return CaseNext; +} + +template +control_t test_readFromInReverse(const size_t call_count) +{ + int32_t rc; + static size_t offset = SIZEOF_LARGE_WRITE; + + //printf("test_readFrom<0x%02x, %" PRIu32 ">: entered with call_count %" PRIu32 "\n", PATTERN, (uint32_t)SIZEOF_READS, (uint32_t)call_count); + + if (call_count == 1) { + FlashJournal_Info_t info; + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT_EQUAL(SIZEOF_LARGE_WRITE, info.sizeofJournaledBlob); + TEST_ASSERT(SIZEOF_READS <= info.sizeofJournaledBlob); + + rc = FlashJournal_readFrom(&journal, offset + 1, buffer, SIZEOF_READS); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_EMPTY, rc); + rc = FlashJournal_readFrom(&journal, offset, buffer, SIZEOF_READS); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_EMPTY, rc); + offset -= SIZEOF_READS; + } else { + if (drv->GetCapabilities().asynchronous_ops) { + if (callbackStatus == 0) { + return CaseNext; /* termination condition */ + } + TEST_ASSERT_EQUAL(SIZEOF_READS, callbackStatus); + } + + for (unsigned i = 0; i < SIZEOF_READS; i++) { + // printf("index %u value %x\n", i, buffer[i]); + TEST_ASSERT_EQUAL((uint8_t)(PATTERN ^ (offset + i)), buffer[i]); + } + if (offset == 0) { + return CaseNext; + } + if (offset >= SIZEOF_READS) { + offset -= SIZEOF_READS; + } else { + offset = 0; + } + } + + // printf("test_readFrom: issuing read at offset %lu\n", offset); + while ((rc = FlashJournal_readFrom(&journal, offset, buffer, SIZEOF_READS)) != JOURNAL_STATUS_EMPTY) { + // printf("read returned %" PRId32 "\n", rc); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + + TEST_ASSERT(rc <= (int32_t)SIZEOF_READS); + for (unsigned i = 0; i < (unsigned)rc; i++) { + // printf("index %u value %x\n", i, buffer[i]); + TEST_ASSERT_EQUAL((uint8_t)(PATTERN ^ (offset + i)), buffer[i]); + } + if (offset == 0) { + return CaseNext; + } + if (offset >= SIZEOF_READS) { + offset -= SIZEOF_READS; + } else { + offset = 0; + } + // printf("test_readFrom: issuing read at offset %lu\n", offset); + }; + + return CaseNext; +} + +template +control_t test_readFromFollowedByReads(size_t call_count) +{ + //printf("test_readFrom<0x%02x, %" PRIu32 ">: entered with call_count %" PRIu32 "\n", PATTERN, (uint32_t)SIZEOF_READS, (uint32_t)call_count); + + int32_t rc; + static size_t offset = SIZEOF_LARGE_WRITE / 2; + + FlashJournal_Info_t info; + switch (call_count) { + case 1: + rc = FlashJournal_getInfo(&journal, &info); + TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); + TEST_ASSERT_EQUAL(SIZEOF_LARGE_WRITE, info.sizeofJournaledBlob); + TEST_ASSERT(SIZEOF_READS <= (info.sizeofJournaledBlob - offset)); + + rc = FlashJournal_readFrom(&journal, offset, buffer, SIZEOF_READS); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + callbackStatus = rc; + /* intentional fall-through */ + call_count = 2; + + case 2: + /* verify the previous readFrom */ + TEST_ASSERT_EQUAL(SIZEOF_READS, callbackStatus); + for (unsigned i = 0; i < (unsigned)callbackStatus; i++) { + // printf("index %u value %x\n", i, buffer[i]); + TEST_ASSERT_EQUAL((uint8_t)(PATTERN ^ (offset + i)), buffer[i]); + } + + /* issue a sequential read to follow the previous readFrom */ + rc = FlashJournal_read(&journal, buffer, SIZEOF_READS); + TEST_ASSERT(rc >= JOURNAL_STATUS_OK); + if (rc == JOURNAL_STATUS_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(500) + CaseRepeatAll; + } + callbackStatus = rc; + /* intentional fall-through */ + call_count = 3; + + case 3: + TEST_ASSERT_EQUAL(SIZEOF_READS, callbackStatus); + + for (unsigned i = 0; i < (unsigned)callbackStatus; i++) { + // printf("index %u value %x\n", i, buffer[i]); + TEST_ASSERT_EQUAL((uint8_t)(PATTERN ^ (offset + SIZEOF_READS + i)), buffer[i]); + } + break; + } + + return CaseNext; +} + template control_t test_logSeveralOddSizedChunks(size_t call_count) { @@ -573,7 +801,7 @@ control_t test_logSeveralOddSizedChunks(size_t call_count) static const uint8_t PATTERN = 0xAA; static size_t totalDataLogged = 0; - printf("test_logSeveralOddSizedChunks<%u, %u>: entered with call_count %u\n", SIZEOF_ODD_CHUNK, N_WRITES, call_count); + //printf("test_logSeveralOddSizedChunks<%" PRIu32 ", %" PRIu32 ">: entered with call_count %" PRIu32 "\n", (uint32_t)SIZEOF_ODD_CHUNK, (uint32_t)N_WRITES, (uint32_t)call_count); TEST_ASSERT(SIZEOF_ODD_CHUNK <= BUFFER_SIZE); /* check the status of the previous asynchronous operation */ @@ -584,7 +812,7 @@ control_t test_logSeveralOddSizedChunks(size_t call_count) rc = FlashJournal_getInfo(&journal, &info); TEST_ASSERT_EQUAL(JOURNAL_STATUS_OK, rc); TEST_ASSERT(SIZEOF_ODD_CHUNK < info.program_unit); - printf("test_logSeveralOddSizedChunks: RETURNING CaseNext\n"); + //printf("test_logSeveralOddSizedChunks: RETURNING CaseNext\n"); return CaseNext; } @@ -600,7 +828,7 @@ control_t test_logSeveralOddSizedChunks(size_t call_count) } while (call_count <= N_WRITES) { - printf("test_logSeveralOddSizedChunks: iteration with call_count %u\n", call_count); + //printf("test_logSeveralOddSizedChunks: iteration with call_count %" PRIu32 "\n", (uint32_t)call_count); memset(buffer, PATTERN, SIZEOF_ODD_CHUNK); rc = FlashJournal_log(&journal, buffer, SIZEOF_ODD_CHUNK); // printf("test_logSeveralOddSizedChunks: called FlashJournal_log(): rc = %" PRId32 "\n", rc); @@ -663,13 +891,13 @@ control_t test_multipleWritesFollowedByCommitFollowedByMultipleReads(const size_ static const size_t SIZEOF_WRITE = BUFFER_SIZE / N_WRITES; static const size_t SIZEOF_READ = BUFFER_SIZE / N_READS; - printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: entered with call_count %u\n", call_count); + //printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: entered with call_count %" PRIu32 "\n", (uint32_t)call_count); if (call_count <= N_WRITES) { - printf("writing pattern %02x\n", PATTERN ^ call_count); + //printf("writing pattern %02" PRIx8 "\n", (uint8_t)(PATTERN ^ call_count)); memset(buffer, (PATTERN ^ call_count), SIZEOF_WRITE); rc = FlashJournal_log(&journal, buffer, SIZEOF_WRITE); - // printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: log returned %" PRId32 "\n", rc); + //printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: log returned %" PRId32 "\n", rc); TEST_ASSERT(rc >= JOURNAL_STATUS_OK); if (rc == JOURNAL_STATUS_OK) { TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); @@ -684,12 +912,12 @@ control_t test_multipleWritesFollowedByCommitFollowedByMultipleReads(const size_ TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); return CaseTimeout(500) + CaseRepeatAll; } - // printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: commit returned %" PRId32 "\n", rc); + //printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: commit returned %" PRId32 "\n", rc); callbackStatus = rc; /* pass forward the return value so that the next iteration can check callbackStatus */ return CaseRepeatAll; } else if (call_count < (N_WRITES + 1 + N_READS + 1)) { unsigned readIteration = call_count - (N_WRITES + 1); - printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: read iteration %u\n", readIteration); + //printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: read iteration %u\n", readIteration); if (call_count == (N_WRITES + 1 /* commit */ + 1 /* first iteration after commit */)) { TEST_ASSERT_EQUAL(1, callbackStatus); @@ -706,7 +934,7 @@ control_t test_multipleWritesFollowedByCommitFollowedByMultipleReads(const size_ } while ((rc = FlashJournal_read(&journal, buffer, SIZEOF_READ)) != JOURNAL_STATUS_EMPTY) { - // printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: read returned %ld\n", rc); + // printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: read returned %" PRId32 "\n", rc); TEST_ASSERT((rc == JOURNAL_STATUS_OK) || (rc == SIZEOF_READ)); if (rc == JOURNAL_STATUS_OK) { TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); @@ -714,7 +942,7 @@ control_t test_multipleWritesFollowedByCommitFollowedByMultipleReads(const size_ } TEST_ASSERT_EQUAL(SIZEOF_READ, rc); - printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: checking for pattern %02x\n", PATTERN ^ readIteration); + //printf("test_multipleWritesFollowedByCommitFollowedByMultipleReads: checking for pattern %02x\n", PATTERN ^ readIteration); for (unsigned i = 0; i < SIZEOF_READ; i++) { // printf("index %u value %x\n", i, buffer[i]); TEST_ASSERT_EQUAL(PATTERN ^ readIteration, buffer[i]); @@ -733,7 +961,7 @@ control_t test_failedSmallWriteFollowedByPaddedWrite(const size_t call_count) static const uint8_t PATTERN = 0xAA; - printf("test_failedSmallWriteFollowedByPaddedWrite: entered with call_count %u\n", call_count); + //printf("test_failedSmallWriteFollowedByPaddedWrite: entered with call_count %" PRIu32 "\n", (uint32_t)call_count); FlashJournal_Info_t info; rc = FlashJournal_getInfo(&journal, &info); @@ -799,9 +1027,35 @@ control_t test_failedSmallWriteFollowedByPaddedWrite(const size_t call_count) return CaseNext; } +void test_crc32() +{ + const unsigned char dummyMsg[] = "ahello world"; + flashJournalCrcReset(); TEST_ASSERT_EQUAL(0xe8b7be43, flashJournalCrcCummulative(dummyMsg, 1)); + flashJournalCrcReset(); TEST_ASSERT_EQUAL(0x7e56a173, flashJournalCrcCummulative(dummyMsg, 2)); + flashJournalCrcReset(); TEST_ASSERT_EQUAL(0x26a80c7d, flashJournalCrcCummulative(dummyMsg, 3)); + flashJournalCrcReset(); TEST_ASSERT_EQUAL(0xb8946773, flashJournalCrcCummulative(dummyMsg, 4)); + flashJournalCrcReset(); TEST_ASSERT_EQUAL(0x5fb2761f, flashJournalCrcCummulative(dummyMsg, 5)); + flashJournalCrcReset(); TEST_ASSERT_EQUAL(0x82582cc7, flashJournalCrcCummulative(dummyMsg, 6)); + flashJournalCrcReset(); TEST_ASSERT_EQUAL(0xeceec07a, flashJournalCrcCummulative(dummyMsg, 7)); + flashJournalCrcReset(); TEST_ASSERT_EQUAL(0xac5f7df0, flashJournalCrcCummulative(dummyMsg, 8)); + flashJournalCrcReset(); TEST_ASSERT_EQUAL(0xb21e3e25, flashJournalCrcCummulative(dummyMsg, 9)); + flashJournalCrcReset(); TEST_ASSERT_EQUAL(0x27bf35e4, flashJournalCrcCummulative(dummyMsg, 10)); + flashJournalCrcReset(); TEST_ASSERT_EQUAL(0x31465baa, flashJournalCrcCummulative(dummyMsg, 11)); + flashJournalCrcReset(); TEST_ASSERT_EQUAL(0xaeef4661, flashJournalCrcCummulative(dummyMsg, 12)); + + /* check for composability */ + uint32_t crc; + for (unsigned msgLen = 1; msgLen < strlen((const char *)dummyMsg); msgLen++) { + for (unsigned partitionIndex = 1; partitionIndex < msgLen; partitionIndex++) { + flashJournalCrcReset(); crc = flashJournalCrcCummulative(dummyMsg, partitionIndex); crc = flashJournalCrcCummulative(dummyMsg + partitionIndex, msgLen - partitionIndex); + flashJournalCrcReset(); TEST_ASSERT_EQUAL(flashJournalCrcCummulative(dummyMsg, msgLen), crc); + } + } +} + #ifndef AVOID_GREENTEA // Custom setup handler required for proper Greentea support -utest::v1::status_t greentea_setup(const size_t number_of_cases) +status_t greentea_setup(const size_t number_of_cases) { GREENTEA_SETUP(60, "default_auto"); // Call the default reporting function @@ -816,6 +1070,9 @@ status_t default_setup(const size_t) // Specify all your test cases here Case cases[] = { + + Case("initializeBeforeCreate", test_initializeBeforeCreate), + Case("format", test_format), Case("initialize", test_initialize), Case("reset and initialize1", test_resetAndInitialize), @@ -861,6 +1118,17 @@ Case cases[] = { Case("read large item in small, odd-sized chunks3", test_readLargeInSmallOddChunks<0xAA, 1021>), Case("read large item in small, odd-sized chunks4", test_readLargeInSmallOddChunks<0xAA, 2401>), + Case("log pattern", test_logPattern<0x55>), + Case("readFrom", test_readFromInReverse<0x55, 255>), + Case("readFrom", test_readFromInReverse<0x55, 512>), + Case("readFrom", test_readFromInReverse<0x55, ((BUFFER_SIZE / 2) - 1)>), + Case("readFrom", test_readFromInReverse<0x55, BUFFER_SIZE>), + Case("readFrom followed by sequential reads", test_readFromFollowedByReads<0x55, 255>), + Case("readFrom followed by sequential reads", test_readFromFollowedByReads<0x55, 511>), + Case("readFrom followed by sequential reads", test_readFromFollowedByReads<0x55, 512>), + Case("readFrom followed by sequential reads", test_readFromFollowedByReads<0x55, 513>), + Case("readFrom followed by sequential reads", test_readFromFollowedByReads<0x55, 1024>), + /* log odd-sized blocks which wouldn't align with program_unit at the tail */ Case("initialize3", test_initialize), Case("log odd-sized chunk", test_logSeveralOddSizedChunks<1, 1>), @@ -876,6 +1144,7 @@ Case cases[] = { Case("failed small write followed by padded write", test_failedSmallWriteFollowedByPaddedWrite), Case("reset and initialize6", test_resetAndInitialize), + Case("crc32", test_crc32), // Case("uninitialize", test_uninitialize), }; diff --git a/features/storage/FEATURE_STORAGE/TESTS/storage-volume-manager/basicAPI/basicAPI.cpp b/features/storage/FEATURE_STORAGE/TESTS/storage-volume-manager/basicAPI/basicAPI.cpp new file mode 100644 index 00000000000..e950f17522f --- /dev/null +++ b/features/storage/FEATURE_STORAGE/TESTS/storage-volume-manager/basicAPI/basicAPI.cpp @@ -0,0 +1,1234 @@ +/* + * Copyright (c) 2006-2016, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef TARGET_LIKE_POSIX +#define AVOID_GREENTEA +#endif + +#ifndef AVOID_GREENTEA +#include "greentea-client/test_env.h" +#endif +#include "utest/utest.h" +#include "unity/unity.h" + +#include "storage-volume-manager/storage_volume_manager.h" +#include +#include + +using namespace utest::v1; + +/* redefine tr_info() to a printf() equivalent to emit trace */ +#define tr_info(...) ((void) 0) +#define mbed_trace_init(...) ((void) 0) +#define mbed_trace_config_set(...) ((void) 0) + +#ifdef TARGET_LIKE_X86_LINUX_NATIVE +extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_RAM; +ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_MTD_RAM; +#elif defined TARGET_LIKE_FRDM_K64F +extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F; +ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_MTD_K64F; +#else +extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F; +ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_MTD_K64F; +#endif + +/* temporary buffer to hold data for testing. */ +static const unsigned BUFFER_SIZE = 16384; +static uint8_t buffer[BUFFER_SIZE]; + +static int32_t callbackStatus; +static int32_t virtualVolumeCallbackStatus; + +#ifndef AVOID_GREENTEA +// Custom setup handler required for proper Greentea support +status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(30, "default_auto"); + // Call the default reporting function + return greentea_test_setup_handler(number_of_cases); +} +#endif + +/* used only for the initialization of the volume-manager. */ +void initializeCallbackHandler(int32_t status) +{ + tr_info("in initializeCallbackHandler\r\n"); + Harness::validate_callback(); +} + +/* used only when accessing MTD directly (for verification) */ +void mtdCallbackHandler(int32_t status, ARM_STORAGE_OPERATION operation) +{ + tr_info("in mtdCallbackHandler"); + callbackStatus = status; + Harness::validate_callback(); +} + +/* the normal callback handler for the virtual volume */ +void virtualMTDCallbackHandler(int32_t status, ARM_STORAGE_OPERATION operation) +{ + tr_info("in virtualMTDCallbackHandler"); + virtualVolumeCallbackStatus = status; + Harness::validate_callback(); +} + +control_t test_initialize(const size_t call_count) +{ + tr_info("test_initialize: called with call_count %lu", call_count); + static StorageVolumeManager volumeManager; + + if (call_count == 1) { + int32_t rc = volumeManager.initialize(drv, initializeCallbackHandler); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + return CaseTimeout(200) + CaseRepeatAll; + } + + /* synchronous completion */ + TEST_ASSERT(rc == 1); + } + + TEST_ASSERT_EQUAL(true, volumeManager.isInitialized()); + TEST_ASSERT(volumeManager.getStorageInfo().total_storage > 0); + for (size_t index = 0; index < MAX_VOLUMES; index++) { + TEST_ASSERT_EQUAL(false, volumeManager.volumeAtIndex(index)->isAllocated()); + } + + return CaseNext; +} + +template +control_t test_againstSingleVolumeAtOffset(const size_t call_count) +{ + tr_info("test_againstSingleVolumeAtOffset: called with call_count %lu", call_count); + static StorageVolumeManager volumeManager; + static StorageVolume *volumeP = NULL; + static ARM_STORAGE_INFO info; + static size_t sizeofDataOperation; + + const uint8_t PATTERN_FOR_PROGRAM_DATA = 0xAA; + + static enum { + VOLUME_MANAGER_INITIALIZE = 1, + BASIC_SYNCHRONOUS_API_TESTING, + READ_DATA, + ERASE, + READ_AFTER_ERASE, + PROGRAM_DATA, + READ_AFTER_PROGRAM_DATA, + VERIFY_PROGRAM_DATA, + DISCONNECT_VOLUME_MANAGER_CALLBACK, + READ_FROM_DRV_AFTER_PROGRAM_DATA, + VERIFY_PROGRAM_DATA2, + } state = VOLUME_MANAGER_INITIALIZE; + tr_info("came in with state %u", state); + + int32_t rc; + ARM_STORAGE_BLOCK firstBlock; + rc = drv->GetNextBlock(NULL, &firstBlock); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + TEST_ASSERT(ARM_STORAGE_VALID_BLOCK(&firstBlock)); + + switch (state) { + case VOLUME_MANAGER_INITIALIZE: + rc = volumeManager.initialize(drv, initializeCallbackHandler); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + state = BASIC_SYNCHRONOUS_API_TESTING; + return CaseTimeout(200) + CaseRepeatAll; + } + + /* synchronous completion */ + TEST_ASSERT(rc == 1); + + /* intentional fall-through */ + + case BASIC_SYNCHRONOUS_API_TESTING: + TEST_ASSERT_EQUAL(true, volumeManager.isInitialized()); + + rc = drv->GetInfo(&info); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + TEST_ASSERT(info.total_storage > 0); + + { /* add volume */ + rc = volumeManager.addVolume(firstBlock.addr + OFFSET /*addr*/, info.total_storage - OFFSET /*size*/ , &volumeP); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + + TEST_ASSERT_EQUAL(true, volumeManager.volumeAtIndex(0)->isAllocated()); + for (size_t index = 1; index < MAX_VOLUMES; index++) { + TEST_ASSERT_EQUAL(false, volumeManager.volumeAtIndex(index)->isAllocated()); + } + } + + { /* GetVersion */ + TEST_ASSERT_EQUAL(drv->GetVersion().api, volumeP->GetVersion().api); + TEST_ASSERT_EQUAL(drv->GetVersion().drv, volumeP->GetVersion().drv); + } + + { /* GetCapabilities */ + TEST_ASSERT_EQUAL(drv->GetCapabilities().asynchronous_ops, volumeP->GetCapabilities().asynchronous_ops); + TEST_ASSERT_EQUAL(drv->GetCapabilities().erase_all, volumeP->GetCapabilities().erase_all); + } + + { /* Initialize */ + rc = volumeP->Initialize(virtualMTDCallbackHandler); + TEST_ASSERT_EQUAL(1, rc); + } + + { /* GetStatus */ + ARM_STORAGE_STATUS status = volumeP->GetStatus(); + TEST_ASSERT_EQUAL(0, status.busy); + TEST_ASSERT_EQUAL(0, status.error); + } + + { /* GetInfo */ + ARM_STORAGE_INFO volumeInfo; + rc = volumeP->GetInfo(&volumeInfo); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + + TEST_ASSERT_EQUAL(info.total_storage - OFFSET, volumeInfo.total_storage); + TEST_ASSERT_EQUAL(info.program_unit, volumeInfo.program_unit); + TEST_ASSERT_EQUAL(info.optimal_program_unit, volumeInfo.optimal_program_unit); + TEST_ASSERT_EQUAL(info.program_cycles, volumeInfo.program_cycles); + TEST_ASSERT_EQUAL(info.erased_value, volumeInfo.erased_value); + TEST_ASSERT_EQUAL(info.memory_mapped, volumeInfo.memory_mapped); + TEST_ASSERT_EQUAL(info.programmability, volumeInfo.programmability); + TEST_ASSERT_EQUAL(info.retention_level, volumeInfo.retention_level); + TEST_ASSERT_EQUAL(info.reserved, volumeInfo.reserved); + TEST_ASSERT_EQUAL(0, memcmp(&info.security, &volumeInfo.security, sizeof(ARM_STORAGE_SECURITY_FEATURES))); + } + + { /* resolve address */ + TEST_ASSERT_EQUAL(firstBlock.addr + OFFSET, volumeP->ResolveAddress(0)); + TEST_ASSERT_EQUAL(firstBlock.addr + OFFSET + info.total_storage, volumeP->ResolveAddress(info.total_storage)); + TEST_ASSERT_EQUAL(firstBlock.addr + OFFSET + info.total_storage / 2, volumeP->ResolveAddress(info.total_storage / 2)); + } + + { /* GetNextBlock */ + rc = volumeP->GetNextBlock(NULL, NULL); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + ARM_STORAGE_BLOCK block; + rc = volumeP->GetNextBlock(NULL, &block); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + TEST_ASSERT_EQUAL(0, block.addr); + TEST_ASSERT_EQUAL(info.total_storage - OFFSET, block.size); + + rc = volumeP->GetNextBlock(&block, NULL); + TEST_ASSERT(rc < ARM_DRIVER_OK); + rc = volumeP->GetNextBlock(&block, &block); + TEST_ASSERT(rc < ARM_DRIVER_OK); + } + + { /* GetBlock */ + ARM_STORAGE_BLOCK block; + rc = volumeP->GetBlock(0, &block); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + TEST_ASSERT_EQUAL(0, block.addr); + TEST_ASSERT_EQUAL(info.total_storage - OFFSET, block.size); + + rc = volumeP->GetBlock((info.total_storage / 2), &block); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + TEST_ASSERT_EQUAL(0, block.addr); + TEST_ASSERT_EQUAL(info.total_storage - OFFSET, block.size); + + rc = volumeP->GetBlock(info.total_storage, &block); + TEST_ASSERT(rc < ARM_DRIVER_OK); + } + + state = READ_DATA; + /* intentional fallthrough */ + + case READ_DATA: + sizeofDataOperation = ((info.total_storage - OFFSET) > BUFFER_SIZE) ? BUFFER_SIZE : (info.total_storage - OFFSET); + TEST_ASSERT(sizeofDataOperation <= BUFFER_SIZE); + + /* ReadData */ + rc = volumeP->ReadData(0, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, volumeP->GetCapabilities().asynchronous_ops); + state = ERASE; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case ERASE: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + /* Erase */ + rc = volumeP->Erase(0, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, volumeP->GetCapabilities().asynchronous_ops); + state = READ_AFTER_ERASE; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case READ_AFTER_ERASE: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + /* Read after Erase */ + rc = volumeP->ReadData(0, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, volumeP->GetCapabilities().asynchronous_ops); + state = PROGRAM_DATA; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case PROGRAM_DATA: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + for (size_t index = 0; index < sizeofDataOperation; index++) { + // if (bytePattern != (buffer)[index]) { + // tr_info("%u: expected %x, found %x", index, bytePattern, buffer[index]); + // } + TEST_ASSERT_EQUAL(info.erased_value ? (uint8_t)0xFF : (uint8_t)0, buffer[index]); + } + + /* ProgramData */ + memset(buffer, PATTERN_FOR_PROGRAM_DATA, sizeofDataOperation); + rc = volumeP->ProgramData(0, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, volumeP->GetCapabilities().asynchronous_ops); + state = READ_AFTER_PROGRAM_DATA; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case READ_AFTER_PROGRAM_DATA: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + /* Read after Program */ + rc = volumeP->ReadData(0, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, volumeP->GetCapabilities().asynchronous_ops); + state = VERIFY_PROGRAM_DATA; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case VERIFY_PROGRAM_DATA: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + for (uint32_t index = 0; index < sizeofDataOperation; index++) { + if ((buffer)[index] != PATTERN_FOR_PROGRAM_DATA) { + tr_info("%s:%u: %" PRIu32 ": expected 0x%02x, found 0x%02x", __FUNCTION__, __LINE__, index, PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + TEST_ASSERT_EQUAL(PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + /* intentional fallthrough */ + + case DISCONNECT_VOLUME_MANAGER_CALLBACK: + rc = drv->Initialize(mtdCallbackHandler); + TEST_ASSERT_EQUAL(1, rc); /* expect synchronous completion */ + /* intentional fallthrough */ + + case READ_FROM_DRV_AFTER_PROGRAM_DATA: + /* Read after Program */ + rc = drv->ReadData(firstBlock.addr + OFFSET, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, volumeP->GetCapabilities().asynchronous_ops); + state = VERIFY_PROGRAM_DATA2; + return CaseTimeout(200) + CaseRepeatAll; + } + + callbackStatus = rc; + /* intentional fallthrough */ + + case VERIFY_PROGRAM_DATA2: + TEST_ASSERT_EQUAL(sizeofDataOperation, callbackStatus); + for (uint32_t index = 0; index < sizeofDataOperation; index++) { + if ((buffer)[index] != PATTERN_FOR_PROGRAM_DATA) { + tr_info("%s:%u: %" PRIu32 ": expected 0x%02x, found 0x%02x", __FUNCTION__, __LINE__, index, PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + TEST_ASSERT_EQUAL(PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + break; + + default: + TEST_ASSERT(false); + } + + return CaseNext; +} + +template +control_t test_againstSingleCStorageAtOffset(const size_t call_count) +{ + tr_info("test_againstSingleCStorageAtOffset: called with call_count %lu", call_count); + static StorageVolumeManager volumeManager; + static _ARM_DRIVER_STORAGE mtd = {}; + static ARM_STORAGE_INFO info; + static size_t sizeofDataOperation; + + const uint8_t PATTERN_FOR_PROGRAM_DATA = 0xAA; + + static enum { + VOLUME_MANAGER_INITIALIZE = 1, + BASIC_SYNCHRONOUS_API_TESTING, + READ_DATA, + ERASE, + READ_AFTER_ERASE, + PROGRAM_DATA, + READ_AFTER_PROGRAM_DATA, + VERIFY_PROGRAM_DATA, + DISCONNECT_VOLUME_MANAGER_CALLBACK, + READ_FROM_DRV_AFTER_PROGRAM_DATA, + VERIFY_PROGRAM_DATA2, + } state = VOLUME_MANAGER_INITIALIZE; + tr_info("came in with state %u", state); + + int32_t rc; + rc = drv->GetNextBlock(NULL, NULL); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + ARM_STORAGE_BLOCK firstBlock; + rc = drv->GetNextBlock(NULL, &firstBlock); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + TEST_ASSERT(ARM_STORAGE_VALID_BLOCK(&firstBlock)); + + switch (state) { + case VOLUME_MANAGER_INITIALIZE: + rc = volumeManager.initialize(drv, initializeCallbackHandler); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + state = BASIC_SYNCHRONOUS_API_TESTING; + return CaseTimeout(200) + CaseRepeatAll; + } + + /* synchronous completion */ + TEST_ASSERT(rc == 1); + + /* intentional fall-through */ + + case BASIC_SYNCHRONOUS_API_TESTING: + TEST_ASSERT_EQUAL(true, volumeManager.isInitialized()); + + rc = drv->GetInfo(&info); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + TEST_ASSERT(info.total_storage > 0); + + { /* add volume */ + rc = volumeManager.addVolume_C(firstBlock.addr + OFFSET /*addr*/, info.total_storage - OFFSET /*size*/ , &mtd); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + + TEST_ASSERT_EQUAL(true, volumeManager.volumeAtIndex(0)->isAllocated()); + for (size_t index = 1; index < MAX_VOLUMES; index++) { + TEST_ASSERT_EQUAL(false, volumeManager.volumeAtIndex(index)->isAllocated()); + } + } + + { /* GetVersion */ + TEST_ASSERT_EQUAL(drv->GetVersion().api, mtd.GetVersion().api); + TEST_ASSERT_EQUAL(drv->GetVersion().drv, mtd.GetVersion().drv); + } + + { /* GetCapabilities */ + TEST_ASSERT_EQUAL(drv->GetCapabilities().asynchronous_ops, mtd.GetCapabilities().asynchronous_ops); + TEST_ASSERT_EQUAL(drv->GetCapabilities().erase_all, mtd.GetCapabilities().erase_all); + } + + { /* Initialize */ + rc = mtd.Initialize(virtualMTDCallbackHandler); + TEST_ASSERT_EQUAL(1, rc); + } + + { /* GetStatus */ + ARM_STORAGE_STATUS status = mtd.GetStatus(); + TEST_ASSERT_EQUAL(0, status.busy); + TEST_ASSERT_EQUAL(0, status.error); + } + + { /* GetInfo */ + ARM_STORAGE_INFO volumeInfo; + rc = mtd.GetInfo(&volumeInfo); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + + TEST_ASSERT_EQUAL(info.total_storage - OFFSET, volumeInfo.total_storage); + TEST_ASSERT_EQUAL(info.program_unit, volumeInfo.program_unit); + TEST_ASSERT_EQUAL(info.optimal_program_unit, volumeInfo.optimal_program_unit); + TEST_ASSERT_EQUAL(info.program_cycles, volumeInfo.program_cycles); + TEST_ASSERT_EQUAL(info.erased_value, volumeInfo.erased_value); + TEST_ASSERT_EQUAL(info.memory_mapped, volumeInfo.memory_mapped); + TEST_ASSERT_EQUAL(info.programmability, volumeInfo.programmability); + TEST_ASSERT_EQUAL(info.retention_level, volumeInfo.retention_level); + TEST_ASSERT_EQUAL(info.reserved, volumeInfo.reserved); + TEST_ASSERT_EQUAL(0, memcmp(&info.security, &volumeInfo.security, sizeof(ARM_STORAGE_SECURITY_FEATURES))); + } + + { /* resolve address */ + TEST_ASSERT_EQUAL(firstBlock.addr + OFFSET, mtd.ResolveAddress(0)); + TEST_ASSERT_EQUAL(firstBlock.addr + OFFSET + info.total_storage, mtd.ResolveAddress(info.total_storage)); + TEST_ASSERT_EQUAL(firstBlock.addr + OFFSET + info.total_storage / 2, mtd.ResolveAddress(info.total_storage / 2)); + } + + { /* GetNextBlock */ + rc = mtd.GetNextBlock(NULL, NULL); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + ARM_STORAGE_BLOCK block; + rc = mtd.GetNextBlock(NULL, &block); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + TEST_ASSERT_EQUAL(0, block.addr); + TEST_ASSERT_EQUAL(info.total_storage - OFFSET, block.size); + + rc = mtd.GetNextBlock(&block, NULL); + TEST_ASSERT(rc < ARM_DRIVER_OK); + rc = mtd.GetNextBlock(&block, &block); + TEST_ASSERT(rc < ARM_DRIVER_OK); + } + + { /* GetBlock */ + rc = mtd.GetBlock(0, NULL); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + ARM_STORAGE_BLOCK block; + rc = mtd.GetBlock(0, &block); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + TEST_ASSERT_EQUAL(0, block.addr); + TEST_ASSERT_EQUAL(info.total_storage - OFFSET, block.size); + + rc = mtd.GetBlock((info.total_storage / 2), NULL); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + rc = mtd.GetBlock((info.total_storage / 2), &block); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + TEST_ASSERT_EQUAL(0, block.addr); + TEST_ASSERT_EQUAL(info.total_storage - OFFSET, block.size); + + rc = mtd.GetBlock(info.total_storage, NULL); + TEST_ASSERT(rc < ARM_DRIVER_OK); + rc = mtd.GetBlock(info.total_storage, &block); + TEST_ASSERT(rc < ARM_DRIVER_OK); + } + + state = READ_DATA; + /* intentional fallthrough */ + + case READ_DATA: + sizeofDataOperation = ((info.total_storage - OFFSET) > BUFFER_SIZE) ? BUFFER_SIZE : (info.total_storage - OFFSET); + TEST_ASSERT(sizeofDataOperation <= BUFFER_SIZE); + + /* ReadData */ + rc = mtd.ReadData(0, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, mtd.GetCapabilities().asynchronous_ops); + state = ERASE; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case ERASE: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + /* Erase */ + rc = mtd.Erase(0, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, mtd.GetCapabilities().asynchronous_ops); + state = READ_AFTER_ERASE; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case READ_AFTER_ERASE: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + /* Read after Erase */ + rc = mtd.ReadData(0, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, mtd.GetCapabilities().asynchronous_ops); + state = PROGRAM_DATA; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case PROGRAM_DATA: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + for (size_t index = 0; index < sizeofDataOperation; index++) { + // if (bytePattern != (buffer)[index]) { + // tr_info("%u: expected %x, found %x", index, bytePattern, buffer[index]); + // } + TEST_ASSERT_EQUAL(info.erased_value ? (uint8_t)0xFF : (uint8_t)0, buffer[index]); + } + + /* ProgramData */ + memset(buffer, PATTERN_FOR_PROGRAM_DATA, sizeofDataOperation); + rc = mtd.ProgramData(0, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, mtd.GetCapabilities().asynchronous_ops); + state = READ_AFTER_PROGRAM_DATA; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case READ_AFTER_PROGRAM_DATA: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + /* Read after Program */ + rc = mtd.ReadData(0, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, mtd.GetCapabilities().asynchronous_ops); + state = VERIFY_PROGRAM_DATA; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case VERIFY_PROGRAM_DATA: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + for (uint32_t index = 0; index < sizeofDataOperation; index++) { + if ((buffer)[index] != PATTERN_FOR_PROGRAM_DATA) { + tr_info("%s:%u: %" PRIu32 ": expected 0x%02x, found 0x%02x", __FUNCTION__, __LINE__, index, PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + TEST_ASSERT_EQUAL(PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + /* intentional fallthrough */ + + case DISCONNECT_VOLUME_MANAGER_CALLBACK: + rc = drv->Initialize(mtdCallbackHandler); + TEST_ASSERT_EQUAL(1, rc); /* expect synchronous completion */ + /* intentional fallthrough */ + + case READ_FROM_DRV_AFTER_PROGRAM_DATA: + /* Read after Program */ + rc = drv->ReadData(firstBlock.addr + OFFSET, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, mtd.GetCapabilities().asynchronous_ops); + state = VERIFY_PROGRAM_DATA2; + return CaseTimeout(200) + CaseRepeatAll; + } + + callbackStatus = rc; + /* intentional fallthrough */ + + case VERIFY_PROGRAM_DATA2: + TEST_ASSERT_EQUAL(sizeofDataOperation, callbackStatus); + for (uint32_t index = 0; index < sizeofDataOperation; index++) { + if ((buffer)[index] != PATTERN_FOR_PROGRAM_DATA) { + tr_info("%s:%u: %" PRIu32 ": expected 0x%02x, found 0x%02x", __FUNCTION__, __LINE__, index, PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + TEST_ASSERT_EQUAL(PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + break; + + default: + TEST_ASSERT(false); + } + + return CaseNext; +} + +template +control_t test_concurrentAccessFromTwoVolumes(const size_t call_count) +{ + tr_info("test_concurrentAccessFromTwoVolumes: called with call_count %lu", call_count); + + if (MAX_VOLUMES <= 1) { + return CaseNext; + } + + static StorageVolumeManager volumeManager; + static StorageVolume *volume1P = NULL; + static StorageVolume *volume2P = NULL; + static ARM_STORAGE_INFO info; + static size_t sizeofDataOperation; + + const uint8_t PATTERN_FOR_PROGRAM_DATA = 0xAA; + + static enum { + VOLUME_MANAGER_INITIALIZE = 1, + ADD_VOLUMES, + ERASE1, + PROGRAM_DATA1, + ERASE2, + PROGRAM_DATA2, + DISCONNECT_VOLUME_MANAGER_CALLBACK, + READ_FROM_DRV_AFTER_PROGRAM_DATA1, + VERIFY_PROGRAM_DATA1, + READ_FROM_DRV_AFTER_PROGRAM_DATA2, + VERIFY_PROGRAM_DATA2, + } state = VOLUME_MANAGER_INITIALIZE; + tr_info("came in with state %u", state); + + int32_t rc; + switch (state) { + case VOLUME_MANAGER_INITIALIZE: + rc = volumeManager.initialize(drv, initializeCallbackHandler); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + state = ADD_VOLUMES; + return CaseTimeout(200) + CaseRepeatAll; + } + + /* synchronous completion */ + TEST_ASSERT(rc == 1); + + /* intentional fall-through */ + + case ADD_VOLUMES: + TEST_ASSERT_EQUAL(true, volumeManager.isInitialized()); + + rc = drv->GetInfo(&info); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + TEST_ASSERT(info.total_storage > 0); + + { /* add volume1 */ + rc = drv->GetBlock(OFFSET1, NULL); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + ARM_STORAGE_BLOCK block1; + rc = drv->GetBlock(OFFSET1, &block1); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + rc = drv->GetBlock(OFFSET1 + SIZE1 - 1, NULL); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + rc = drv->GetBlock(OFFSET1 + SIZE1 - 1, &block1); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + + rc = volumeManager.addVolume(OFFSET1 /*addr*/, SIZE1 /*size*/ , &volume1P); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + + TEST_ASSERT_EQUAL(true, volumeManager.volumeAtIndex(0)->isAllocated()); + for (size_t index = 1; index < MAX_VOLUMES; index++) { + TEST_ASSERT_EQUAL(false, volumeManager.volumeAtIndex(index)->isAllocated()); + } + + { /* Initialize */ + rc = volume1P->Initialize(virtualMTDCallbackHandler); + TEST_ASSERT_EQUAL(1, rc); + } + } + { /* add volume2 */ + rc = drv->GetBlock(OFFSET2, NULL); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + ARM_STORAGE_BLOCK block2; + rc = drv->GetBlock(OFFSET2, &block2); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + rc = drv->GetBlock(OFFSET2 + SIZE2 - 2, NULL); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + rc = drv->GetBlock(OFFSET2 + SIZE2 - 2, &block2); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + + rc = volumeManager.addVolume(OFFSET2 /*addr*/, SIZE2 /*size*/ , &volume2P); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + + TEST_ASSERT_EQUAL(true, volumeManager.volumeAtIndex(1)->isAllocated()); + for (size_t index = 2; index < MAX_VOLUMES; index++) { + TEST_ASSERT_EQUAL(false, volumeManager.volumeAtIndex(index)->isAllocated()); + } + + { /* Initialize */ + rc = volume2P->Initialize(virtualMTDCallbackHandler); + TEST_ASSERT_EQUAL(1, rc); + } + } + + sizeofDataOperation = (SIZE1 > BUFFER_SIZE) ? BUFFER_SIZE : SIZE1; + sizeofDataOperation = (SIZE2 > sizeofDataOperation) ? sizeofDataOperation : SIZE2; + TEST_ASSERT((sizeofDataOperation > 0) && (sizeofDataOperation <= BUFFER_SIZE)); + memset(buffer, PATTERN_FOR_PROGRAM_DATA, sizeofDataOperation); + + /* intentional fall-through */ + + case ERASE1: + rc = volume1P->Erase(0, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, volume1P->GetCapabilities().asynchronous_ops); + state = PROGRAM_DATA1; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case PROGRAM_DATA1: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + tr_info("PROGRAM_DATA1"); + rc = volume1P->ProgramData(0, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, volume1P->GetCapabilities().asynchronous_ops); + + ARM_STORAGE_STATUS status; + status = drv->GetStatus(); + TEST_ASSERT_EQUAL(1, status.busy); + TEST_ASSERT_EQUAL(0, status.error); + status = volume1P->GetStatus(); + TEST_ASSERT_EQUAL(1, status.busy); + TEST_ASSERT_EQUAL(0, status.error); + status = volume2P->GetStatus(); + TEST_ASSERT_EQUAL(1, status.busy); + TEST_ASSERT_EQUAL(0, status.error); + + rc = volume2P->ProgramData(0, buffer, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + rc = volume1P->ProgramData(0, buffer, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + rc = volume1P->ReadData(0, buffer, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + rc = volume1P->Erase(0, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + + state = ERASE2; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case ERASE2: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + rc = volume2P->Erase(0, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, volume2P->GetCapabilities().asynchronous_ops); + state = PROGRAM_DATA2; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case PROGRAM_DATA2: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + tr_info("PROGRAM_DATA2"); + rc = volume2P->ProgramData(0, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, volume2P->GetCapabilities().asynchronous_ops); + + ARM_STORAGE_STATUS status; + status = drv->GetStatus(); + TEST_ASSERT_EQUAL(1, status.busy); + TEST_ASSERT_EQUAL(0, status.error); + status = volume2P->GetStatus(); + TEST_ASSERT_EQUAL(1, status.busy); + TEST_ASSERT_EQUAL(0, status.error); + status = volume1P->GetStatus(); + TEST_ASSERT_EQUAL(1, status.busy); + TEST_ASSERT_EQUAL(0, status.error); + + rc = volume1P->ProgramData(0, buffer, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + rc = volume2P->ProgramData(0, buffer, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + rc = volume2P->ReadData(0, buffer, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + rc = volume2P->Erase(0, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + + state = DISCONNECT_VOLUME_MANAGER_CALLBACK; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case DISCONNECT_VOLUME_MANAGER_CALLBACK: + tr_info("DISCONNECT_VOLUME_MANAGER_CALLBACK"); + rc = drv->Initialize(mtdCallbackHandler); + TEST_ASSERT_EQUAL(1, rc); /* expect synchronous completion */ + /* intentional fallthrough */ + + case READ_FROM_DRV_AFTER_PROGRAM_DATA1: + tr_info("verifying state"); + /* Read after Program */ + rc = drv->ReadData(OFFSET1, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + state = VERIFY_PROGRAM_DATA1; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case VERIFY_PROGRAM_DATA1: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + for (uint32_t index = 0; index < sizeofDataOperation; index++) { + if ((buffer)[index] != PATTERN_FOR_PROGRAM_DATA) { + tr_info("%s:%u: %" PRIu32 ": expected 0x%02x, found 0x%02x", __FUNCTION__, __LINE__, index, PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + TEST_ASSERT_EQUAL(PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + /* intentional fallthrough */ + + case READ_FROM_DRV_AFTER_PROGRAM_DATA2: + /* Read after Program */ + rc = drv->ReadData(OFFSET2, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + state = VERIFY_PROGRAM_DATA2; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case VERIFY_PROGRAM_DATA2: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + for (uint32_t index = 0; index < sizeofDataOperation; index++) { + if ((buffer)[index] != PATTERN_FOR_PROGRAM_DATA) { + tr_info("%s:%u: %" PRIu32 ": expected 0x%02x, found 0x%02x", __FUNCTION__, __LINE__, index, PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + TEST_ASSERT_EQUAL(PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + break; + + default: + TEST_ASSERT(false); + } + + return CaseNext; +} + +template +control_t test_concurrentAccessFromTwoCStorageDevices(const size_t call_count) +{ + tr_info("test_concurrentAccessFromTwoCStorageDevices: called with call_count %lu", call_count); + + if (MAX_VOLUMES <= 1) { + return CaseNext; + } + + static StorageVolumeManager volumeManager; + static _ARM_DRIVER_STORAGE mtd1 = {}; + static _ARM_DRIVER_STORAGE mtd2 = {}; + static ARM_STORAGE_INFO info; + static size_t sizeofDataOperation; + + const uint8_t PATTERN_FOR_PROGRAM_DATA = 0xAA; + + static enum { + VOLUME_MANAGER_INITIALIZE = 1, + ADD_VOLUMES, + ERASE1, + PROGRAM_DATA1, + ERASE2, + PROGRAM_DATA2, + DISCONNECT_VOLUME_MANAGER_CALLBACK, + READ_FROM_DRV_AFTER_PROGRAM_DATA1, + VERIFY_PROGRAM_DATA1, + READ_FROM_DRV_AFTER_PROGRAM_DATA2, + VERIFY_PROGRAM_DATA2, + } state = VOLUME_MANAGER_INITIALIZE; + tr_info("came in with state %u", state); + + int32_t rc; + switch (state) { + case VOLUME_MANAGER_INITIALIZE: + rc = volumeManager.initialize(drv, initializeCallbackHandler); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + state = ADD_VOLUMES; + return CaseTimeout(200) + CaseRepeatAll; + } + + /* synchronous completion */ + TEST_ASSERT(rc == 1); + + /* intentional fall-through */ + + case ADD_VOLUMES: + TEST_ASSERT_EQUAL(true, volumeManager.isInitialized()); + + rc = drv->GetInfo(&info); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + TEST_ASSERT(info.total_storage > 0); + + { /* add C_Storage device 1 */ + ARM_STORAGE_BLOCK block1; + rc = drv->GetBlock(OFFSET1, &block1); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + rc = drv->GetBlock(OFFSET1 + SIZE1 - 1, &block1); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + + rc = volumeManager.addVolume_C(OFFSET1 /*addr*/, SIZE1 /*size*/ , &mtd1); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + + TEST_ASSERT_EQUAL(true, volumeManager.volumeAtIndex(0)->isAllocated()); + for (size_t index = 1; index < MAX_VOLUMES; index++) { + TEST_ASSERT_EQUAL(false, volumeManager.volumeAtIndex(index)->isAllocated()); + } + + { /* Initialize */ + rc = mtd1.Initialize(virtualMTDCallbackHandler); + TEST_ASSERT_EQUAL(1, rc); + } + } + { /* add C_Storage device 2 */ + ARM_STORAGE_BLOCK block2; + rc = drv->GetBlock(OFFSET2, &block2); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + rc = drv->GetBlock(OFFSET2 + SIZE2 - 2, &block2); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + + rc = volumeManager.addVolume_C(OFFSET2 /*addr*/, SIZE2 /*size*/ , &mtd2); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, rc); + + TEST_ASSERT_EQUAL(true, volumeManager.volumeAtIndex(1)->isAllocated()); + for (size_t index = 2; index < MAX_VOLUMES; index++) { + TEST_ASSERT_EQUAL(false, volumeManager.volumeAtIndex(index)->isAllocated()); + } + + { /* Initialize */ + rc = mtd2.Initialize(virtualMTDCallbackHandler); + TEST_ASSERT_EQUAL(1, rc); + } + } + + sizeofDataOperation = (SIZE1 > BUFFER_SIZE) ? BUFFER_SIZE : SIZE1; + sizeofDataOperation = (SIZE2 > sizeofDataOperation) ? sizeofDataOperation : SIZE2; + TEST_ASSERT((sizeofDataOperation > 0) && (sizeofDataOperation <= BUFFER_SIZE)); + memset(buffer, PATTERN_FOR_PROGRAM_DATA, sizeofDataOperation); + + /* intentional fall-through */ + + case ERASE1: + rc = mtd1.Erase(0, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, mtd1.GetCapabilities().asynchronous_ops); + state = PROGRAM_DATA1; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case PROGRAM_DATA1: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + tr_info("PROGRAM_DATA1"); + rc = mtd1.ProgramData(0, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, mtd1.GetCapabilities().asynchronous_ops); + + ARM_STORAGE_STATUS status; + status = drv->GetStatus(); + TEST_ASSERT_EQUAL(1, status.busy); + TEST_ASSERT_EQUAL(0, status.error); + status = mtd1.GetStatus(); + TEST_ASSERT_EQUAL(1, status.busy); + TEST_ASSERT_EQUAL(0, status.error); + status = mtd2.GetStatus(); + TEST_ASSERT_EQUAL(1, status.busy); + TEST_ASSERT_EQUAL(0, status.error); + + rc = mtd2.ProgramData(0, buffer, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + rc = mtd1.ProgramData(0, buffer, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + rc = mtd1.ReadData(0, buffer, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + rc = mtd1.Erase(0, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + + state = ERASE2; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case ERASE2: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + rc = mtd2.Erase(0, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, mtd2.GetCapabilities().asynchronous_ops); + state = PROGRAM_DATA2; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case PROGRAM_DATA2: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + tr_info("PROGRAM_DATA2"); + rc = mtd2.ProgramData(0, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, mtd2.GetCapabilities().asynchronous_ops); + + ARM_STORAGE_STATUS status; + status = drv->GetStatus(); + TEST_ASSERT_EQUAL(1, status.busy); + TEST_ASSERT_EQUAL(0, status.error); + status = mtd2.GetStatus(); + TEST_ASSERT_EQUAL(1, status.busy); + TEST_ASSERT_EQUAL(0, status.error); + status = mtd1.GetStatus(); + TEST_ASSERT_EQUAL(1, status.busy); + TEST_ASSERT_EQUAL(0, status.error); + + rc = mtd1.ProgramData(0, buffer, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + rc = mtd2.ProgramData(0, buffer, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + rc = mtd2.ReadData(0, buffer, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + rc = mtd2.Erase(0, sizeofDataOperation); + TEST_ASSERT_EQUAL(ARM_DRIVER_ERROR_BUSY, rc); + + state = DISCONNECT_VOLUME_MANAGER_CALLBACK; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case DISCONNECT_VOLUME_MANAGER_CALLBACK: + tr_info("DISCONNECT_VOLUME_MANAGER_CALLBACK"); + rc = drv->Initialize(mtdCallbackHandler); + TEST_ASSERT_EQUAL(1, rc); /* expect synchronous completion */ + /* intentional fallthrough */ + + case READ_FROM_DRV_AFTER_PROGRAM_DATA1: + tr_info("verifying state"); + /* Read after Program */ + rc = drv->ReadData(OFFSET1, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + state = VERIFY_PROGRAM_DATA1; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case VERIFY_PROGRAM_DATA1: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + for (uint32_t index = 0; index < sizeofDataOperation; index++) { + if ((buffer)[index] != PATTERN_FOR_PROGRAM_DATA) { + tr_info("%s:%u: %" PRIu32 ": expected 0x%02x, found 0x%02x", __FUNCTION__, __LINE__, index, PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + TEST_ASSERT_EQUAL(PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + /* intentional fallthrough */ + + case READ_FROM_DRV_AFTER_PROGRAM_DATA2: + /* Read after Program */ + rc = drv->ReadData(OFFSET2, buffer, sizeofDataOperation); + TEST_ASSERT(rc >= ARM_DRIVER_OK); + if (rc == ARM_DRIVER_OK) { + TEST_ASSERT_EQUAL(1, drv->GetCapabilities().asynchronous_ops); + state = VERIFY_PROGRAM_DATA2; + return CaseTimeout(200) + CaseRepeatAll; + } + + virtualVolumeCallbackStatus = rc; + /* intentional fallthrough */ + + case VERIFY_PROGRAM_DATA2: + TEST_ASSERT_EQUAL(sizeofDataOperation, virtualVolumeCallbackStatus); + + for (uint32_t index = 0; index < sizeofDataOperation; index++) { + if ((buffer)[index] != PATTERN_FOR_PROGRAM_DATA) { + tr_info("%s:%u: %" PRIu32 ": expected 0x%02x, found 0x%02x", __FUNCTION__, __LINE__, index, PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + TEST_ASSERT_EQUAL(PATTERN_FOR_PROGRAM_DATA, buffer[index]); + } + break; + + default: + TEST_ASSERT(false); + } + + return CaseNext; +} + +// Specify all your test cases here +Case cases[] = { + Case("initialize", test_initialize), + Case("Against a single volume at offset", test_againstSingleVolumeAtOffset<0>), + Case("Against a single volume at offset", test_againstSingleVolumeAtOffset<4096>), + Case("Against a single volume at offset", test_againstSingleVolumeAtOffset<8192>), + Case("Against a single volume at offset", test_againstSingleVolumeAtOffset<65536>), + Case("Against a single C_Storage at offset", test_againstSingleCStorageAtOffset<0>), + Case("Against a single C_Storage at offset", test_againstSingleCStorageAtOffset<4096>), + Case("Against a single C_Storage at offset", test_againstSingleCStorageAtOffset<8192>), + Case("Against a single C_Storage at offset", test_againstSingleCStorageAtOffset<65536>), + + /* note: the following tests are unportable in the sense that they require the underlying storage device to support certain address ranges. */ + Case("Concurrent accesss from two volumes", test_concurrentAccessFromTwoVolumes<512*1024, 128*1024, (512+128)*1024, 128*1024>), + Case("Concurrent accesss from two volumes", test_concurrentAccessFromTwoVolumes<512*1024, 128*1024, (512+128)*1024, 128*1024>), + Case("Concurrent accesss from two volumes", test_concurrentAccessFromTwoVolumes<512*1024, 128*1024, (512+256)*1024, 128*1024>), + Case("Concurrent accesss from two volumes", test_concurrentAccessFromTwoVolumes<512*1024, 128*1024, (512+384)*1024, 128*1024>), + Case("Concurrent accesss from two C_Storage devices", test_concurrentAccessFromTwoCStorageDevices<512*1024, 128*1024, (512+128)*1024, 128*1024>), + Case("Concurrent accesss from two C_Storage devices", test_concurrentAccessFromTwoCStorageDevices<512*1024, 128*1024, (512+256)*1024, 128*1024>), + Case("Concurrent accesss from two C_Storage devices", test_concurrentAccessFromTwoCStorageDevices<512*1024, 128*1024, (512+384)*1024, 128*1024>), +}; + +// Declare your test specification with a custom setup handler +#ifndef AVOID_GREENTEA +Specification specification(greentea_setup, cases); +#else +Specification specification([](const size_t) {return STATUS_CONTINUE;}, cases); +#endif + +int main(int argc, char** argv) +{ + mbed_trace_init(); // initialize the trace library + mbed_trace_config_set(TRACE_MODE_COLOR | TRACE_ACTIVE_LEVEL_INFO | TRACE_CARRIAGE_RETURN); + + // Run the test specification + Harness::run(specification); +} diff --git a/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_config.h b/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_config.h index fe7b76f702f..3ab1732814e 100644 --- a/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_config.h +++ b/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_config.h @@ -56,9 +56,8 @@ #define CFSTORE_CONFIG_BACKEND_FLASH_ENABLED #endif -// todo: fixup for storage driver using more than the STORAGE_xxx namespace: -#if defined DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS -#define CFSTORE_STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS +#if defined STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS +#define CFSTORE_STORAGE_DRIVER_CONFIG_HARDWARE_MTD_ASYNC_OPS STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS #endif diff --git a/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_debug.h b/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_debug.h index d9f096fae91..8d8b7fea543 100644 --- a/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_debug.h +++ b/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_debug.h @@ -27,6 +27,7 @@ printf(_fmt, __VA_ARGS__); \ }while(0); +//todo: restore #define noCFSTORE_DEBUG #define noCFSTORE_DEBUG //#define CFSTORE_DEBUG #ifdef CFSTORE_DEBUG diff --git a/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_svm.cpp b/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_svm.cpp new file mode 100644 index 00000000000..c1c6c72d86f --- /dev/null +++ b/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_svm.cpp @@ -0,0 +1,76 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include "storage_volume_manager.h" +#include "cfstore_config.h" +#include "cfstore_debug.h" +#include "cfstore_svm.h" + +/** @file cfstore_svm.cpp + * + * This module is provides a C wrapper to the C++ storage-volume-manager.h API, + * so it can be called by the C-HAL implementation configuration_store.c + */ + +#define CFSTORE_SVM_VOL_01_START_OFFSET 0x80000UL +#define CFSTORE_SVM_VOL_01_SIZE 0x80000UL + +#ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED +extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F; +static ARM_DRIVER_STORAGE *cfstore_svm_storage_drv = &ARM_Driver_Storage_MTD_K64F; + +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ + +/* the storage volume manager instance used to generate virtual mtd descriptors */ +static StorageVolumeManager volumeManager; + +/* used only for the initialization of the volume-manager. */ +static void cfstore_svm_volume_manager_initialize_callback(int32_t status) +{ + CFSTORE_FENTRYLOG("%s: operation %d with status %d" , __func__, (int) operation, (int) status); +} + +static void cfstore_svm_journal_mtc_callback(int32_t status, ARM_STORAGE_OPERATION operation) +{ + CFSTORE_FENTRYLOG("%s: operation %d with status %d" , __func__, (int) operation, (int) status); +} + +int32_t cfstore_svm_init(struct _ARM_DRIVER_STORAGE *storage_mtd) +{ + int32_t ret = ARM_DRIVER_OK; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + ret = volumeManager.initialize(cfstore_svm_storage_drv, cfstore_svm_volume_manager_initialize_callback); + if(ret < ARM_DRIVER_OK) { + CFSTORE_ERRLOG("%s:debug: volume-manager::initialize() failed for storage_mtd=%p (ret=%d)", __func__, storage_mtd, (int) ret); + return ret; + } + ret = volumeManager.addVolume_C(CFSTORE_SVM_VOL_01_START_OFFSET, CFSTORE_SVM_VOL_01_SIZE, storage_mtd); + if(ret < ARM_DRIVER_OK) { + CFSTORE_ERRLOG("%s:debug: volume-manager::addVolume_C() failed for storage_mtd=%p (ret=%d)", __func__, storage_mtd, (int) ret); + return ret; + } + ret = storage_mtd->Initialize(cfstore_svm_journal_mtc_callback); + if(ret < ARM_DRIVER_OK) { + CFSTORE_ERRLOG("%s:debug: storage_mtd->initialize() failed for storage_mtd=%p (ret=%d)", __func__, storage_mtd, (int) ret); + return ret; + } + return ret; +} diff --git a/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_svm.h b/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_svm.h new file mode 100644 index 00000000000..37b0412cf0c --- /dev/null +++ b/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_svm.h @@ -0,0 +1,41 @@ +/* + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** @file cfstore_svm.h + * + * This is the interface file to configuration store storage volume manager. + */ + +#ifndef __CFSTORE_SVM_H_ +#define __CFSTORE_SVM_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +int32_t cfstore_svm_init(struct _ARM_DRIVER_STORAGE *mtd); + + +#ifdef __cplusplus +} +#endif + +#endif /*__CFSTORE_SVM_H_ */ diff --git a/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_test.c b/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_test.c index af164de3e70..e975cc51093 100644 --- a/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_test.c +++ b/features/storage/FEATURE_STORAGE/cfstore/source/cfstore_test.c @@ -257,8 +257,8 @@ int32_t cfstore_test_startup(void) #ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED static FlashJournal_t jrnl; - extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0); - const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0); + extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F; + const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_MTD_K64F; ret = FlashJournal_initialize(&jrnl, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, NULL); if(ret < JOURNAL_STATUS_OK){ diff --git a/features/storage/FEATURE_STORAGE/cfstore/source/configuration_store.c b/features/storage/FEATURE_STORAGE/cfstore/source/configuration_store.c index efcfff34e68..34150c48ae1 100644 --- a/features/storage/FEATURE_STORAGE/cfstore/source/configuration_store.c +++ b/features/storage/FEATURE_STORAGE/cfstore/source/configuration_store.c @@ -31,6 +31,7 @@ #endif /* YOTTA_CFG_CFSTORE_UVISOR */ #ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED +#include "cfstore_svm.h" #include "flash_journal_strategy_sequential.h" #include "flash_journal.h" #include "Driver_Common.h" @@ -45,8 +46,10 @@ #ifdef CFSTORE_DEBUG uint32_t cfstore_optDebug_g = 1; -uint32_t cfstore_optLogLevel_g = CFSTORE_LOG_NONE; /*CFSTORE_LOG_NONE|CFSTORE_LOG_ERR|CFSTORE_LOG_DEBUG|CFSTORE_LOG_FENTRY */ -uint32_t cfstore_optLogTracepoint_g = CFSTORE_TP_NONE; /*CFSTORE_TP_NONE|CFSTORE_TP_CLOSE|CFSTORE_TP_CREATE|CFSTORE_TP_DELETE|CFSTORE_TP_FILE|CFSTORE_TP_FIND|CFSTORE_TP_FLUSH|CFSTORE_TP_INIT|CFSTORE_TP_OPEN|CFSTORE_TP_READ|CFSTORE_TP_WRITE|CFSTORE_TP_VERBOSE1|CFSTORE_TP_VERBOSE2|CFSTORE_TP_VERBOSE3|CFSTORE_TP_FENTRY; */ +//todo: restore uint32_t cfstore_optLogLevel_g = CFSTORE_LOG_NONE; /*CFSTORE_LOG_NONE|CFSTORE_LOG_ERR|CFSTORE_LOG_DEBUG|CFSTORE_LOG_FENTRY */ +//uint32_t cfstore_optLogTracepoint_g = CFSTORE_TP_NONE; /*CFSTORE_TP_NONE|CFSTORE_TP_CLOSE|CFSTORE_TP_CREATE|CFSTORE_TP_DELETE|CFSTORE_TP_FILE|CFSTORE_TP_FIND|CFSTORE_TP_FLUSH|CFSTORE_TP_INIT|CFSTORE_TP_OPEN|CFSTORE_TP_READ|CFSTORE_TP_WRITE|CFSTORE_TP_VERBOSE1|CFSTORE_TP_VERBOSE2|CFSTORE_TP_VERBOSE3|CFSTORE_TP_FENTRY; */ +uint32_t cfstore_optLogLevel_g = CFSTORE_LOG_NONE|CFSTORE_LOG_ERR|CFSTORE_LOG_DEBUG|CFSTORE_LOG_FENTRY; +uint32_t cfstore_optLogTracepoint_g = CFSTORE_TP_NONE|CFSTORE_TP_CLOSE|CFSTORE_TP_CREATE|CFSTORE_TP_DELETE|CFSTORE_TP_FILE|CFSTORE_TP_FIND|CFSTORE_TP_FLUSH|CFSTORE_TP_INIT|CFSTORE_TP_OPEN|CFSTORE_TP_READ|CFSTORE_TP_WRITE|CFSTORE_TP_VERBOSE1|CFSTORE_TP_VERBOSE2|CFSTORE_TP_VERBOSE3|CFSTORE_TP_FENTRY; #endif @@ -54,10 +57,12 @@ uint32_t cfstore_optLogTracepoint_g = CFSTORE_TP_NONE; /*CFSTORE_TP_NONE|CFSTORE * Externs */ #ifdef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED -extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0); -ARM_DRIVER_STORAGE *cfstore_storage_drv = &ARM_Driver_Storage_(0); +extern ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F; +ARM_DRIVER_STORAGE *cfstore_storage_drv = &ARM_Driver_Storage_MTD_K64F; #endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ +struct _ARM_DRIVER_STORAGE cfstore_journal_mtd; + /* * Defines * @@ -69,7 +74,10 @@ ARM_DRIVER_STORAGE *cfstore_storage_drv = &ARM_Driver_Storage_(0); * valid sizes of areas should always be greater than the size of the header, and therefore * greater than this value, which is defined as smaller than the header size * - * ARM_DRIVER_OK_DONE + * CFSTORE_FLASH_NUMSLOTS + * number of flash journal slots + * + * ARM_DRIVER_OK_DONE * value that indicates an operation has been done i.e. a value > 0 */ #define CFSTORE_KEY_NAME_CHARS_ACCEPTABLE "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}.-_@" @@ -79,6 +87,7 @@ ARM_DRIVER_STORAGE *cfstore_storage_drv = &ARM_Driver_Storage_(0); #define CFSTORE_FILE_CREATE_MODE_DEFAULT (ARM_CFSTORE_FMODE)0 #define CFSTORE_FLASH_STACK_BUF_SIZE 64 #define CFSTORE_FLASH_AREA_SIZE_MIN (sizeof(cfstore_area_header_t) - 1) +#define CFSTORE_FLASH_NUMSLOTS 4 #define cfstore_fsm_null NULL #define CFSTORE_SENTINEL 0x7fffffff #define CFSTORE_CALLBACK_RET_CODE_DEFAULT 0x1 @@ -169,6 +178,7 @@ typedef enum cfstore_fsm_state_t { cfstore_fsm_state_committing, cfstore_fsm_state_resetting, cfstore_fsm_state_ready, /* ready for next flash journal command to arise */ + cfstore_fsm_state_formatting, /* flash formatting in progress */ cfstore_fsm_state_max } cfstore_fsm_state_t; @@ -180,6 +190,7 @@ typedef enum cfstore_fsm_event_t { cfstore_fsm_event_commit_req, cfstore_fsm_event_commit_done, cfstore_fsm_event_reset_done, + cfstore_fsm_event_format_done, cfstore_fsm_event_max, } cfstore_fsm_event_t; @@ -198,6 +209,7 @@ typedef struct cfstore_fsm_t /* strings used for debug trace */ static const char* cfstore_flash_opcode_str[] = { + "FLASH_JOURNAL_OPCODE_FORMAT", "FLASH_JOURNAL_OPCODE_INITIALIZE", "FLASH_JOURNAL_OPCODE_GET_INFO", "FLASH_JOURNAL_OPCODE_READ_BLOB", @@ -215,6 +227,7 @@ static const char* cfstore_flash_state_str[] = "committing", "resetting", "ready", + "formatting", "unknown" }; @@ -226,6 +239,7 @@ static const char* cfstore_flash_event_str[] = "commit_req", "commit_done", "reset_done", + "format_done", "unknown" }; #endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ @@ -727,7 +741,6 @@ void *cfstore_realloc(void *ptr, ARM_CFSTORE_SIZE size) #ifdef CFSTORE_TARGET_LIKE_X86_LINUX_NATIVE -static inline void cfstore_critical_section_init(CFSTORE_LOCK* lock){ *lock = 0; } static inline void cfstore_critical_section_lock(CFSTORE_LOCK* lock, const char* tag){ (void) tag; __sync_fetch_and_add(lock, 1); } static inline void cfstore_critical_section_unlock(CFSTORE_LOCK* lock, const char* tag){(void) tag; __sync_fetch_and_sub(lock, 1); } @@ -764,7 +777,6 @@ static CFSTORE_INLINE int32_t cfstore_hkvt_refcount_inc(cfstore_area_hkvt_t* hkv * Platform Specific Function Implementations */ -static inline void cfstore_critical_section_init(CFSTORE_LOCK* lock){ *lock = 0; } static inline void cfstore_critical_section_unlock(CFSTORE_LOCK* lock, const char* tag) { (void) lock; @@ -1540,6 +1552,9 @@ static void cfstore_flash_journal_callback(int32_t status, FlashJournal_OpCode_t CFSTORE_FENTRYLOG("%s:entered: status=%d, cmd_code=%d (%s)\n", __func__, (int) status, (int) cmd_code, cfstore_flash_opcode_str[cmd_code]); switch(cmd_code) { + case FLASH_JOURNAL_OPCODE_FORMAT: + ctx->fsm.event = cfstore_fsm_event_format_done; + break; case FLASH_JOURNAL_OPCODE_INITIALIZE: ctx->fsm.event = cfstore_fsm_event_init_done; break; @@ -1595,14 +1610,30 @@ static int32_t cfstore_fsm_stop_on_entry(void* context) static int32_t cfstore_fsm_init_on_entry(void* context) { int32_t ret = ARM_DRIVER_ERROR; - const ARM_DRIVER_STORAGE *drv = &ARM_Driver_Storage_(0); cfstore_ctx_t* ctx = (cfstore_ctx_t*) context; CFSTORE_FENTRYLOG("%s:entered\n", __func__); - ret = FlashJournal_initialize(&ctx->jrnl, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, cfstore_flash_journal_callback); + ret = cfstore_svm_init(&cfstore_journal_mtd); + if(ret < ARM_DRIVER_OK){ + CFSTORE_DBGLOG("%s:Error: Unable to initialize storage volume manager\n", __func__); + cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_formatting, ctx); + return ARM_DRIVER_OK; + } + + ret = FlashJournal_initialize(&ctx->jrnl, (ARM_DRIVER_STORAGE *) &cfstore_journal_mtd, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, cfstore_flash_journal_callback); CFSTORE_TP(CFSTORE_TP_FSM, "%s:FlashJournal_initialize ret=%d\n", __func__, (int) ret); if(ret < ARM_DRIVER_OK){ + if(ret == JOURNAL_STATUS_NOT_FORMATTED) { + CFSTORE_DBGLOG("%s:Error: flash not formatted\n", __func__); + cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_formatting, ctx); + return ARM_DRIVER_OK; + } + if(ret == JOURNAL_STATUS_METADATA_ERROR) { + CFSTORE_ERRLOG("%s:Error: flash meta-data (CRC) error detected when initializing flash. Reformatting flash.\n", __func__); + cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_formatting, ctx); + return ARM_DRIVER_OK; + } CFSTORE_ERRLOG("%s:Error: failed to initialize flash journaling layer (ret=%d)\n", __func__, (int) ret); cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_stopped, ctx); } @@ -1993,18 +2024,64 @@ static int32_t cfstore_fsm_ready_on_commit_req(void* context) /* int32_t cfstore_fsm_ready_on_exit(void* context){ (void) context;} */ +/** @brief fsm handler when entering the formatting state + */ +static int32_t cfstore_fsm_format_on_entry(void* context) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_ctx_t* ctx = (cfstore_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + + ret = flashJournalStrategySequential_format((ARM_DRIVER_STORAGE *) &cfstore_journal_mtd, CFSTORE_FLASH_NUMSLOTS, cfstore_flash_journal_callback); + CFSTORE_TP(CFSTORE_TP_FSM, "%s:flashJournalStrategySequential_format ret=%d\n", __func__, (int) ret); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to format flash (ret=%d)\n", __func__, (int) ret); + cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_stopped, ctx); + } + else if(ret > 0){ + /* operation completed synchronously*/ + cfstore_flash_journal_callback(ret, FLASH_JOURNAL_OPCODE_FORMAT); + } + return ret; +} + +/** @brief fsm handler when in formatting state + */ +int32_t cfstore_fsm_formatting(void* context) +{ + int32_t ret = ARM_DRIVER_OK; + cfstore_ctx_t* ctx = (cfstore_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + CFSTORE_ASSERT(ctx->fsm.state == cfstore_fsm_state_formatting); + CFSTORE_ASSERT(ctx->cmd_code == FLASH_JOURNAL_OPCODE_FORMAT); + + /* only change state if status > 0*/ + if(ctx->status > 0){ + ret = cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_initing, ctx); + } else if(ctx->status < 0) { + CFSTORE_ERRLOG("%s:Error: failed to format flash (ret=%d)\n", __func__, (int) ctx->status); + cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_stopped, ctx); + } + return ret; +} + +/* int32_t cfstore_fsm_format_on_exit(void* context){ (void) context;} */ + /* handler functions while in state */ static cfstore_fsm_handler cfstore_flash_fsm[cfstore_fsm_state_max][cfstore_fsm_event_max] = { -/* state\event: init_done read_done log_done commit_req commit_done reset_done */ -/* stopped */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null }, -/* init */ {cfstore_fsm_initing, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null }, -/* reading */ {cfstore_fsm_null, cfstore_fsm_reading, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null }, -/* logging */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_logging, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null }, -/* committing */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_committing, cfstore_fsm_null }, -/* resetting */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null }, -/* ready */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_ready_on_commit_req, cfstore_fsm_null, cfstore_fsm_null }, +/* state\event: init_done read_done log_done commit_req commit_done reset_done format_done, */ +/* stopped */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null }, +/* init */ {cfstore_fsm_initing, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null }, +/* reading */ {cfstore_fsm_null, cfstore_fsm_reading, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null }, +/* logging */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_logging, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null }, +/* committing */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_committing, cfstore_fsm_null, cfstore_fsm_null }, +/* resetting */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null }, +/* ready */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_ready_on_commit_req, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null }, +/* formatting */ {cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_null, cfstore_fsm_formatting }, }; /* handler functions for entering the state*/ @@ -2016,7 +2093,8 @@ cfstore_fsm_handler cfstore_fsm_on_entry[cfstore_fsm_state_max] = cfstore_fsm_log_on_entry, cfstore_fsm_commit_on_entry, cfstore_fsm_null, /* cfstore_fsm_reset_on_entry */ - cfstore_fsm_null /* cfstore_fsm_ready_on_entry */ + cfstore_fsm_null, /* cfstore_fsm_ready_on_entry */ + cfstore_fsm_format_on_entry /* cfstore_fsm_format_on_entry */ }; /* handler functions for exiting state, currently none used */ @@ -2028,7 +2106,8 @@ cfstore_fsm_handler cfstore_fsm_on_exit[cfstore_fsm_state_max] = cfstore_fsm_log_on_exit, cfstore_fsm_commit_on_exit, cfstore_fsm_null, /* cfstore_fsm_reset_on_exit */ - cfstore_fsm_null /* cfstore_fsm_ready_on_exit */ + cfstore_fsm_null, /* cfstore_fsm_ready_on_exit */ + cfstore_fsm_null /* cfstore_fsm_format_on_exit */ }; @@ -2425,8 +2504,8 @@ static bool cfstore_file_is_empty(ARM_CFSTORE_HANDLE hkey) /* @brief See definition in configuration_store.h for description. */ ARM_CFSTORE_CAPABILITIES cfstore_get_capabilities(void) { - /* getting capabilities doesnt change the sram area so this can happen independently of - * an oustanding async operation. its unnecessary to check the fsm state */ + /* getting capabilities doesn't change the sram area so this can happen independently of + * an outstanding async operation. its unnecessary to check the fsm state */ return cfstore_caps_g; } @@ -3866,9 +3945,7 @@ static int32_t cfstore_initialise(ARM_CFSTORE_CALLBACK callback, void* client_co ctx->init_ref_count++; /* initially there is no memory allocated for the area */ CFSTORE_INIT_LIST_HEAD(&ctx->file_list); - /* This is not required here are the lock is statically initialised to 0 - * cfstore_critical_section_init(&ctx->rw_area0_lock); - */ + /* ctx->rw_area0_lock initialisation is not required here as the lock is statically initialised to 0 */ ctx->area_0_head = NULL; ctx->area_0_tail = NULL; diff --git a/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/config.h b/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/config.h deleted file mode 100644 index 4a126107270..00000000000 --- a/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/config.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2006-2016, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __FLASH_JOURNAL_CONFIG_H__ -#define __FLASH_JOURNAL_CONFIG_H__ - -#ifndef SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS -#define SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS 4 -#endif - -#endif /* __FLASH_JOURNAL_CONFIG_H__ */ diff --git a/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/flash_journal_crc.c b/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/flash_journal_crc.c new file mode 100644 index 00000000000..41627f1c22b --- /dev/null +++ b/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/flash_journal_crc.c @@ -0,0 +1,220 @@ +/********************************************************************** + * + * Filename: flash_journal_crc.c + * + * Description: Slow and fast implementations of the CRC standards. + * + * Notes: The parameters for each supported CRC standard are + * defined in the header file crc.h. The implementations + * here should stand up to further additions to that list. + * + * + * Copyright (c) 2000 by Michael Barr. This software is placed into + * the public domain and may be used for any purpose. However, this + * notice must not be changed or removed and no warranty is either + * expressed or implied by its publication or distribution. + **********************************************************************/ + +#include "flash-journal-strategy-sequential/flash_journal_crc.h" + +#define FALSE 0 +#define TRUE !FALSE + +#define CRC_NAME "CRC-32" +#define POLYNOMIAL 0x04C11DB7 +#define INITIAL_REMAINDER 0xFFFFFFFF +#define FINAL_XOR_VALUE 0xFFFFFFFF +#define REFLECT_DATA TRUE +#define REFLECT_REMAINDER TRUE +#define CHECK_VALUE 0xCBF43926 + + +/* + * Derive parameters from the standard-specific parameters in crc.h. + */ +#define WIDTH (8 * sizeof(flash_journal_crc32_t)) +/* U postfix required to suppress the following warning with TOOLCHAIN_ARM: + * #61-D: integer operation result is out of range + */ +#define TOPBIT 0x80000000U + +#if (REFLECT_DATA == TRUE) +#undef REFLECT_DATA +#define REFLECT_DATA(X) ((unsigned char) reflect((X), 8)) +#else +#undef REFLECT_DATA +#define REFLECT_DATA(X) (X) +#endif + +#if (REFLECT_REMAINDER == TRUE) +#undef REFLECT_REMAINDER +#define REFLECT_REMAINDER(X) ((flash_journal_crc32_t) reflect((X), WIDTH)) +#else +#undef REFLECT_REMAINDER +#define REFLECT_REMAINDER(X) (X) +#endif + + +/********************************************************************* + * + * Function: reflect() + * + * Description: Reorder the bits of a binary sequence, by reflecting + * them about the middle position. + * + * Notes: No checking is done that nBits <= 32. + * + * Returns: The reflection of the original data. + * + *********************************************************************/ +static uint32_t +reflect(uint32_t data, unsigned char nBits) +{ + uint32_t reflection = 0x00000000; + unsigned char bit; + + /* + * Reflect the data about the center bit. + */ + for (bit = 0; bit < nBits; ++bit) + { + /* + * If the LSB bit is set, set the reflection of it. + */ + if (data & 0x01) + { + reflection |= (1 << ((nBits - 1) - bit)); + } + + data = (data >> 1); + } + + return (reflection); + +} /* reflect() */ + + +static flash_journal_crc32_t crcTable[256]; +static flash_journal_crc32_t crcEngineRemainder = INITIAL_REMAINDER; + +/********************************************************************* + * + * Function: flashJournalCrcInit() + * + * Description: Populate the partial CRC lookup table. + * + * Notes: This function must be rerun any time the CRC standard + * is changed. If desired, it can be run "offline" and + * the table results stored in an embedded system's ROM. + * + * Returns: None defined. + * + *********************************************************************/ +void +flashJournalCrcInit(void) +{ + flash_journal_crc32_t remainder; + int dividend; + unsigned char bit; + + + /* + * Compute the remainder of each possible dividend. + */ + for (dividend = 0; dividend < 256; ++dividend) + { + /* + * Start with the dividend followed by zeros. + */ + remainder = dividend << (WIDTH - 8); + + /* + * Perform modulo-2 division, a bit at a time. + */ + for (bit = 8; bit > 0; --bit) + { + /* + * Try to divide the current data bit. + */ + if (remainder & TOPBIT) + { + remainder = (remainder << 1) ^ POLYNOMIAL; + } + else + { + remainder = (remainder << 1); + } + } + + /* + * Store the result into the table. + */ + crcTable[dividend] = remainder; + } + +} /* flashJournalCrcInit() */ + +/********************************************************************* + * + * Function: flashJournalCrcReset() + * + * Description: Resets internal state before calling crcCummulative(). + * + * Notes: See the notes to crcCummulative(). + * + * Returns: None defined. + * + *********************************************************************/ +void +flashJournalCrcReset(void) +{ + static unsigned initCalled = 0; + if (!initCalled) { + flashJournalCrcInit(); + initCalled = 1; + } + + crcEngineRemainder = INITIAL_REMAINDER; + +} /* flashJournalCrcReset() */ + +/********************************************************************* + * + * Function: crcCummulative() + * + * Description: Compute the CRC of a group of messages. + * + * Notes: + * This function is intended to be used in the following way: + * - crcReset() is called first to reset internal state before the first + * fragment of a new message is processed with crcCummulative(). + * - crcCummulative() called successfully appending additional message + * fragments to those previously supplied (in order), and returning + * the current crc for the message payload so far. + * + * Returns: The CRC of the message. + * + *********************************************************************/ +flash_journal_crc32_t +flashJournalCrcCummulative(unsigned char const message[], int nBytes) +{ + unsigned char data; + int byte; + + + /* + * Divide the message by the polynomial, a byte at a time. + */ + for (byte = 0; byte < nBytes; ++byte) + { + data = REFLECT_DATA(message[byte]) ^ (crcEngineRemainder >> (WIDTH - 8)); + crcEngineRemainder = crcTable[data] ^ (crcEngineRemainder << 8); + } + + /* + * The final remainder is the CRC. + */ + return (REFLECT_REMAINDER(crcEngineRemainder) ^ FINAL_XOR_VALUE); + +} /* crcCummulative() */ + diff --git a/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/flash_journal_crc.h b/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/flash_journal_crc.h new file mode 100644 index 00000000000..4c8a5adf1d0 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/flash_journal_crc.h @@ -0,0 +1,34 @@ +/********************************************************************** + * + * Filename: flash_journal_crc.h.h + * + * Description: A header file describing the various CRC standards. + * + * Notes: + * + * + * Copyright (c) 2000 by Michael Barr. This software is placed into + * the public domain and may be used for any purpose. However, this + * notice must not be changed or removed and no warranty is either + * expressed or implied by its publication or distribution. + **********************************************************************/ + +#ifndef _flash_journal_crc_h +#define _flash_journal_crc_h + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint32_t flash_journal_crc32_t; + +void flashJournalCrcReset(void); +flash_journal_crc32_t flashJournalCrcCummulative(unsigned char const message[], int nBytes); + +#ifdef __cplusplus +} +#endif + +#endif /* _flash_journal_crc_h */ diff --git a/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/flash_journal_private.h b/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/flash_journal_private.h index 207367745b6..f2c2a9f14fb 100644 --- a/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/flash_journal_private.h +++ b/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/flash_journal_private.h @@ -23,11 +23,22 @@ extern "C" { #endif // __cplusplus #include "flash-journal/flash_journal.h" -#include "flash-journal-strategy-sequential/config.h" + +static inline uint32_t roundUp_uint32(uint32_t N, uint32_t BOUNDARY) { + return ((((N) + (BOUNDARY) - 1) / (BOUNDARY)) * (BOUNDARY)); +} +static inline uint32_t roundDown_uint32(uint32_t N, uint32_t BOUNDARY) { + return (((N) / (BOUNDARY)) * (BOUNDARY)); +} + +#define LCM_OF_ALL_ERASE_UNITS 4096 /* Assume an LCM of erase_units for now. This will be generalized later. */ static const uint32_t SEQUENTIAL_FLASH_JOURNAL_INVALD_NEXT_SEQUENCE_NUMBER = 0xFFFFFFFFUL; -static const uint32_t SEQUENTIAL_FLASH_JOURNAL_VERSION = 1; -static const uint32_t SEQUENTIAL_FLASH_JOURNAL_MAGIC = 0xCE02102AUL; +static const uint32_t SEQUENTIAL_FLASH_JOURNAL_MAGIC = 0xCE02102AUL; +static const uint32_t SEQUENTIAL_FLASH_JOURNAL_VERSION = 1; +static const uint32_t SEQUENTIAL_FLASH_JOURNAL_HEADER_MAGIC = 0xCEA00AEEUL; +static const uint32_t SEQUENTIAL_FLASH_JOURNAL_HEADER_VERSION = 1; + typedef enum { SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED, @@ -41,6 +52,20 @@ typedef enum { SEQUENTIAL_JOURNAL_STATE_READING, } SequentialFlashJournalState_t; +/** + * Meta-data placed at the head of a Journal. The actual header would be an + * extension of this generic header, and would depend on the implementation + * strategy. Initialization algorithms can expect to find this generic header at + * the start of every Journal. + */ +typedef struct _SequentialFlashJournalHeader { + FlashJournalHeader_t genericHeader; /** Generic meta-data placed at the head of a Journal; common to all journal types. */ + uint32_t magic; /** Sequential journal header specific magic code. */ + uint32_t version; /** Revision number for this sequential journal header. */ + uint32_t numSlots; /** Maximum number of logged blobs; i.e. maximum number of versions of the journaled payload. */ + uint32_t sizeofSlot; /** Slot size. Each slot holds a header, blob-payload, and a tail. */ +} SequentialFlashJournalHeader_t; + /** * Meta-data placed at the head of a sequential-log entry. */ @@ -62,9 +87,12 @@ typedef struct _SequentialFlashJournalLogHead { * a partially written log-entry-tail won't be accepted as valid. */ typedef struct _SequentialFlashJournalLogTail { - uint64_t sizeofBlob; /**< the size of the payload in this blob. */ + uint32_t sizeofBlob; /**< the size of the payload in this blob. */ uint32_t magic; uint32_t sequenceNumber; + uint32_t crc32; /**< This field contains the CRC of the header, body (only including logged data), + * and the tail. The 'CRC32' field is assumed to hold 0x0 for the purpose of + * computing the CRC */ } SequentialFlashJournalLogTail_t; #define SEQUENTIAL_JOURNAL_VALID_TAIL(TAIL_PTR) ((TAIL_PTR)->magic == SEQUENTIAL_FLASH_JOURNAL_MAGIC) @@ -76,7 +104,9 @@ typedef struct _SequentialFlashJournal_t { ARM_DRIVER_STORAGE *mtd; /**< The underlying Memory-Technology-Device. */ ARM_STORAGE_CAPABILITIES mtdCapabilities; /**< the return from mtd->GetCapabilities(); held for quick reference. */ uint64_t mtdStartOffset; /**< the start of the address range maintained by the underlying MTD. */ - uint32_t sequentialSkip; /**< size of the log stride. */ + uint32_t firstSlotOffset; /** Offset from the start of the journal header to the actual logged journal. */ + uint32_t numSlots; /** Maximum number of logged blobs; i.e. maximum number of versions of the journaled payload. */ + uint32_t sizeofSlot; /**< size of the log stride. */ uint32_t nextSequenceNumber; /**< the next valid sequence number to be used when logging the next blob. */ uint32_t currentBlobIndex; /**< index of the most recently written blob. */ SequentialFlashJournalState_t state; /**< state of the journal. SEQUENTIAL_JOURNAL_STATE_INITIALIZED being the default. */ @@ -90,12 +120,9 @@ typedef struct _SequentialFlashJournal_t { /** state relevant to initialization. */ struct { uint64_t currentOffset; - union { - SequentialFlashJournalLogHead_t head; - struct { - uint32_t headSequenceNumber; - SequentialFlashJournalLogTail_t tail; - }; + struct { + uint32_t headSequenceNumber; + SequentialFlashJournalLogTail_t tail; }; } initScan; @@ -105,11 +132,11 @@ typedef struct _SequentialFlashJournal_t { size_t sizeofBlob; union { struct { - uint64_t eraseOffset; + uint64_t mtdEraseOffset; }; struct { - uint64_t offset; /**< the current offset at which data is being written. */ - uint64_t tailOffset; /**< offset at which the SequentialFlashJournalLogTail_t will be logged for this log-entry. */ + uint64_t mtdOffset; /**< the current Storage offset at which data will be written. */ + uint64_t mtdTailOffset; /**< Storage offset at which the SequentialFlashJournalLogTail_t will be logged for this log-entry. */ const uint8_t *dataBeingLogged; /**< temporary pointer aimed at the next data to be logged. */ size_t amountLeftToLog; union { @@ -124,10 +151,10 @@ typedef struct _SequentialFlashJournal_t { struct { const uint8_t *blob; /**< the original buffer holding source data. */ size_t sizeofBlob; - uint64_t offset; /**< the current offset at which data is being written. */ + uint64_t mtdOffset; /**< the current Storage offset from which data is being read. */ uint8_t *dataBeingRead; /**< temporary pointer aimed at the next data to be read-into. */ size_t amountLeftToRead; - size_t totalDataRead; /**< the total data that has been read off the blob so far. */ + size_t logicalOffset; /**< the logical offset within the blob at which the next read will occur. */ } read; }; } SequentialFlashJournal_t; @@ -139,6 +166,8 @@ typedef struct _SequentialFlashJournal_t { */ typedef char AssertSequentialJournalSizeLessThanOrEqualToGenericJournal[sizeof(SequentialFlashJournal_t)<=sizeof(FlashJournal_t)?1:-1]; +#define SLOT_ADDRESS(JOURNAL, INDEX) ((JOURNAL)->mtdStartOffset + (JOURNAL)->firstSlotOffset + ((INDEX) * (JOURNAL)->sizeofSlot)) + #ifdef __cplusplus } #endif // __cplusplus diff --git a/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/flash_journal_strategy_sequential.h b/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/flash_journal_strategy_sequential.h index 12421877908..3b1c7ac38d1 100644 --- a/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/flash_journal_strategy_sequential.h +++ b/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/flash_journal_strategy_sequential.h @@ -24,12 +24,118 @@ extern "C" { #include "flash-journal/flash_journal.h" +/** + * Create/format a sequential flash journal at a given offset within a storage + * device and with a given slot-cardinality. + * + * This function must be called *once* for each incarnation of a sequential + * journal. + * + * @param[in] mtd + * The underlying Storage driver. + * + * @param[in] numSlots + * Number of slots in the sequential journal. Each slot holds a header, blob-payload, and a tail. + * + * @param[in] callback + * Caller-defined callback to be invoked upon command completion + * in case the storage device executes operations asynchronously. + * Use a NULL pointer when no callback signals are required. + * + * @note: this is an asynchronous operation, but it can finish + * synchronously if the underlying MTD supports that. + * + * @return + * The function executes in the following ways: + * - When the operation is asynchronous, the function only starts the + * initialization and control returns to the caller with an + * JOURNAL_STATUS_OK before the actual completion of the operation (or with + * an appropriate error code in case of failure). When the operation is + * completed the command callback is invoked with 1 passed in as the + * 'status' parameter of the callback. In case of errors, the completion + * callback is invoked with an error status. + * - When the operation is executed by the journal in a blocking (i.e. + * synchronous) manner, control returns to the caller only upon the actual + * completion of the operation or the discovery of a failure condition. In + * this case, the function returns 1 to signal successful synchronous + * completion or an appropriate error code, and no further + * invocation of the completion callback should be expected at a later time. + * + * Here's a code snippet to suggest how this API might be used by callers: + * \code + * ASSERT(JOURNAL_STATUS_OK == 0); // this is a precondition; it doesn't need to be put in code + * int32_t returnValue = flashJournalStrategySequential_format(MTD, numSlots, callbackHandler); + * if (returnValue < JOURNAL_STATUS_OK) { + * // handle error + * } else if (returnValue == JOURNAL_STATUS_OK) { + * ASSERT(MTD->GetCapabilities().asynchronous_ops == 1); + * // handle early return from asynchronous execution + * } else { + * ASSERT(returnValue == 1); + * // handle synchronous completion + * } + * \endcode + * + * +-------------------------------+ ^ + * | | | + * | Journal Header | | + * | starts with generic header | | + * | followed by specific header | | + * | | | multiple of program_unit + * +-------------------------------+ | and erase-boundary + * +-------------------------------+ | + * | | | + * | padding to allow alignment | | + * | | | + * +-------------------------------+ v + * +-------------------------------+ + * | +---------------------------+ | ^ + * | | slot header | | | + * | | aligned with program_unit| | | + * | +---------------------------+ | | slot 0 + * | | | aligned with LCM of all erase boundaries + * | | | + * | | | + * | | | + * | BLOB0 | | + * | | | + * | | | + * | +---------------------------+ | | + * | | slot tail | | | + * | | aligned with program_unit| | | + * | +---------------------------+ | | + * +-------------------------------+ v + * +-------------------------------+ + * | +---------------------------+ | ^ + * | | slot header | | | + * | | aligned with program_unit| | | + * | +---------------------------+ | | slot 1 + * | | | aligned with LCM of all erase boundaries + * | BLOB1 | | + * | | | + * . . . + * . . . + * + * . . . + * . BLOB(N-1) . . + * | | | + * | +---------------------------+ | | slot 'N - 1' + * | | slot tail | | | aligned with LCM of all erase boundaries + * | | aligned with program_unit| | | + * | +---------------------------+ | | + * +-------------------------------+ v + */ +int32_t flashJournalStrategySequential_format(ARM_DRIVER_STORAGE *mtd, + uint32_t numSlots, + FlashJournal_Callback_t callback); + int32_t flashJournalStrategySequential_initialize(FlashJournal_t *journal, ARM_DRIVER_STORAGE *mtd, const FlashJournal_Ops_t *ops, FlashJournal_Callback_t callback); FlashJournal_Status_t flashJournalStrategySequential_getInfo(FlashJournal_t *journal, FlashJournal_Info_t *info); int32_t flashJournalStrategySequential_read(FlashJournal_t *journal, void *blob, size_t n); +int32_t flashJournalStrategySequential_readFrom(FlashJournal_t *journal, size_t offset, void *blob, size_t n); int32_t flashJournalStrategySequential_log(FlashJournal_t *journal, const void *blob, size_t n); int32_t flashJournalStrategySequential_commit(FlashJournal_t *journal); int32_t flashJournalStrategySequential_reset(FlashJournal_t *journal); @@ -38,6 +144,7 @@ static const FlashJournal_Ops_t FLASH_JOURNAL_STRATEGY_SEQUENTIAL = { flashJournalStrategySequential_initialize, flashJournalStrategySequential_getInfo, flashJournalStrategySequential_read, + flashJournalStrategySequential_readFrom, flashJournalStrategySequential_log, flashJournalStrategySequential_commit, flashJournalStrategySequential_reset diff --git a/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/strategy.c b/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/strategy.c index 5a4a715868f..598e206da1c 100644 --- a/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/strategy.c +++ b/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/strategy.c @@ -15,6 +15,7 @@ * limitations under the License. */ +#include "flash-journal-strategy-sequential/flash_journal_crc.h" #include "flash-journal-strategy-sequential/flash_journal_private.h" #include "flash-journal-strategy-sequential/flash_journal_strategy_sequential.h" #include "support_funcs.h" @@ -23,82 +24,101 @@ SequentialFlashJournal_t *activeJournal; -/* forward declarations */ -void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation); +/* + * forward declarations of static-inline helper functions. + */ +static inline int32_t mtdGetTotalCapacity(ARM_DRIVER_STORAGE *mtd, uint64_t *capacityP); +static inline int32_t flashJournalStrategySequential_format_sanityChecks(ARM_DRIVER_STORAGE *mtd, uint32_t numSlots); +static inline int32_t flashJournalStrategySequential_read_sanityChecks(SequentialFlashJournal_t *journal, const void *blob, size_t sizeofBlob); +static inline int32_t flashJournalStrategySequential_log_sanityChecks(SequentialFlashJournal_t *journal, const void *blob, size_t sizeofBlob); +static inline int32_t flashJournalStrategySequential_commit_sanityChecks(SequentialFlashJournal_t *journal); -static inline int32_t mtdGetTotalCapacity(ARM_DRIVER_STORAGE *mtd, uint64_t *capacityP) + +int32_t flashJournalStrategySequential_format(ARM_DRIVER_STORAGE *mtd, + uint32_t numSlots, + FlashJournal_Callback_t callback) { - /* fetch MTD's INFO */ + int32_t rc; + if ((rc = flashJournalStrategySequential_format_sanityChecks(mtd, numSlots)) != JOURNAL_STATUS_OK) { + return rc; + } + ARM_STORAGE_INFO mtdInfo; - int32_t rc = mtd->GetInfo(&mtdInfo); - if (rc != ARM_DRIVER_OK) { + if (mtd->GetInfo(&mtdInfo) < ARM_DRIVER_OK) { + return JOURNAL_STATUS_STORAGE_API_ERROR; + } + uint64_t mtdAddr; + if (mtdGetStartAddr(mtd, &mtdAddr) < JOURNAL_STATUS_OK) { return JOURNAL_STATUS_STORAGE_API_ERROR; } - *capacityP = mtdInfo.total_storage; - return JOURNAL_STATUS_OK; -} + formatInfoSingleton.mtd = mtd; + formatInfoSingleton.mtdAddr = mtdAddr; + formatInfoSingleton.callback = callback; + formatInfoSingleton.mtdProgramUnit = mtdInfo.program_unit; -static inline int32_t flashJournalStrategySequential_read_sanityChecks(SequentialFlashJournal_t *journal, const void *blob, size_t sizeofBlob) -{ - if ((journal == NULL) || (blob == NULL) || (sizeofBlob == 0)) { - return JOURNAL_STATUS_PARAMETER; - } - if ((journal->state == SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED) || (journal->state == SEQUENTIAL_JOURNAL_STATE_INIT_SCANNING_LOG_HEADERS)) { - return JOURNAL_STATUS_NOT_INITIALIZED; + if ((rc = setupSequentialJournalHeader(&formatInfoSingleton.header, mtd, mtdInfo.total_storage, numSlots)) != JOURNAL_STATUS_OK) { + return rc; } - if (journal->state != SEQUENTIAL_JOURNAL_STATE_INITIALIZED) { - return JOURNAL_STATUS_ERROR; /* journal is in an un-expected state. */ + + /* initialize MTD */ + rc = mtd->Initialize(formatHandler); + if (rc < ARM_DRIVER_OK) { + return JOURNAL_STATUS_STORAGE_API_ERROR; + } else if (rc == ARM_DRIVER_OK) { + return JOURNAL_STATUS_OK; /* An asynchronous operation is pending; it will result in a completion callback + * where the rest of processing will take place. */ } - // printf("read sanity checks: totalDataRead = %lu, sizeofJournaledBlob = %lu\n", (uint32_t)journal->read.totalDataRead, (uint32_t)journal->info.sizeofJournaledBlob); - if ((journal->info.sizeofJournaledBlob == 0) || (journal->read.totalDataRead == journal->info.sizeofJournaledBlob)) { - journal->read.totalDataRead = 0; - return JOURNAL_STATUS_EMPTY; + if (rc != 1) { + return JOURNAL_STATUS_STORAGE_API_ERROR; /* synchronous completion is expected to return 1 */ } - return JOURNAL_STATUS_OK; + /* progress the rest of the create state-machine */ + return flashJournalStrategySequential_format_progress(ARM_DRIVER_OK, ARM_STORAGE_OPERATION_INITIALIZE); } -static inline int32_t flashJournalStrategySequential_log_sanityChecks(SequentialFlashJournal_t *journal, const void *blob, size_t sizeofBlob) +/** + * Validate a header at the start of the MTD. + * + * @param [in/out] headerP + * Caller-allocated header which gets filled in during validation. + + * @return JOURNAL_STATUS_OK if the header is sane. As a side-effect, the memory + * pointed to by 'headerP' is initialized with the header. + */ +int32_t readAndVerifyJournalHeader(SequentialFlashJournal_t *journal, SequentialFlashJournalHeader_t *headerP) { - if ((journal == NULL) || (blob == NULL) || (sizeofBlob == 0)) { + if (headerP == NULL) { return JOURNAL_STATUS_PARAMETER; } - if ((journal->state == SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED) || (journal->state == SEQUENTIAL_JOURNAL_STATE_INIT_SCANNING_LOG_HEADERS)) { - return JOURNAL_STATUS_NOT_INITIALIZED; - } - if ((journal->state != SEQUENTIAL_JOURNAL_STATE_INITIALIZED) && (journal->state != SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY)) { - return JOURNAL_STATUS_ERROR; /* journal is in an un-expected state. */ - } - if (journal->state == SEQUENTIAL_JOURNAL_STATE_INITIALIZED) { - if (sizeofBlob > journal->info.capacity) { - return JOURNAL_STATUS_BOUNDED_CAPACITY; /* adding this log chunk would cause us to exceed capacity (write past the tail). */ - } - } else if (journal->state == SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY) { - if (journal->log.offset + sizeofBlob > journal->log.tailOffset) { - return JOURNAL_STATUS_BOUNDED_CAPACITY; /* adding this log chunk would cause us to exceed capacity (write past the tail). */ + + int32_t rc = journal->mtd->ReadData(journal->mtdStartOffset, headerP, sizeof(SequentialFlashJournalHeader_t)); + if (rc < ARM_DRIVER_OK) { + return JOURNAL_STATUS_STORAGE_IO_ERROR; + } else if (rc == ARM_DRIVER_OK) { + ARM_STORAGE_CAPABILITIES mtdCaps = journal->mtd->GetCapabilities(); + if (!mtdCaps.asynchronous_ops) { + return JOURNAL_STATUS_ERROR; /* asynchronous_ops must be set if MTD returns ARM_DRIVER_OK. */ } - } - return JOURNAL_STATUS_OK; -} + return JOURNAL_STATUS_ERROR; /* TODO: handle init with pending asynchronous activity. */ + } -static inline int32_t flashJournalStrategySequential_commit_sanityChecks(SequentialFlashJournal_t *journal) -{ - if (journal == NULL) { - return JOURNAL_STATUS_PARAMETER; + if ((headerP->genericHeader.magic != FLASH_JOURNAL_HEADER_MAGIC) || + (headerP->genericHeader.version != FLASH_JOURNAL_HEADER_VERSION) || + (headerP->genericHeader.sizeofHeader != sizeof(SequentialFlashJournalHeader_t)) || + (headerP->magic != SEQUENTIAL_FLASH_JOURNAL_HEADER_MAGIC) || + (headerP->version != SEQUENTIAL_FLASH_JOURNAL_HEADER_VERSION)) { + return JOURNAL_STATUS_NOT_FORMATTED; } - if (journal->state == SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY) { - if (journal->prevCommand != FLASH_JOURNAL_OPCODE_LOG_BLOB) { - return JOURNAL_STATUS_ERROR; - } - if ((journal->log.offset == ARM_STORAGE_INVALID_OFFSET) || - (journal->log.tailOffset == ARM_STORAGE_INVALID_OFFSET) || - (journal->log.tailOffset < journal->log.offset) || - (journal->log.tail.sizeofBlob == 0) || - (journal->log.tail.sizeofBlob > journal->info.capacity)) { - return JOURNAL_STATUS_ERROR; /* journal is in an un-expected state. */ - } + + uint32_t expectedCRC = headerP->genericHeader.checksum; + headerP->genericHeader.checksum = 0; + flashJournalCrcReset(); + uint32_t computedCRC = flashJournalCrcCummulative((const unsigned char *)&headerP->genericHeader, sizeof(SequentialFlashJournalLogHead_t)); + if (computedCRC != expectedCRC) { + //printf("readAndVerifyJournalHeader: checksum mismatch during header verification: expected = %u, computed = %u\n", (unsigned int) expectedCRC, (unsigned int) computedCRC); + return JOURNAL_STATUS_METADATA_ERROR; } return JOURNAL_STATUS_OK; @@ -111,9 +131,30 @@ int32_t flashJournalStrategySequential_initialize(FlashJournal_t *_jou { int32_t rc; + /* initialize MTD */ + rc = mtd->Initialize(mtdHandler); + if (rc < ARM_DRIVER_OK) { + memset(_journal, 0, sizeof(FlashJournal_t)); + return JOURNAL_STATUS_STORAGE_API_ERROR; + } + if (rc == ARM_DRIVER_OK) { + ARM_STORAGE_CAPABILITIES mtdCaps = mtd->GetCapabilities(); + if (!mtdCaps.asynchronous_ops) { + return JOURNAL_STATUS_ERROR; /* asynchronous_ops must be set if MTD returns ARM_DRIVER_OK. */ + } + + return JOURNAL_STATUS_ERROR; /* TODO: handle init with pending asynchronous activity. */ + } + SequentialFlashJournal_t *journal; - activeJournal = journal = (SequentialFlashJournal_t *)_journal; - journal->state = SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED; + activeJournal = journal = (SequentialFlashJournal_t *)_journal; + journal->state = SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED; + journal->mtd = mtd; + + /* Setup start address within MTD. */ + if ((rc = mtdGetStartAddr(journal->mtd, &journal->mtdStartOffset)) != JOURNAL_STATUS_OK) { + return rc; + } /* fetch MTD's total capacity */ uint64_t mtdCapacity; @@ -125,27 +166,27 @@ int32_t flashJournalStrategySequential_initialize(FlashJournal_t *_jou return JOURNAL_STATUS_STORAGE_API_ERROR; } + SequentialFlashJournalHeader_t journalHeader; + if ((rc = readAndVerifyJournalHeader(journal, &journalHeader)) != JOURNAL_STATUS_OK) { + return rc; + } + /* initialize the journal structure */ memcpy(&journal->ops, ops, sizeof(FlashJournal_Ops_t)); - journal->mtd = mtd; journal->mtdCapabilities = mtd->GetCapabilities(); /* fetch MTD's capabilities */ - journal->sequentialSkip = mtdCapacity / SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS; - journal->info.capacity = journal->sequentialSkip - (sizeof(SequentialFlashJournalLogHead_t) + sizeof(SequentialFlashJournalLogTail_t)); /* effective capacity */ + + journal->firstSlotOffset = journalHeader.genericHeader.journalOffset; + journal->numSlots = journalHeader.numSlots; + journal->sizeofSlot = journalHeader.sizeofSlot; + + /* effective capacity */ + journal->info.capacity = journal->sizeofSlot + - roundUp_uint32(sizeof(SequentialFlashJournalLogHead_t), mtdInfo.program_unit) + - roundUp_uint32(sizeof(SequentialFlashJournalLogTail_t), mtdInfo.program_unit); journal->info.program_unit = mtdInfo.program_unit; journal->callback = callback; journal->prevCommand = FLASH_JOURNAL_OPCODE_INITIALIZE; - /* initialize MTD */ - ARM_STORAGE_CAPABILITIES mtdCaps = mtd->GetCapabilities(); - rc = mtd->Initialize(mtdHandler); - if (rc < ARM_DRIVER_OK) { - memset(journal, 0, sizeof(FlashJournal_t)); - return JOURNAL_STATUS_STORAGE_API_ERROR; - } - if ((mtdCaps.asynchronous_ops) && (rc == ARM_DRIVER_OK)) { - return JOURNAL_STATUS_OK; /* we've got pending asynchronous activity. */ - } - if ((rc = discoverLatestLoggedBlob(journal)) != JOURNAL_STATUS_OK) { return rc; } @@ -167,6 +208,10 @@ int32_t flashJournalStrategySequential_read(FlashJournal_t *_journal, void *blob SequentialFlashJournal_t *journal; activeJournal = journal = (SequentialFlashJournal_t *)_journal; + if (journal->prevCommand != FLASH_JOURNAL_OPCODE_READ_BLOB) { + journal->read.logicalOffset = 0; + } + int32_t rc; if ((rc = flashJournalStrategySequential_read_sanityChecks(journal, blob, sizeofBlob)) != JOURNAL_STATUS_OK) { return rc; @@ -175,18 +220,53 @@ int32_t flashJournalStrategySequential_read(FlashJournal_t *_journal, void *blob journal->read.blob = blob; journal->read.sizeofBlob = sizeofBlob; - if ((journal->prevCommand != FLASH_JOURNAL_OPCODE_READ_BLOB) || (journal->read.totalDataRead == 0)) { - journal->read.offset = journal->mtdStartOffset - + (journal->currentBlobIndex * journal->sequentialSkip) - + sizeof(SequentialFlashJournalLogHead_t); - journal->read.totalDataRead = 0; + if (journal->read.logicalOffset == 0) { + { /* Establish the sanity of this slot before proceeding with the read. */ + uint32_t headSequenceNumber; + SequentialFlashJournalLogTail_t tail; + if (slotIsSane(journal, + SLOT_ADDRESS(journal, journal->currentBlobIndex), + &headSequenceNumber, + &tail) != 1) { + /* TODO: rollback to an older slot. */ + return JOURNAL_STATUS_STORAGE_IO_ERROR; + } + } + + journal->read.mtdOffset = SLOT_ADDRESS(journal, journal->currentBlobIndex) + sizeof(SequentialFlashJournalLogHead_t); } else { /* journal->read.offset is already set from the previous read execution */ // printf("flashJournalStrategySequential_read: continuing read of %lu from offset %lu\n", sizeofBlob, (uint32_t)journal->read.offset); } journal->read.dataBeingRead = blob; - journal->read.amountLeftToRead = ((journal->info.sizeofJournaledBlob - journal->read.totalDataRead) < sizeofBlob) ? - (journal->info.sizeofJournaledBlob - journal->read.totalDataRead) : sizeofBlob; + journal->read.amountLeftToRead = ((journal->info.sizeofJournaledBlob - journal->read.logicalOffset) < sizeofBlob) ? + (journal->info.sizeofJournaledBlob - journal->read.logicalOffset) : sizeofBlob; + // printf("amount left to read %u\n", journal->read.amountLeftToRead); + + journal->state = SEQUENTIAL_JOURNAL_STATE_READING; + journal->prevCommand = FLASH_JOURNAL_OPCODE_READ_BLOB; + return flashJournalStrategySequential_read_progress(); +} + +int32_t flashJournalStrategySequential_readFrom(FlashJournal_t *_journal, size_t offset, void *blob, size_t sizeofBlob) +{ + SequentialFlashJournal_t *journal; + activeJournal = journal = (SequentialFlashJournal_t *)_journal; + + journal->read.logicalOffset = offset; + int32_t rc; + if ((rc = flashJournalStrategySequential_read_sanityChecks(journal, blob, sizeofBlob)) != JOURNAL_STATUS_OK) { + return rc; + } + + journal->read.blob = blob; + journal->read.sizeofBlob = sizeofBlob; + + journal->read.mtdOffset = SLOT_ADDRESS(journal, journal->currentBlobIndex) + sizeof(SequentialFlashJournalLogHead_t) + offset; + + journal->read.dataBeingRead = blob; + journal->read.amountLeftToRead = ((journal->info.sizeofJournaledBlob - journal->read.logicalOffset) < sizeofBlob) ? + (journal->info.sizeofJournaledBlob - journal->read.logicalOffset) : sizeofBlob; // printf("amount left to read %u\n", journal->read.amountLeftToRead); journal->state = SEQUENTIAL_JOURNAL_STATE_READING; @@ -208,24 +288,27 @@ int32_t flashJournalStrategySequential_log(FlashJournal_t *_journal, const void journal->log.sizeofBlob = size; if (journal->prevCommand != FLASH_JOURNAL_OPCODE_LOG_BLOB) { + /* + * This is the first log in the sequence. We have to begin by identifying a new slot and erasing it. + */ + + /* choose the next slot */ uint32_t logBlobIndex = journal->currentBlobIndex + 1; - if (logBlobIndex == SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS) { + if (logBlobIndex == journal->numSlots) { logBlobIndex = 0; } - journal->log.eraseOffset = journal->mtdStartOffset + (logBlobIndex * journal->sequentialSkip); - - /* ensure that the request is at least as large as the minimum program unit */ - if (size < journal->info.program_unit) { - return JOURNAL_STATUS_SMALL_LOG_REQUEST; - } - journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_ERASE; /* start with erasing the log region */ - journal->prevCommand = FLASH_JOURNAL_OPCODE_LOG_BLOB; + /* setup an erase for the slot */ + journal->log.mtdEraseOffset = SLOT_ADDRESS(journal, logBlobIndex); + journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_ERASE; /* start with erasing the log region */ + journal->prevCommand = FLASH_JOURNAL_OPCODE_LOG_BLOB; } else { + /* This is a continuation of an ongoing logging sequence. */ journal->log.dataBeingLogged = blob; journal->log.amountLeftToLog = size; } + /* progress the state machine for log() */ return flashJournalStrategySequential_log_progress(); } @@ -240,17 +323,21 @@ int32_t flashJournalStrategySequential_commit(FlashJournal_t *_journal) } if (journal->prevCommand == FLASH_JOURNAL_OPCODE_LOG_BLOB) { - journal->log.offset = journal->log.tailOffset; + /* the tail has already been setup during previous calls to log(); we can now include it in the crc32. */ + journal->log.tail.crc32 = flashJournalCrcCummulative((const unsigned char *)&journal->log.tail, sizeof(SequentialFlashJournalLogTail_t)); + flashJournalCrcReset(); + + journal->log.mtdOffset = journal->log.mtdTailOffset; journal->log.dataBeingLogged = (const uint8_t *)&journal->log.tail; journal->log.amountLeftToLog = sizeof(SequentialFlashJournalLogTail_t); journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_TAIL; } else { uint32_t logBlobIndex = journal->currentBlobIndex + 1; - if (logBlobIndex == SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS) { + if (logBlobIndex == journal->numSlots) { logBlobIndex = 0; } - journal->log.eraseOffset = journal->mtdStartOffset + (logBlobIndex * journal->sequentialSkip); - journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_ERASE; + journal->log.mtdEraseOffset = SLOT_ADDRESS(journal, logBlobIndex); + journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_ERASE; } journal->prevCommand = FLASH_JOURNAL_OPCODE_COMMIT; @@ -267,3 +354,124 @@ int32_t flashJournalStrategySequential_reset(FlashJournal_t *_journal) journal->prevCommand = FLASH_JOURNAL_OPCODE_RESET; return flashJournalStrategySequential_reset_progress(); } + +int32_t mtdGetTotalCapacity(ARM_DRIVER_STORAGE *mtd, uint64_t *capacityP) +{ + /* fetch MTD's INFO */ + ARM_STORAGE_INFO mtdInfo; + int32_t rc = mtd->GetInfo(&mtdInfo); + if (rc != ARM_DRIVER_OK) { + return JOURNAL_STATUS_STORAGE_API_ERROR; + } + *capacityP = mtdInfo.total_storage; + + return JOURNAL_STATUS_OK; +} + +int32_t flashJournalStrategySequential_format_sanityChecks(ARM_DRIVER_STORAGE *mtd, uint32_t numSlots) +{ + /* + * basic parameter checking + */ + if ((mtd == NULL) || (numSlots == 0)) { + return JOURNAL_STATUS_PARAMETER; + } + + ARM_STORAGE_INFO mtdInfo; + if (mtd->GetInfo(&mtdInfo) < ARM_DRIVER_OK) { + return JOURNAL_STATUS_STORAGE_API_ERROR; + } + if (mtdInfo.total_storage == 0) { + return JOURNAL_STATUS_STORAGE_API_ERROR; + } + + uint64_t mtdAddr; + if (mtdGetStartAddr(mtd, &mtdAddr) < JOURNAL_STATUS_OK) { + return JOURNAL_STATUS_STORAGE_API_ERROR; + } + if (mtd->GetBlock(mtdAddr, NULL) < ARM_DRIVER_OK) { /* check validity of journal's start address */ + return JOURNAL_STATUS_PARAMETER; + } + if (mtd->GetBlock(mtdAddr + mtdInfo.total_storage - 1, NULL) < ARM_DRIVER_OK) { /* check validity of the journal's end address */ + return JOURNAL_STATUS_PARAMETER; + } + + if ((mtdAddr % mtdInfo.program_unit) != 0) { /* ensure that the journal starts at a programmable unit */ + return JOURNAL_STATUS_PARAMETER; + } + if ((mtdAddr % LCM_OF_ALL_ERASE_UNITS) != 0) { /* ensure that the journal starts and ends at an erase-boundary */ + return JOURNAL_STATUS_PARAMETER; + } + + return JOURNAL_STATUS_OK; +} + +int32_t flashJournalStrategySequential_read_sanityChecks(SequentialFlashJournal_t *journal, const void *blob, size_t sizeofBlob) +{ + if ((journal == NULL) || (blob == NULL) || (sizeofBlob == 0)) { + return JOURNAL_STATUS_PARAMETER; + } + if ((journal->state == SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED) || (journal->state == SEQUENTIAL_JOURNAL_STATE_INIT_SCANNING_LOG_HEADERS)) { + return JOURNAL_STATUS_NOT_INITIALIZED; + } + if (journal->state != SEQUENTIAL_JOURNAL_STATE_INITIALIZED) { + return JOURNAL_STATUS_ERROR; /* journal is in an un-expected state. */ + } + // printf("read sanity checks: logicalOffset = %lu, sizeofJournaledBlob = %lu\n", (uint32_t)journal->read.logicalOffset, (uint32_t)journal->info.sizeofJournaledBlob); + if ((journal->info.sizeofJournaledBlob == 0) || (journal->read.logicalOffset >= journal->info.sizeofJournaledBlob)) { + journal->read.logicalOffset = 0; + return JOURNAL_STATUS_EMPTY; + } + + return JOURNAL_STATUS_OK; +} + +int32_t flashJournalStrategySequential_log_sanityChecks(SequentialFlashJournal_t *journal, const void *blob, size_t sizeofBlob) +{ + if ((journal == NULL) || (blob == NULL) || (sizeofBlob == 0)) { + return JOURNAL_STATUS_PARAMETER; + } + if ((journal->state == SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED) || (journal->state == SEQUENTIAL_JOURNAL_STATE_INIT_SCANNING_LOG_HEADERS)) { + return JOURNAL_STATUS_NOT_INITIALIZED; + } + if ((journal->state != SEQUENTIAL_JOURNAL_STATE_INITIALIZED) && (journal->state != SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY)) { + return JOURNAL_STATUS_ERROR; /* journal is in an un-expected state. */ + } + if (journal->state == SEQUENTIAL_JOURNAL_STATE_INITIALIZED) { + if (sizeofBlob > journal->info.capacity) { + return JOURNAL_STATUS_BOUNDED_CAPACITY; /* adding this log chunk would cause us to exceed capacity (write past the tail). */ + } + } else if (journal->state == SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY) { + if (journal->log.mtdOffset + sizeofBlob > journal->log.mtdTailOffset) { + return JOURNAL_STATUS_BOUNDED_CAPACITY; /* adding this log chunk would cause us to exceed capacity (write past the tail). */ + } + } + + /* ensure that the request is at least as large as the minimum program unit */ + if (sizeofBlob < journal->info.program_unit) { + return JOURNAL_STATUS_SMALL_LOG_REQUEST; + } + + return JOURNAL_STATUS_OK; +} + +int32_t flashJournalStrategySequential_commit_sanityChecks(SequentialFlashJournal_t *journal) +{ + if (journal == NULL) { + return JOURNAL_STATUS_PARAMETER; + } + if (journal->state == SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY) { + if (journal->prevCommand != FLASH_JOURNAL_OPCODE_LOG_BLOB) { + return JOURNAL_STATUS_ERROR; + } + if ((journal->log.mtdOffset == ARM_STORAGE_INVALID_OFFSET) || + (journal->log.mtdTailOffset == ARM_STORAGE_INVALID_OFFSET) || + (journal->log.mtdTailOffset < journal->log.mtdOffset) || + (journal->log.tail.sizeofBlob == 0) || + (journal->log.tail.sizeofBlob > journal->info.capacity)) { + return JOURNAL_STATUS_ERROR; /* journal is in an un-expected state. */ + } + } + + return JOURNAL_STATUS_OK; +} diff --git a/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/support_funcs.c b/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/support_funcs.c index 3af02b357a0..a09d6db5205 100644 --- a/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/support_funcs.c +++ b/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/support_funcs.c @@ -15,12 +15,15 @@ * limitations under the License. */ +#include "flash-journal-strategy-sequential/flash_journal_crc.h" #include "support_funcs.h" #include #include #include -static inline int32_t mtdGetStartAddr(ARM_DRIVER_STORAGE *mtd, uint64_t *startAddrP) +struct FormatInfo_t formatInfoSingleton; + +int32_t mtdGetStartAddr(ARM_DRIVER_STORAGE *mtd, uint64_t *startAddrP) { ARM_STORAGE_BLOCK mtdBlock; if ((mtd->GetNextBlock(NULL, &mtdBlock)) != ARM_DRIVER_OK) { @@ -34,130 +37,295 @@ static inline int32_t mtdGetStartAddr(ARM_DRIVER_STORAGE *mtd, uint64_t *startAd return JOURNAL_STATUS_OK; } -int32_t discoverLatestLoggedBlob(SequentialFlashJournal_t *journal) +/** + * Check the sanity of a given slot + * @param journal + * @param slotOffset + * @param [out] headSequenceNumberP + * sequence number of the slot as read from the header. + * @param [out] tailP + * the tail of the slot + * @return 1 if the slot is valid; i.e. if head and tail match, and if CRC32 agrees. + */ +int32_t slotIsSane(SequentialFlashJournal_t *journal, + uint64_t slotOffset, + uint32_t *headSequenceNumberP, + SequentialFlashJournalLogTail_t *tailP) { int32_t rc; + ARM_DRIVER_STORAGE *mtd = journal->mtd; - /* reset top level journal metadata prior to scanning headers. */ - journal->nextSequenceNumber = SEQUENTIAL_FLASH_JOURNAL_INVALD_NEXT_SEQUENCE_NUMBER; /* we are currently unaware of previously written blobs */ - journal->currentBlobIndex = SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS; - journal->info.sizeofJournaledBlob = 0; + SequentialFlashJournalLogHead_t head; + /* TODO: add support for asynchronous read */ + if (((rc = mtd->ReadData(slotOffset, &head, sizeof(SequentialFlashJournalLogHead_t))) < ARM_DRIVER_OK) || + (rc != sizeof(SequentialFlashJournalLogHead_t))) { + if ((rc == ARM_DRIVER_OK) && (journal->mtdCapabilities.asynchronous_ops)) { + return JOURNAL_STATUS_UNSUPPORTED; + } - /* begin header-scan from the first block of the MTD */ - ARM_DRIVER_STORAGE *mtd = journal->mtd; - if ((rc = mtdGetStartAddr(journal->mtd, &journal->mtdStartOffset)) != JOURNAL_STATUS_OK) { - return rc; + return JOURNAL_STATUS_STORAGE_IO_ERROR; } - journal->initScan.currentOffset = journal->mtdStartOffset; - journal->state = SEQUENTIAL_JOURNAL_STATE_INIT_SCANNING_LOG_HEADERS; - // printf("start of init scan\n"); - for (unsigned blobIndex = 0; - blobIndex < SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS; - blobIndex++, journal->initScan.currentOffset += journal->sequentialSkip) { - // printf("blob index %u\n", blobIndex); - /* TODO: it is possible that the header structure spans multiple blocks, needing multiple reads. */ + /* compute the CRC32 of the header */ + flashJournalCrcReset(); + flashJournalCrcCummulative((const unsigned char *)&head, sizeof(SequentialFlashJournalLogHead_t)); - if (((rc = mtd->ReadData(journal->initScan.currentOffset, &journal->initScan.head, sizeof(SequentialFlashJournalLogHead_t))) < ARM_DRIVER_OK) || - (rc != sizeof(SequentialFlashJournalLogHead_t))) { - /* TODO: add support for asynchronous scan */ - if ((rc == ARM_DRIVER_OK) && (journal->mtdCapabilities.asynchronous_ops)) { - return JOURNAL_STATUS_UNSUPPORTED; - } + // printf("head->version: %lu\n", journal->initScan.head.version); + // printf("head->magic: %lx\n", journal->initScan.head.magic); + // printf("head->sequenceNumber: %lu\n", journal->initScan.head.sequenceNumber); + // printf("head->reserved: %lu\n", journal->initScan.head.reserved); + + if (SEQUENTIAL_JOURNAL_VALID_HEAD(&head)) { + *headSequenceNumberP = head.sequenceNumber; + // printf("found valid header with sequenceNumber %" PRIu32 "\n", *headSequenceNumberP); + uint64_t tailoffset = slotOffset + - ((slotOffset - SLOT_ADDRESS(journal, 0)) % journal->sizeofSlot) + + journal->sizeofSlot + - sizeof(SequentialFlashJournalLogTail_t); + // printf("hoping to read a tail at offset %lu\n", (uint32_t)tailoffset); + + /* TODO: add support for asynchronous read */ + if (((rc = mtd->ReadData(tailoffset, tailP, sizeof(SequentialFlashJournalLogTail_t))) < ARM_DRIVER_OK) || + (rc != sizeof(SequentialFlashJournalLogTail_t))) { return JOURNAL_STATUS_STORAGE_IO_ERROR; } - // printf("head->version: %lu\n", journal->initScan.head.version); - // printf("head->magic: %lx\n", journal->initScan.head.magic); - // printf("head->sequenceNumber: %lu\n", journal->initScan.head.sequenceNumber); - // printf("head->reserved: %lu\n", journal->initScan.head.reserved); - - if (SEQUENTIAL_JOURNAL_VALID_HEAD(&journal->initScan.head)) { - journal->initScan.headSequenceNumber = journal->initScan.head.sequenceNumber; - // printf("found valid header with sequenceNumber %lu\n", journal->initScan.headSequenceNumber); - - uint64_t tailoffset = journal->initScan.currentOffset - - ((journal->initScan.currentOffset - journal->mtdStartOffset) % journal->sequentialSkip) - + journal->sequentialSkip - - sizeof(SequentialFlashJournalLogTail_t); - - // printf("hoping to read a tail at offset %lu\n", (uint32_t)tailoffset); - if (((rc = mtd->ReadData(tailoffset, &journal->initScan.tail, sizeof(SequentialFlashJournalLogTail_t))) < ARM_DRIVER_OK) || - (rc != sizeof(SequentialFlashJournalLogTail_t))) { - return JOURNAL_STATUS_STORAGE_IO_ERROR; - } - if (SEQUENTIAL_JOURNAL_VALID_TAIL(&journal->initScan.tail) && - (journal->initScan.tail.sequenceNumber == journal->initScan.headSequenceNumber)) { - // printf("found valid blob with sequence number %lu\n", journal->initScan.headSequenceNumber); - uint32_t nextSequenceNumber = journal->initScan.headSequenceNumber + 1; - if (nextSequenceNumber == SEQUENTIAL_FLASH_JOURNAL_INVALD_NEXT_SEQUENCE_NUMBER) { - nextSequenceNumber = 0; + if (SEQUENTIAL_JOURNAL_VALID_TAIL(tailP) && (tailP->sequenceNumber == *headSequenceNumberP)) { + // printf("found valid tail\n"); + + /* iterate over the body of the slot computing CRC */ + #define CRC_CHUNK_SIZE 64 + uint8_t crcBuffer[CRC_CHUNK_SIZE]; + uint64_t bodyIndex = 0; + uint64_t bodyOffset = slotOffset + sizeof(SequentialFlashJournalLogHead_t); + while (bodyIndex < tailP->sizeofBlob) { + size_t sizeofReadOperation; + if ((tailP->sizeofBlob - bodyIndex) > CRC_CHUNK_SIZE) { + sizeofReadOperation = CRC_CHUNK_SIZE; + } else { + sizeofReadOperation = (tailP->sizeofBlob - bodyIndex); } - if ((journal->nextSequenceNumber == SEQUENTIAL_FLASH_JOURNAL_INVALD_NEXT_SEQUENCE_NUMBER) || - /* We take advantage of properties of unsigned arithmetic in the following - * expression. - * - * We want to calculate if (nextSequenceNumber > journal->nextSequenceNumber), - * instead we use the expression ((nextSequenceNumber - journal->nextSequenceNumber) > 0) - * to take wraparounds into account. - */ - ((int32_t)(nextSequenceNumber - journal->nextSequenceNumber) > 0)) { - journal->currentBlobIndex = blobIndex; - journal->nextSequenceNumber = nextSequenceNumber; - journal->info.sizeofJournaledBlob = journal->initScan.tail.sizeofBlob; - // printf("discoverLatestLoggedBlob: index %lu, sizeofBlob: %lu, nextSequenceNumber: %lu\n", - // journal->currentBlobIndex, (uint32_t)journal->info.sizeofJournaledBlob, journal->nextSequenceNumber); + /* TODO: add support for asynchronous read */ + rc = mtd->ReadData(bodyOffset + bodyIndex, crcBuffer, sizeofReadOperation); + if (rc != (int32_t)sizeofReadOperation) { + return JOURNAL_STATUS_STORAGE_IO_ERROR; } + + bodyIndex += sizeofReadOperation; + flashJournalCrcCummulative(crcBuffer, sizeofReadOperation); + } + + /* compute CRC32 over the tail */ + /* extract existing CRC32 from the tail. The CRC32 field in the tail needs to contain 0 before CRC32 can be computed over it. */ + uint32_t expectedCRC32 = tailP->crc32; + tailP->crc32 = 0; + + uint32_t crc32 = flashJournalCrcCummulative((const unsigned char *)tailP, sizeof(SequentialFlashJournalLogTail_t)); + flashJournalCrcReset(); + // printf("expectedCRC32: 0x%x, computedCRC32: 0x%x\n", expectedCRC32, crc32); + if (crc32 == expectedCRC32) { + return 1; + } + } + } + + return JOURNAL_STATUS_ERROR; +} + +int32_t setupSequentialJournalHeader(SequentialFlashJournalHeader_t *headerP, ARM_DRIVER_STORAGE *mtd, uint64_t totalSize, uint32_t numSlots) +{ + ARM_STORAGE_INFO mtdInfo; + if (mtd->GetInfo(&mtdInfo) < ARM_DRIVER_OK) { + return JOURNAL_STATUS_STORAGE_API_ERROR; + } + + headerP->genericHeader.magic = FLASH_JOURNAL_HEADER_MAGIC; + headerP->genericHeader.version = FLASH_JOURNAL_HEADER_VERSION; + headerP->genericHeader.sizeofHeader = sizeof(SequentialFlashJournalHeader_t); + + /* Determine 'journalOffset'. + * Constraint: journal header should start and terminate at an erase-boundary + * (so that slot-0 can be erased independently), and also a program-unit boundary. + */ + headerP->genericHeader.journalOffset = roundUp_uint32(headerP->genericHeader.sizeofHeader, LCM_OF_ALL_ERASE_UNITS); + if ((headerP->genericHeader.journalOffset % mtdInfo.program_unit) != 0) { + //printf("setupSequentialJournalHeader: journalOffset is not a multiple of MTD's program_unit\r\n"); + return JOURNAL_STATUS_PARAMETER; + } + + headerP->magic = SEQUENTIAL_FLASH_JOURNAL_HEADER_MAGIC; + headerP->version = SEQUENTIAL_FLASH_JOURNAL_HEADER_VERSION; + headerP->numSlots = numSlots; + + /* Determine 'sizeofSlot'. + * Constraint: slot-size should be a multiple of the erase-units of all involved storage blocks. + */ + uint64_t spaceAvailableForSlots = totalSize - headerP->genericHeader.journalOffset; + headerP->sizeofSlot = roundDown_uint32(spaceAvailableForSlots / numSlots, LCM_OF_ALL_ERASE_UNITS); + if (headerP->sizeofSlot == 0) { + //printf("setupSequentialJournalHeader: not enough space to create %" PRIu32 " slots\r\n", numSlots); + return JOURNAL_STATUS_PARAMETER; + } + + headerP->genericHeader.totalSize = headerP->genericHeader.journalOffset + (headerP->sizeofSlot * numSlots); + //printf("setupSequentialJournalHeader: header size = %" PRIu32 ", journalOffset = %" PRIu32 ", sizeofSlot = %" PRIu32 ", totalSize = %lu\n", headerP->genericHeader.sizeofHeader, headerP->genericHeader.journalOffset, headerP->sizeofSlot, (uint32_t)headerP->genericHeader.totalSize); + + /* compute checksum over the entire header */ + headerP->genericHeader.checksum = 0; + flashJournalCrcReset(); + headerP->genericHeader.checksum = flashJournalCrcCummulative((const unsigned char *)&headerP->genericHeader, sizeof(SequentialFlashJournalLogHead_t)); + + return JOURNAL_STATUS_OK; +} + +int32_t discoverLatestLoggedBlob(SequentialFlashJournal_t *journal) +{ + /* reset top level journal metadata prior to scanning headers. */ + journal->nextSequenceNumber = SEQUENTIAL_FLASH_JOURNAL_INVALD_NEXT_SEQUENCE_NUMBER; /* we are currently unaware of previously written blobs */ + journal->currentBlobIndex = journal->numSlots; + journal->info.sizeofJournaledBlob = 0; + + /* begin header-scan from the first block of the MTD */ + journal->initScan.currentOffset = SLOT_ADDRESS(journal, 0); + journal->state = SEQUENTIAL_JOURNAL_STATE_INIT_SCANNING_LOG_HEADERS; + + // printf("discoverLatestLoggedBlob: start of init scan\n"); + for (unsigned blobIndex = 0; + blobIndex < journal->numSlots; + blobIndex++, journal->initScan.currentOffset += journal->sizeofSlot) { + // printf("discoverLatestLoggedBlob: blob index %u\n", blobIndex); + /* TODO: it is possible that the header structure spans multiple blocks, needing multiple reads. */ + + if (slotIsSane(journal, + journal->initScan.currentOffset, + &journal->initScan.headSequenceNumber, + &journal->initScan.tail) == 1) { + // printf("found valid blob with sequence number %lu\n", journal->initScan.headSequenceNumber); + uint32_t nextSequenceNumber = journal->initScan.headSequenceNumber + 1; + if (nextSequenceNumber == SEQUENTIAL_FLASH_JOURNAL_INVALD_NEXT_SEQUENCE_NUMBER) { + nextSequenceNumber = 0; + } + + /* Have we found the best of the slots seen so far? */ + if ((journal->nextSequenceNumber == SEQUENTIAL_FLASH_JOURNAL_INVALD_NEXT_SEQUENCE_NUMBER) || + /* We take advantage of properties of unsigned arithmetic in the following + * expression. + * + * We want to calculate if (nextSequenceNumber > journal->nextSequenceNumber), + * instead we use the expression ((nextSequenceNumber - journal->nextSequenceNumber) > 0) + * to take wraparounds into account. + */ + ((int32_t)(nextSequenceNumber - journal->nextSequenceNumber) > 0)) { + journal->currentBlobIndex = blobIndex; + journal->nextSequenceNumber = nextSequenceNumber; + journal->info.sizeofJournaledBlob = journal->initScan.tail.sizeofBlob; + // printf("discoverLatestLoggedBlob: index %lu, sizeofBlob: %lu, nextSequenceNumber: %lu\n", + // journal->currentBlobIndex, (uint32_t)journal->info.sizeofJournaledBlob, journal->nextSequenceNumber); } } } - // printf("finished init scan\n"); + // printf("discoverLatestLoggedBlob: finished init scan\n"); /* Handle the case where our scan hasn't yielded any results. */ if (journal->nextSequenceNumber == SEQUENTIAL_FLASH_JOURNAL_INVALD_NEXT_SEQUENCE_NUMBER) { // printf("discoverLatestLoggedBlob: initializing to defaults\n"); journal->currentBlobIndex = (uint32_t)-1; /* to be incremented to 0 during the first attempt to log(). */ journal->nextSequenceNumber = 0; - - /* setup info.current_program_unit */ - ARM_STORAGE_BLOCK storageBlock; - if ((rc = journal->mtd->GetBlock(journal->mtdStartOffset, &storageBlock)) != ARM_DRIVER_OK) { - return JOURNAL_STATUS_STORAGE_API_ERROR; - } } journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; - return JOURNAL_STATUS_OK; } +/** + * Progress the state machine for the 'format' operation. This method can also be called from an interrupt handler. + * @return < JOURNAL_STATUS_OK for error + * = JOURNAL_STATUS_OK to signal pending asynchronous activity + * > JOURNAL_STATUS_OK for completion + */ +int32_t flashJournalStrategySequential_format_progress(int32_t status, ARM_STORAGE_OPERATION operationWhichJustFinshed) +{ + int32_t rc; + size_t sizeofWrite = roundUp_uint32(formatInfoSingleton.header.genericHeader.sizeofHeader, formatInfoSingleton.mtdProgramUnit); + size_t sizeofErase = roundUp_uint32(formatInfoSingleton.header.genericHeader.sizeofHeader, LCM_OF_ALL_ERASE_UNITS); + switch (operationWhichJustFinshed) { + case ARM_STORAGE_OPERATION_INITIALIZE: + if (status != ARM_DRIVER_OK) { + return JOURNAL_STATUS_STORAGE_API_ERROR; + } + + // printf("erasing %u bytes from offset %u\n", roundUp_uint32(header.genericHeader.sizeofHeader, mtdInfo.program_unit), mtdAddr); + rc = (formatInfoSingleton.mtd)->Erase(formatInfoSingleton.mtdAddr, sizeofErase); + if (rc < ARM_DRIVER_OK) { + if (rc == ARM_STORAGE_ERROR_RUNTIME_OR_INTEGRITY_FAILURE) { + return JOURNAL_STATUS_STORAGE_RUNTIME_OR_INTEGRITY_FAILURE; + } else { + return JOURNAL_STATUS_STORAGE_IO_ERROR; + } + } else if (rc == ARM_DRIVER_OK) { + return JOURNAL_STATUS_OK; /* An asynchronous operation is pending; it will result in a completion callback + * where the rest of processing will take place. */ + } + /* handle synchronous completion of programData */ + status = rc; + + /* intentional fall-through */ + + case ARM_STORAGE_OPERATION_ERASE: + if (status != (int32_t)sizeofErase) { + return JOURNAL_STATUS_STORAGE_IO_ERROR; + } + + // printf("calling ProgramData at address %u for %u bytes\n", + // formatInfoSingleton.mtdAddr, roundUp_uint32(formatInfoSingleton.header.genericHeader.sizeofHeader, formatInfoSingleton.mtdProgramUnit)); + rc = (formatInfoSingleton.mtd)->ProgramData(formatInfoSingleton.mtdAddr, &(formatInfoSingleton.header), sizeofWrite); + if (rc < ARM_DRIVER_OK) { + if (rc == ARM_STORAGE_ERROR_RUNTIME_OR_INTEGRITY_FAILURE) { + return JOURNAL_STATUS_STORAGE_RUNTIME_OR_INTEGRITY_FAILURE; + } else { + return JOURNAL_STATUS_STORAGE_IO_ERROR; + } + } else if (rc == ARM_DRIVER_OK) { + return JOURNAL_STATUS_OK; /* An asynchronous operation is pending; it will result in a completion callback + * where the rest of processing will take place. */ + } + /* handle synchronous completion of programData */ + status = rc; + + /* intentional fall-through */ + + case ARM_STORAGE_OPERATION_PROGRAM_DATA: + if (status != (int32_t)sizeofWrite) { + return JOURNAL_STATUS_STORAGE_IO_ERROR; + } + + return 1; /* acknowledge the completion of create */ + + default: + return JOURNAL_STATUS_STORAGE_API_ERROR; /* we don't expect to be here */ + } +} + int32_t flashJournalStrategySequential_reset_progress(void) { int32_t rc; SequentialFlashJournal_t *journal = activeJournal; - if (journal->mtdCapabilities.erase_all) { - if ((rc = journal->mtd->EraseAll()) < ARM_DRIVER_OK) { - journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */ - return JOURNAL_STATUS_STORAGE_IO_ERROR; - } - if ((journal->mtdCapabilities.asynchronous_ops) && (rc == ARM_DRIVER_OK)) { - return JOURNAL_STATUS_OK; /* we've got pending asynchronous activity. */ - } - /* else we fall through to handle synchronous completion */ - } else { - if ((rc = journal->mtd->Erase(journal->mtdStartOffset, SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS * journal->sequentialSkip)) < ARM_DRIVER_OK) { - journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */ + if ((rc = journal->mtd->Erase(SLOT_ADDRESS(journal, 0), journal->numSlots * journal->sizeofSlot)) < ARM_DRIVER_OK) { + journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */ + if (rc == ARM_STORAGE_ERROR_RUNTIME_OR_INTEGRITY_FAILURE) { + return JOURNAL_STATUS_STORAGE_RUNTIME_OR_INTEGRITY_FAILURE; + } else { return JOURNAL_STATUS_STORAGE_IO_ERROR; } - if ((journal->mtdCapabilities.asynchronous_ops) && (rc == ARM_DRIVER_OK)) { - printf("eturning JOURNAL_STATUS_OK\n"); - return JOURNAL_STATUS_OK; /* we've got pending asynchronous activity. */ - } - /* else we fall through to handle synchronous completion */ } + if ((journal->mtdCapabilities.asynchronous_ops) && (rc == ARM_DRIVER_OK)) { + //printf("eturning JOURNAL_STATUS_OK\n"); + return JOURNAL_STATUS_OK; /* we've got pending asynchronous activity. */ + } + /* else we fall through to handle synchronous completion */ journal->nextSequenceNumber = 0; journal->currentBlobIndex = (uint32_t)-1; @@ -179,11 +347,11 @@ int32_t flashJournalStrategySequential_read_progress(void) ARM_STORAGE_BLOCK storageBlock; if ((journal->read.amountLeftToRead) && - ((rc = journal->mtd->GetBlock(journal->read.offset, &storageBlock)) != ARM_DRIVER_OK)) { + ((rc = journal->mtd->GetBlock(journal->read.mtdOffset, &storageBlock)) != ARM_DRIVER_OK)) { journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */ return JOURNAL_STATUS_STORAGE_API_ERROR; } - uint64_t storageBlockAvailableCapacity = storageBlock.size - (journal->read.offset - storageBlock.addr); + uint64_t storageBlockAvailableCapacity = storageBlock.size - (journal->read.mtdOffset - storageBlock.addr); while (journal->read.amountLeftToRead) { while (!storageBlockAvailableCapacity) { @@ -191,7 +359,7 @@ int32_t flashJournalStrategySequential_read_progress(void) journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */ return JOURNAL_STATUS_ERROR; /* We ran out of storage blocks. Journal is in an un-expected state. */ } - journal->read.offset = storageBlock.addr; /* This should not be necessary since we assume + journal->read.mtdOffset = storageBlock.addr; /* This should not be necessary since we assume * storage map manages a contiguous address space. */ storageBlockAvailableCapacity = storageBlock.size; } @@ -201,8 +369,8 @@ int32_t flashJournalStrategySequential_read_progress(void) journal->read.amountLeftToRead : storageBlockAvailableCapacity; /* perform the IO */ - //printf("reading %lu bytes at offset %lu\n", xfer, (uint32_t)journal->read.offset); - rc = journal->mtd->ReadData(journal->read.offset, journal->read.dataBeingRead, xfer); + //printf("reading %lu bytes at offset %lu\n", xfer, (uint32_t)journal->read.mtdOffset); + rc = journal->mtd->ReadData(journal->read.mtdOffset, journal->read.dataBeingRead, xfer); if (rc < ARM_DRIVER_OK) { journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */ return JOURNAL_STATUS_STORAGE_IO_ERROR; @@ -211,10 +379,10 @@ int32_t flashJournalStrategySequential_read_progress(void) return JOURNAL_STATUS_OK; /* we've got pending asynchronous activity. */ } else { /* synchronous completion. 'rc' contains the actual number of bytes transferred. */ - journal->read.offset += rc; + journal->read.mtdOffset += rc; journal->read.amountLeftToRead -= rc; journal->read.dataBeingRead += rc; - journal->read.totalDataRead += rc; + journal->read.logicalOffset += rc; } } @@ -222,6 +390,12 @@ int32_t flashJournalStrategySequential_read_progress(void) return (journal->read.dataBeingRead - journal->read.blob); } +/** + * Progress the state machine for the 'log' operation. This method can also be called from an interrupt handler. + * @return < JOURNAL_STATUS_OK for error + * = JOURNAL_STATUS_OK to signal pending asynchronous activity + * > JOURNAL_STATUS_OK for completion + */ int32_t flashJournalStrategySequential_log_progress(void) { SequentialFlashJournal_t *journal = activeJournal; @@ -234,7 +408,7 @@ int32_t flashJournalStrategySequential_log_progress(void) } uint32_t blobIndexBeingLogged = journal->currentBlobIndex + 1; - if (blobIndexBeingLogged == SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS) { + if (blobIndexBeingLogged == journal->numSlots) { blobIndexBeingLogged = 0; } @@ -242,22 +416,24 @@ int32_t flashJournalStrategySequential_log_progress(void) int32_t rc; if (journal->state == SEQUENTIAL_JOURNAL_STATE_LOGGING_ERASE) { - uint64_t amountLeftToErase = journal->mtdStartOffset - + (blobIndexBeingLogged + 1) * journal->sequentialSkip - - journal->log.eraseOffset; + uint64_t amountLeftToErase = SLOT_ADDRESS(journal, blobIndexBeingLogged + 1) - journal->log.mtdEraseOffset; // printf("journal state: erasing; offset %lu [size %lu]\n", // (uint32_t)journal->log.eraseOffset, (uint32_t)amountLeftToErase); while (amountLeftToErase) { - if ((rc = journal->mtd->Erase(journal->log.eraseOffset, amountLeftToErase)) < ARM_DRIVER_OK) { + if ((rc = journal->mtd->Erase(journal->log.mtdEraseOffset, amountLeftToErase)) < ARM_DRIVER_OK) { journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */ - return JOURNAL_STATUS_ERROR; /* We ran out of storage blocks. Journal is in an un-expected state. */ + if (rc == ARM_STORAGE_ERROR_RUNTIME_OR_INTEGRITY_FAILURE) { + return JOURNAL_STATUS_STORAGE_RUNTIME_OR_INTEGRITY_FAILURE; + } else { + return JOURNAL_STATUS_ERROR; + } } if ((journal->mtdCapabilities.asynchronous_ops) && (rc == ARM_DRIVER_OK)) { return JOURNAL_STATUS_OK; /* we've got pending asynchronous activity. */ } else { /* synchronous completion. */ - journal->log.eraseOffset += rc; - amountLeftToErase -= rc; + journal->log.mtdEraseOffset += rc; + amountLeftToErase -= rc; } } } else { @@ -273,11 +449,11 @@ int32_t flashJournalStrategySequential_log_progress(void) } /* check for alignment of next log offset with program_unit */ - if ((rc = journal->mtd->GetBlock(journal->log.offset, &storageBlock)) != ARM_DRIVER_OK) { + if ((rc = journal->mtd->GetBlock(journal->log.mtdOffset, &storageBlock)) != ARM_DRIVER_OK) { journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */ return JOURNAL_STATUS_STORAGE_API_ERROR; } - if ((journal->log.offset - storageBlock.addr) % journal->info.program_unit) { + if ((journal->log.mtdOffset - storageBlock.addr) % journal->info.program_unit) { journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */ return JOURNAL_STATUS_ERROR; /* Program offset doesn't align with info.program_unit. This would result in an IO error if attempted. */ } @@ -286,17 +462,21 @@ int32_t flashJournalStrategySequential_log_progress(void) xfer -= xfer % journal->info.program_unit; /* align transfer-size with program_unit. */ /* perform the IO */ - // printf("programming %lu bytes at offset %lu\n", xfer, (uint32_t)journal->log.offset); - rc = journal->mtd->ProgramData(journal->log.offset, journal->log.dataBeingLogged, xfer); + // printf("programming %lu bytes at offset %lu\n", xfer, (uint32_t)journal->log.mtdOffset); + rc = journal->mtd->ProgramData(journal->log.mtdOffset, journal->log.dataBeingLogged, xfer); if (rc < ARM_DRIVER_OK) { journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */ - return JOURNAL_STATUS_STORAGE_IO_ERROR; + if (rc == ARM_STORAGE_ERROR_RUNTIME_OR_INTEGRITY_FAILURE) { + return JOURNAL_STATUS_STORAGE_RUNTIME_OR_INTEGRITY_FAILURE; + } else { + return JOURNAL_STATUS_STORAGE_IO_ERROR; + } } if ((journal->mtdCapabilities.asynchronous_ops) && (rc == ARM_DRIVER_OK)) { return JOURNAL_STATUS_OK; /* we've got pending asynchronous activity. */ } else { /* synchronous completion. 'rc' contains the actual number of bytes transferred. */ - journal->log.offset += rc; + journal->log.mtdOffset += rc; journal->log.amountLeftToLog -= rc; journal->log.dataBeingLogged += rc; if (journal->state == SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY) { @@ -312,7 +492,7 @@ int32_t flashJournalStrategySequential_log_progress(void) switch (journal->state) { case SEQUENTIAL_JOURNAL_STATE_LOGGING_ERASE: journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_HEAD; - journal->log.offset = journal->mtdStartOffset + blobIndexBeingLogged * journal->sequentialSkip; + journal->log.mtdOffset = SLOT_ADDRESS(journal, blobIndexBeingLogged); journal->log.head.version = SEQUENTIAL_FLASH_JOURNAL_VERSION; journal->log.head.magic = SEQUENTIAL_FLASH_JOURNAL_MAGIC; journal->log.head.sequenceNumber = journal->nextSequenceNumber; @@ -322,23 +502,32 @@ int32_t flashJournalStrategySequential_log_progress(void) // printf("newstate: program HEAD; amount to log %u\n", journal->log.amountLeftToLog); break; - case SEQUENTIAL_JOURNAL_STATE_LOGGING_HEAD: /* switch to writing the body */ + case SEQUENTIAL_JOURNAL_STATE_LOGGING_HEAD: /* we've finished writing the head */ + /* compute CRC32 on the header */ + flashJournalCrcReset(); + flashJournalCrcCummulative((const unsigned char *)&journal->log.head, sizeof(SequentialFlashJournalLogHead_t)); + + /* switch to writing the body */ + /* Prepare for the tail to be written out at a later time. * This will only be done once Commit() is called. */ - journal->log.tailOffset = journal->mtdStartOffset - + (blobIndexBeingLogged + 1) * journal->sequentialSkip - - sizeof(SequentialFlashJournalLogTail_t); + journal->log.mtdTailOffset = SLOT_ADDRESS(journal, blobIndexBeingLogged + 1) - sizeof(SequentialFlashJournalLogTail_t); + journal->log.tail.magic = SEQUENTIAL_FLASH_JOURNAL_MAGIC; journal->log.tail.sequenceNumber = journal->nextSequenceNumber; journal->log.tail.sizeofBlob = 0; /* we'll update this as we complete our writes. */ + journal->log.tail.crc32 = 0; if (journal->prevCommand == FLASH_JOURNAL_OPCODE_COMMIT) { /* This branch is taken only when commit() is called without any preceding log() operations. */ + journal->log.tail.crc32 = flashJournalCrcCummulative((const unsigned char *)&journal->log.tail, sizeof(SequentialFlashJournalLogTail_t)); + flashJournalCrcReset(); + journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_TAIL; journal->log.dataBeingLogged = (const uint8_t *)&journal->log.tail; journal->log.amountLeftToLog = sizeof(SequentialFlashJournalLogTail_t); - journal->log.offset = journal->log.tailOffset; - // printf("newstate: program TAIL at offset %lu\r\n", (uint32_t)journal->log.offset); + journal->log.mtdOffset = journal->log.mtdTailOffset; + // printf("newstate: program TAIL at offset %lu\r\n", (uint32_t)journal->log.mtdOffset); } else { journal->state = SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY; journal->log.dataBeingLogged = journal->log.blob; @@ -352,15 +541,19 @@ int32_t flashJournalStrategySequential_log_progress(void) if (journal->log.dataBeingLogged == journal->log.blob) { return JOURNAL_STATUS_SMALL_LOG_REQUEST; } else { - return (journal->log.dataBeingLogged - journal->log.blob); + uint32_t amountOfDataLogged = (journal->log.dataBeingLogged - journal->log.blob); + flashJournalCrcCummulative(journal->log.blob, amountOfDataLogged); /* compute CRC32 on logged data */ + return amountOfDataLogged; } case SEQUENTIAL_JOURNAL_STATE_LOGGING_TAIL: + // printf("crc32 of slot: 0x%x\n", journal->log.tail.crc32); + journal->info.sizeofJournaledBlob = journal->log.tail.sizeofBlob; journal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state to allow further operations */ ++journal->currentBlobIndex; - if (journal->currentBlobIndex == SEQUENTIAL_FLASH_JOURNAL_MAX_LOGGED_BLOBS) { + if (journal->currentBlobIndex == journal->numSlots) { journal->currentBlobIndex = 0; } // printf("currentBlobIndex: %lu\n", journal->currentBlobIndex); @@ -381,24 +574,48 @@ int32_t flashJournalStrategySequential_log_progress(void) } } +void formatHandler(int32_t status, ARM_STORAGE_OPERATION operation) +{ + if (status < ARM_DRIVER_OK) { + if (formatInfoSingleton.callback) { + formatInfoSingleton.callback(status, FLASH_JOURNAL_OPCODE_FORMAT); + } + return; + } + + int32_t rc = flashJournalStrategySequential_format_progress(status, operation); + if (rc != JOURNAL_STATUS_OK) { + if (formatInfoSingleton.callback) { + formatInfoSingleton.callback(rc, FLASH_JOURNAL_OPCODE_FORMAT); + } + } +} + void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation) { int32_t rc; if (status < ARM_DRIVER_OK) { - printf("mtdHandler: received error status %" PRId32 "\n", status); + /* Map integrity failures reported by the Storage driver appropriately. */ + if (status == ARM_STORAGE_ERROR_RUNTIME_OR_INTEGRITY_FAILURE) { + status = JOURNAL_STATUS_STORAGE_RUNTIME_OR_INTEGRITY_FAILURE; + } else { + status = JOURNAL_STATUS_STORAGE_IO_ERROR; + } + + // printf("journal mtdHandler: received error status %ld\n", status); switch (activeJournal->state) { case SEQUENTIAL_JOURNAL_STATE_NOT_INITIALIZED: case SEQUENTIAL_JOURNAL_STATE_INIT_SCANNING_LOG_HEADERS: if (activeJournal->callback) { - activeJournal->callback(JOURNAL_STATUS_STORAGE_IO_ERROR, FLASH_JOURNAL_OPCODE_INITIALIZE); + activeJournal->callback(status, FLASH_JOURNAL_OPCODE_INITIALIZE); } break; case SEQUENTIAL_JOURNAL_STATE_RESETING: activeJournal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */ if (activeJournal->callback) { - activeJournal->callback(JOURNAL_STATUS_STORAGE_IO_ERROR, FLASH_JOURNAL_OPCODE_RESET); + activeJournal->callback(status, FLASH_JOURNAL_OPCODE_RESET); } break; @@ -411,7 +628,7 @@ void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation) activeJournal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; if (activeJournal->callback) { - activeJournal->callback(JOURNAL_STATUS_STORAGE_IO_ERROR, FLASH_JOURNAL_OPCODE_LOG_BLOB); + activeJournal->callback(status, FLASH_JOURNAL_OPCODE_LOG_BLOB); } break; case SEQUENTIAL_JOURNAL_STATE_READING: @@ -419,7 +636,7 @@ void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation) activeJournal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; if (activeJournal->callback) { - activeJournal->callback(JOURNAL_STATUS_STORAGE_IO_ERROR, FLASH_JOURNAL_OPCODE_READ_BLOB); + activeJournal->callback(status, FLASH_JOURNAL_OPCODE_READ_BLOB); } break; } @@ -455,7 +672,7 @@ void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation) return; } - activeJournal->log.eraseOffset += status; + activeJournal->log.mtdEraseOffset += status; if ((rc = flashJournalStrategySequential_log_progress()) != JOURNAL_STATUS_OK) { activeJournal->state = SEQUENTIAL_JOURNAL_STATE_INITIALIZED; /* reset state */ @@ -476,9 +693,9 @@ void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation) break; case ARM_STORAGE_OPERATION_PROGRAM_DATA: - // printf("PROGRAM_DATA: received status of %ld\n", status); + // printf("journal mtdHandler: PROGRAM_DATA: received status of %ld\n", status); rc = status; - activeJournal->log.offset += rc; + activeJournal->log.mtdOffset += rc; activeJournal->log.amountLeftToLog -= rc; activeJournal->log.dataBeingLogged += rc; if (activeJournal->state == SEQUENTIAL_JOURNAL_STATE_LOGGING_BODY) { @@ -504,7 +721,7 @@ void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation) break; default: - printf("mtdHandler: unknown operation %u\n", operation); + //printf("mtdHandler: unknown operation %u\n", operation); break; } } diff --git a/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/support_funcs.h b/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/support_funcs.h index 5a3145e701e..fcefb4f9211 100644 --- a/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/support_funcs.h +++ b/features/storage/FEATURE_STORAGE/flash-journal/flash-journal-strategy-sequential/support_funcs.h @@ -21,12 +21,58 @@ #include "flash-journal-strategy-sequential/flash_journal_private.h" #include "flash-journal-strategy-sequential/flash_journal_strategy_sequential.h" +/* The following singleton captures the state of the format machine. Format is + * handled differently because it executes even before a journal exists (or a + * Journal_t can be initialized. */ +extern struct FormatInfo_t { + ARM_DRIVER_STORAGE *mtd; + SequentialFlashJournalHeader_t header; + FlashJournal_Callback_t callback; + uint64_t mtdAddr; + uint32_t mtdProgramUnit; +} formatInfoSingleton; + extern SequentialFlashJournal_t *activeJournal; +/** + * Check the sanity of a given slot + * @param journal + * @param slotOffset + * @param [out] headSequenceNumberP + * sequence number of the slot as read from the header. + * @param [out] tailP + * the tail of the slot + * @return 1 if the slot is valid; i.e. if head and tail match, and if CRC32 agrees. + */ +int32_t slotIsSane(SequentialFlashJournal_t *journal, + uint64_t slotOffset, + uint32_t *headSequenceNumberP, + SequentialFlashJournalLogTail_t *tailP); + +int32_t mtdGetStartAddr(ARM_DRIVER_STORAGE *mtd, uint64_t *startAddrP); +int32_t setupSequentialJournalHeader(SequentialFlashJournalHeader_t *headerP, ARM_DRIVER_STORAGE *mtd, uint64_t totalSize, uint32_t numSlots); int32_t discoverLatestLoggedBlob(SequentialFlashJournal_t *journal); + +/** + * Progress the state machine for the 'format' operation. This method can also be called from an interrupt handler. + * @return < JOURNAL_STATUS_OK for error + * = JOURNAL_STATUS_OK to signal pending asynchronous activity + * > JOURNAL_STATUS_OK for completion + */ +int32_t flashJournalStrategySequential_format_progress(int32_t status, ARM_STORAGE_OPERATION operationWhichJustFinshed); + +/** + * Progress the state machine for the 'log' operation. This method can also be called from an interrupt handler. + * @return < JOURNAL_STATUS_OK for error + * = JOURNAL_STATUS_OK to signal pending asynchronous activity + * > JOURNAL_STATUS_OK for completion + */ +int32_t flashJournalStrategySequential_log_progress(void); + int32_t flashJournalStrategySequential_reset_progress(void); int32_t flashJournalStrategySequential_read_progress(void); -int32_t flashJournalStrategySequential_log_progress(void); + void mtdHandler(int32_t status, ARM_STORAGE_OPERATION operation); +void formatHandler(int32_t status, ARM_STORAGE_OPERATION operation); #endif /*__FLASH_JOURNAL_SEQUENTIAL_STRATEGY_SUPPORT_FUNCTIONS_H__*/ diff --git a/features/storage/FEATURE_STORAGE/flash-journal/flash_journal.h b/features/storage/FEATURE_STORAGE/flash-journal/flash_journal.h index 874ef0d7b52..e1825195e0a 100644 --- a/features/storage/FEATURE_STORAGE/flash-journal/flash_journal.h +++ b/features/storage/FEATURE_STORAGE/flash-journal/flash_journal.h @@ -23,7 +23,6 @@ extern "C" { #endif // __cplusplus #include "storage_abstraction/Driver_Storage.h" -#include /** * General return codes. All Flash-Journal APIs return an int32_t to allow for @@ -44,6 +43,9 @@ typedef enum _FlashJournal_Status JOURNAL_STATUS_NOT_INITIALIZED = -9, ///< journal not initialized JOURNAL_STATUS_EMPTY = -10, ///< There is no further data to read JOURNAL_STATUS_SMALL_LOG_REQUEST = -11, ///< log request is smaller than the program_unit of the underlying MTD block. + JOURNAL_STATUS_NOT_FORMATTED = -12, ///< need to call xxx_format() before using the journal. + JOURNAL_STATUS_METADATA_ERROR = -13, ///< sanity checks for the journal metadata failed. + JOURNAL_STATUS_STORAGE_RUNTIME_OR_INTEGRITY_FAILURE = -14, ///< validation or run-time errors arising from the badkend media. } FlashJournal_Status_t; /** @@ -51,6 +53,7 @@ typedef enum _FlashJournal_Status * completing commands. Refer to \ref ARM_Flash_Callback_t. */ typedef enum _FlashJournal_OpCode { + FLASH_JOURNAL_OPCODE_FORMAT, FLASH_JOURNAL_OPCODE_INITIALIZE, FLASH_JOURNAL_OPCODE_GET_INFO, FLASH_JOURNAL_OPCODE_READ_BLOB, @@ -76,6 +79,29 @@ typedef struct _FlashJournal_Info { ///< the requested amount). } FlashJournal_Info_t; + +static const uint32_t FLASH_JOURNAL_HEADER_MAGIC = 0xA00AEE1DUL; +static const uint32_t FLASH_JOURNAL_HEADER_VERSION = 1; + +/** + * Meta-data placed at the head of a Journal. The actual header would be an + * extension of this generic header, and would depend on the implementation + * strategy. Initialization algorithms can expect to find this generic header at + * the start of every Journal. + */ +typedef struct _FlashJournalHeader { + uint32_t magic; /** Journal-header specific magic code */ + uint32_t version; /** Revision number for this generic journal header. */ + uint64_t totalSize; /** Total space (in bytes) occupied by the journal, including the header. + * Both 'mtdOffset' and 'mtdOffset + totalSize' should + * lie on erase boundaries. */ + uint32_t sizeofHeader; /** The size of the journal header; this is expected to be larger than this generic header. */ + uint32_t journalOffset; /** Offset from the start of the journal header to the actual logged journal. */ + uint32_t checksum; /** CRC32 over the entire flash-journal-header, including the implementation + * specific extension (i.e. over 'sizeofHeader' bytes). The value of the + * field is taken to be 0 for the purpose of computing the checksum. */ +} FlashJournalHeader_t; + /** * This is the type of the command completion callback handler for the * asynchronous flash-journal APIs: initialize(), read(), log(), commit() and @@ -95,8 +121,7 @@ typedef struct _FlashJournal_Info { typedef void (*FlashJournal_Callback_t)(int32_t status, FlashJournal_OpCode_t cmd_code); /* forward declarations. */ -typedef struct _FlashJournal_t FlashJournal_t; -typedef struct _FlashJournal_Ops_t FlashJournal_Ops_t; +struct FlashJournal_t; /** * @ref FlashJournal_t is an abstraction implemented by a table of generic @@ -111,44 +136,53 @@ typedef struct _FlashJournal_Ops_t FlashJournal_Ops_t; * strategy-specific metadata. The value of this MAX_SIZE may need to be * increased if some future journal-strategy needs more metadata. */ -#define FLASH_JOURNAL_HANDLE_MAX_SIZE 140 +#define FLASH_JOURNAL_HANDLE_MAX_SIZE 160 /** * This is the set of operations offered by the flash-journal abstraction. A set * of implementations for these operations defines a logging strategy. */ -typedef struct _FlashJournal_Ops_t { + +typedef struct FlashJournal_Ops_t { /** * \brief Initialize the flash journal. Refer to @ref FlashJournal_initialize. */ - int32_t (*initialize)(FlashJournal_t *journal, ARM_DRIVER_STORAGE *mtd, const FlashJournal_Ops_t *ops, FlashJournal_Callback_t callback); + int32_t (*initialize)(struct FlashJournal_t *journal, + ARM_DRIVER_STORAGE *mtd, + const struct FlashJournal_Ops_t *ops, + FlashJournal_Callback_t callback); /** * \brief fetch journal metadata. Refer to @ref FlashJournal_getInfo. */ - FlashJournal_Status_t (*getInfo) (FlashJournal_t *journal, FlashJournal_Info_t *info); + FlashJournal_Status_t (*getInfo) (struct FlashJournal_t *journal, FlashJournal_Info_t *info); /** * @brief Read from the most recently logged blob. Refer to @ref FlashJournal_read. */ - int32_t (*read) (FlashJournal_t *journal, void *buffer, size_t size); + int32_t (*read) (struct FlashJournal_t *journal, void *buffer, size_t size); + + /** + * @brief Read from the most recently logged blob from a particular offset. Refer to @ref FlashJournal_readFrom. + */ + int32_t (*readFrom) (struct FlashJournal_t *journal, size_t offset, void *buffer, size_t size); /** * @brief Start logging a new blob or append to the one currently being logged. Refer to @ref FlashJournal_log. */ - int32_t (*log) (FlashJournal_t *journal, const void *blob, size_t size); + int32_t (*log) (struct FlashJournal_t *journal, const void *blob, size_t size); /** * @brief commit a blob accumulated through a non-empty sequence of * previously successful log() operations. Refer to @ref FlashJournal_commit. */ - int32_t (*commit) (FlashJournal_t *journal); + int32_t (*commit) (struct FlashJournal_t *journal); /** * @brief Reset the journal. This has the effect of erasing all valid blobs. * Refer to @ref FlashJournal_reset. */ - int32_t (*reset) (FlashJournal_t *journal); + int32_t (*reset) (struct FlashJournal_t *journal); } FlashJournal_Ops_t; /** @@ -167,7 +201,7 @@ typedef struct _FlashJournal_Ops_t { * @note: there is a risk of overallocation in case an implementation doesn't * need FLASH_JOURNAL_HANDLE_MAX_SIZE bytes, but the impact should be small. */ -typedef struct _FlashJournal_t { +typedef struct FlashJournal_t { FlashJournal_Ops_t ops; union { @@ -224,7 +258,7 @@ typedef struct _FlashJournal_t { * JOURNAL_STATUS_OK before the actual completion of the operation (or * with an appropriate error code in case of failure). When the * operation is completed the command callback is invoked with - * JOURNAL_STATUS_OK passed in as the 'status' parameter of the + * 1 passed in as the 'status' parameter of the * callback. In case of errors, the completion callback is invoked with * an error status. * - When the operation is executed by the journal in a blocking (i.e. @@ -234,6 +268,11 @@ typedef struct _FlashJournal_t { * completion or an appropriate error code, and no further * invocation of the completion callback should be expected at a later time. * + * @note The user must call an appropriate xxx_format() to format underlying + * storage before initializing it for use. If Initialize() is called on + * unformatted storage, an error value of JOURNAL_STATUS_NOT_FORMATTED will be + * returned. + * * Here's a code snippet to suggest how this API might be used by callers: * \code * ASSERT(JOURNAL_STATUS_OK == 0); // this is a precondition; it doesn't need to be put in code @@ -377,6 +416,88 @@ static inline int32_t FlashJournal_read(FlashJournal_t *journal, void *blob, siz return journal->ops.read(journal, blob, n); } +/** + * @brief Read from the most recently logged blob at a given offset. A front-end + * for @ref FlashJournal_Ops_t::readFrom(). + * + * @details Read off a chunk of the logged blob from a given offset. The journal + * maintains a read-pointer internally to allow reads to continue where the + * previous one left off. This call effectively sets the read-counter before + * fetching data. Subsequent reads continue sequentially from where the + * readFrom() left off. + * + * @note: If the given offset stands at (or is beyond) the end of the previously + * logged blob, readFrom() returns the error JOURNAL_STATUS_EMPTY (or passes + * that value as the status of a completion callback) and resets the read- + * pointer to allow re-reading the blob from the start. + * + * @param [in] journal + * A previously initialized journal. + * + * @param [in] offset + * The logical offset (within the blob) at which to read data from. + * + * @param [out] buffer + * The destination of the read operation. The memory is owned + * by the caller and should remain valid for the lifetime + * of this operation. + * + * @param [in] size + * The maximum amount of data which can be read in this + * operation. The memory pointed to by 'buffer' should be as + * large as this amount. + * + * @return + * The function executes in the following ways: + * - When the operation is asynchronous--i.e. when the underlying MTD's + * ARM_STOR_CAPABILITIES::asynchronous_ops is set to 1--and the operation + * executed by the journal in a non-blocking (i.e. asynchronous) manner, + * control returns to the caller with JOURNAL_STATUS_OK before the actual + * completion of the operation (or with an appropriate error code in case of + * failure). When the operation completes, the command callback is + * invoked with the number of successfully transferred bytes passed in as + * the 'status' parameter of the callback. If any error is encountered + * after the launch of an asynchronous operation, the completion callback + * is invoked with an error status. + * - When the operation is executed by the journal in a blocking (i.e. + * synchronous) manner, control returns to the caller only upon the + * actual completion of the operation, or the discovery of a failure + * condition. In synchronous mode, the function returns the number + * of data items read or an appropriate error code. + * + * @note If the underlying MTD's ARM_STORAGE_CAPABILITIES::asynchronous_ops + * is set then this operation may execute asynchronously. In the case of + * asynchronous operation, the invocation returns early (with + * JOURNAL_STATUS_OK) and results in a completion callback later. + * + * @note If the underlying MTD's ARM_STORAGE_CAPABILITIES::asynchronous_ops + * is set, the journal is not required to operate asynchronously. A Read + * operation can be finished synchronously in spite of + * ARM_STORAGE_CAPABILITIES::asynchronous_ops being set, returning the + * number of data items read to indicate successful completion, or an + * appropriate error code. In this case no further invocation of a + * completion callback should be expected at a later time. + * + * Here's a code snippet to suggest how this API might be used by callers: + * \code + * ASSERT(JOURNAL_STATUS_OK == 0); // this is a precondition; it doesn't need to be put in code + * int32_t returnValue = FlashJournal_readFrom(&journal, offset, buffer, size); + * if (returnValue < JOURNAL_STATUS_OK) { + * // handle error + * } else if (returnValue == JOURNAL_STATUS_OK) { + * ASSERT(MTD->GetCapabilities().asynchronous_ops == 1); + * // handle early return from asynchronous execution + * } else { + * ASSERT(returnValue == size); + * // handle synchronous completion + * } + * \endcode + */ +static inline int32_t FlashJournal_readFrom(struct FlashJournal_t *journal, size_t offset, void *blob, size_t n) +{ + return journal->ops.readFrom(journal, offset, blob, n); +} + /** * @brief Start logging a new blob or append to the one currently being logged. * A front-end for @ref FlashJournal_Ops_t::log(). diff --git a/features/storage/FEATURE_STORAGE/storage-volume-manager/source/storage_volume.cpp b/features/storage/FEATURE_STORAGE/storage-volume-manager/source/storage_volume.cpp new file mode 100644 index 00000000000..8ea34069ee5 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/storage-volume-manager/source/storage_volume.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2006-2016, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "storage-volume-manager/storage_volume_manager.h" +#include +#include + +/* redefine tr_debug() to a printf() equivalent to emit trace */ +#define tr_debug(...) + + +void StorageVolume::setup(uint64_t _addr, uint64_t _size, StorageVolumeManager *_volumeManager) +{ + volumeOffset = _addr; + volumeSize = _size; + volumeManager = _volumeManager; + allocated = true; +} + +ARM_DRIVER_VERSION StorageVolume::GetVersion(void) +{ + ARM_DRIVER_VERSION bad_ver = {0, 0}; + + if (!allocated) { + return bad_ver; + } + return volumeManager->getStorage()->GetVersion(); +} + +ARM_STORAGE_CAPABILITIES StorageVolume::GetCapabilities(void) +{ + ARM_STORAGE_CAPABILITIES bad_cap; + + if (!allocated) { + memset(&bad_cap, 0, sizeof(ARM_STORAGE_CAPABILITIES)); + return bad_cap; + } + return volumeManager->getStorage()->GetCapabilities(); +} + +int32_t StorageVolume::Initialize(ARM_Storage_Callback_t _callback) +{ + if (!allocated) { + return STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED; + } + + callback = _callback; /* nothing else to do since we've already initialized the storage */ + return 1; /* synchronous completion. */ +} + +int32_t StorageVolume::Uninitialize(void) +{ + if (!allocated) { + return STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED; + } + + return 1; /* synchronous completion. */ +} + +int32_t StorageVolume::PowerControl(ARM_POWER_STATE state) +{ + tr_debug("called powerControl(%u)", state); + if (!allocated) { + return STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED; + } + if (volumeManager->activeVolume != NULL) { + return ARM_DRIVER_ERROR_BUSY; + } + + volumeManager->activeVolume = this; + int32_t rc = volumeManager->getStorage()->PowerControl(state); + if (rc != ARM_DRIVER_OK) { + volumeManager->activeVolume = NULL; /* we're certain that there is no more pending asynch. activity */ + } + return rc; +} + +int32_t StorageVolume::ReadData(uint64_t addr, void *data, uint32_t size) +{ + tr_debug("called ReadData(%" PRIu32 ", %" PRIu32 ")", (uint32_t)addr, size); + if (!allocated) { + return STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED; + } + if (volumeManager->activeVolume != NULL) { + return ARM_DRIVER_ERROR_BUSY; + } + if ((size > volumeSize) || ((addr + size) > volumeSize)) { + return ARM_DRIVER_ERROR_PARAMETER; + } + + volumeManager->activeVolume = this; + int32_t rc = volumeManager->getStorage()->ReadData(volumeOffset + addr, data, size); + if (rc != ARM_DRIVER_OK) { + volumeManager->activeVolume = NULL; /* we're certain that there is no more pending asynch. activity */ + } + return rc; +} + +int32_t StorageVolume::ProgramData(uint64_t addr, const void *data, uint32_t size) +{ + tr_debug("called ProgramData(%" PRIu32 ", %" PRIu32 ")", (uint32_t)addr, size); + if (!allocated) { + return STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED; + } + if (volumeManager->activeVolume != NULL) { + return ARM_DRIVER_ERROR_BUSY; + } + if ((size > volumeSize) || ((addr + size) > volumeSize)) { + return ARM_DRIVER_ERROR_PARAMETER; + } + + volumeManager->activeVolume = this; + int32_t rc = volumeManager->getStorage()->ProgramData(volumeOffset + addr, data, size); + if (rc != ARM_DRIVER_OK) { + volumeManager->activeVolume = NULL; /* we're certain that there is no more pending asynch. activity */ + } + return rc; +} + +int32_t StorageVolume::Erase(uint64_t addr, uint32_t size) +{ + tr_debug("called erase(%" PRIu32 ", %" PRIu32 ")", (uint32_t)addr, size); + if (!allocated) { + return STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED; + } + if (volumeManager->activeVolume != NULL) { + return ARM_DRIVER_ERROR_BUSY; + } + if ((size > volumeSize) || ((addr + size) > volumeSize)) { + return ARM_DRIVER_ERROR_PARAMETER; + } + + volumeManager->activeVolume = this; + int32_t rc = volumeManager->getStorage()->Erase(volumeOffset + addr, size); + if (rc != ARM_DRIVER_OK) { + volumeManager->activeVolume = NULL; /* we're certain that there is no more pending asynch. activity */ + } + return rc; +} + +int32_t StorageVolume::EraseAll(void) +{ + tr_debug("called eraseAll"); + if (!allocated) { + return STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED; + } + if (volumeManager->activeVolume != NULL) { + return ARM_DRIVER_ERROR_BUSY; + } + + int32_t rc; + + /* Allow EraseAll() only if the volume spans the entire storage. */ + { + ARM_STORAGE_INFO info; + rc = volumeManager->getStorage()->GetInfo(&info); + if (rc != ARM_DRIVER_OK) { + return ARM_DRIVER_ERROR; + } + + if ((volumeOffset != 0) || (volumeSize != info.total_storage)) { + return ARM_DRIVER_ERROR_UNSUPPORTED; + } + } + + volumeManager->activeVolume = this; + rc = volumeManager->getStorage()->EraseAll(); + if (rc != ARM_DRIVER_OK) { + volumeManager->activeVolume = NULL; /* we're certain that there is no more pending asynch. activity */ + } + return rc; +} + +ARM_STORAGE_STATUS StorageVolume::GetStatus(void) +{ + const uint32_t busy = ((volumeManager->activeVolume != NULL) ? (uint32_t)1 : (uint32_t)0); + ARM_STORAGE_STATUS status = {0, 0}; + status.busy = busy; + return status; +} + +int32_t StorageVolume::GetInfo(ARM_STORAGE_INFO *infoP) +{ + int32_t rc; + rc = volumeManager->getStorage()->GetInfo(infoP); + if (rc != ARM_DRIVER_OK) { + return ARM_DRIVER_ERROR; + } + + infoP->total_storage = volumeSize; + return ARM_DRIVER_OK; +} + +uint32_t StorageVolume::ResolveAddress(uint64_t addr) { + return (uint32_t)(volumeOffset + addr); +} + +int32_t StorageVolume::GetNextBlock(const ARM_STORAGE_BLOCK* prevP, ARM_STORAGE_BLOCK *nextP) +{ + int32_t rc; + ARM_STORAGE_BLOCK tmpBlock; + do { + /* iterate forward */ + rc = volumeManager->getStorage()->GetNextBlock(prevP, &tmpBlock); + if (rc != ARM_DRIVER_OK) { + return rc; + } + + /* Stop iteration if we have progressed past the boundary of this volume. */ + if (tmpBlock.addr >= (volumeOffset + volumeSize)) { + return ARM_DRIVER_ERROR; + } + } while (!this->overlapsWithBlock(&tmpBlock)); + + if (nextP) { + memcpy(nextP, &tmpBlock, sizeof(ARM_STORAGE_BLOCK)); + transformBlockToVolume(nextP); + } + return ARM_DRIVER_OK; +} + +int32_t StorageVolume::GetBlock(uint64_t addr, ARM_STORAGE_BLOCK *blockP) +{ + ARM_STORAGE_BLOCK tmpBlock; + int32_t rc = volumeManager->getStorage()->GetBlock(ResolveAddress(addr), &tmpBlock); + if (rc != ARM_DRIVER_OK) { + return rc; + } + if (!this->overlapsWithBlock(&tmpBlock)) { + return ARM_DRIVER_ERROR; + } + + if (blockP) { + memcpy(blockP, &tmpBlock, sizeof(ARM_STORAGE_BLOCK)); + transformBlockToVolume(blockP); + } + return ARM_DRIVER_OK; +} diff --git a/features/storage/FEATURE_STORAGE/storage-volume-manager/source/storage_volume_manager.cpp b/features/storage/FEATURE_STORAGE/storage-volume-manager/source/storage_volume_manager.cpp new file mode 100644 index 00000000000..e0bc71dba22 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/storage-volume-manager/source/storage_volume_manager.cpp @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2006-2016, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "storage-volume-manager/storage_volume_manager.h" +#include +#include + +/* redefine these macros to a printf() equivalent to emit trace */ +#define tr_debug(...) +#define tr_error(...) + +/* + * The following variable records the volume-manager instance which last setup + * StorageVolumeManager::storageCallback() as a callback for a storage driver. + * We use this value in the callback handler to de-multiplex the callback into a + * volume-manager. + */ +static StorageVolumeManager *activeVolumeManager; + +InitializeCallback_t initializeCallback; + +#define STORAGE_API_DEFINITIONS_FOR_VOLUME(N) \ +extern "C" ARM_DRIVER_VERSION GetVersion_ ## N(void) { \ + return activeVolumeManager->volumes[(N)].GetVersion(); \ +} \ +ARM_STORAGE_CAPABILITIES GetCapabilities_ ## N(void) { \ + return activeVolumeManager->volumes[(N)].GetCapabilities(); \ +} \ +int32_t Initialize_ ## N(ARM_Storage_Callback_t callback) { \ + return activeVolumeManager->volumes[(N)].Initialize(callback); \ +} \ +int32_t Uninitialize_ ## N(void) { \ + return activeVolumeManager->volumes[(N)].Uninitialize(); \ +} \ +int32_t PowerControl_ ## N(ARM_POWER_STATE state) { \ + return activeVolumeManager->volumes[(N)].PowerControl(state); \ +} \ +int32_t ReadData_ ## N(uint64_t addr, void *data, uint32_t size) { \ + return activeVolumeManager->volumes[(N)].ReadData(addr, data, size); \ +} \ +int32_t ProgramData_ ## N(uint64_t addr, const void *data, uint32_t size) { \ + return activeVolumeManager->volumes[(N)].ProgramData(addr, data, size); \ +} \ +int32_t Erase_ ## N(uint64_t addr, uint32_t size) { \ + return activeVolumeManager->volumes[(N)].Erase(addr, size); \ +} \ +int32_t EraseAll_ ## N(void) { \ + return activeVolumeManager->volumes[(N)].EraseAll(); \ +} \ +ARM_STORAGE_STATUS GetStatus_ ## N(void) { \ + return activeVolumeManager->volumes[(N)].GetStatus(); \ +} \ +int32_t GetInfo_ ## N(ARM_STORAGE_INFO *infoP) { \ + return activeVolumeManager->volumes[(N)].GetInfo(infoP); \ +} \ +uint32_t ResolveAddress_ ## N(uint64_t addr) { \ + return activeVolumeManager->volumes[(N)].ResolveAddress(addr); \ +} \ +int32_t GetNextBlock_ ## N(const ARM_STORAGE_BLOCK* prevP, ARM_STORAGE_BLOCK *nextP) { \ + return activeVolumeManager->volumes[(N)].GetNextBlock(prevP, nextP); \ +} \ +int32_t GetBlock_ ## N(uint64_t addr, ARM_STORAGE_BLOCK *blockP) { \ + return activeVolumeManager->volumes[(N)].GetBlock(addr, blockP); \ +} \ +ARM_DRIVER_STORAGE VIRTUAL_MTD_ ## N = { \ + .GetVersion = GetVersion_ ## N, \ + .GetCapabilities = GetCapabilities_ ## N, \ + .Initialize = Initialize_ ## N, \ + .Uninitialize = Uninitialize_ ## N, \ + .PowerControl = PowerControl_ ## N, \ + .ReadData = ReadData_ ## N, \ + .ProgramData = ProgramData_ ## N, \ + .Erase = Erase_ ## N, \ + .EraseAll = EraseAll_ ## N, \ + .GetStatus = GetStatus_ ## N, \ + .GetInfo = GetInfo_ ## N, \ + .ResolveAddress = ResolveAddress_ ## N, \ + .GetNextBlock = GetNextBlock_ ## N, \ + .GetBlock = GetBlock_ ## N, \ +}; + +#define STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_1 STORAGE_API_DEFINITIONS_FOR_VOLUME(0) +#define STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_2 STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_1 STORAGE_API_DEFINITIONS_FOR_VOLUME(1) +#define STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_3 STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_2 STORAGE_API_DEFINITIONS_FOR_VOLUME(2) +#define STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_4 STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_3 STORAGE_API_DEFINITIONS_FOR_VOLUME(3) +/* ... add more of the above if ever needed */ + +#define STORAGE_API_DEFINITIONS_FOR_VOLUMES(N) EXPAND(CONCATENATE(STORAGE_API_DEFINITIONS_LIST_FOR_VOLUME_, N)) + +STORAGE_API_DEFINITIONS_FOR_VOLUMES(MAX_VOLUMES); + +int32_t StorageVolumeManager::initialize(ARM_DRIVER_STORAGE *mtd, InitializeCallback_t callback) +{ + activeVolume = NULL; + initializeCallback = callback; + + storage = mtd; + storageCapabilities = mtd->GetCapabilities(); + + int32_t rc = mtd->GetInfo(&storageInfo); + if (rc != ARM_DRIVER_OK) { + tr_error("StorageVolumeManager::initialize: call to GetInfo() failed with %" PRId32, rc); + return ARM_DRIVER_ERROR; + } + + rc = mtd->Initialize(storageCallback); + if (rc < ARM_DRIVER_OK) { + tr_error("Initialize() failed with error %" PRId32, rc); + return ARM_DRIVER_ERROR; + } + + activeVolumeManager = this; + if (rc == ARM_DRIVER_OK) { + /* there is pending asynchronous activity which will result in a callback later */ + return ARM_DRIVER_OK; + } + + /* Clear previously allocated volumes */ + for (size_t i = 0; i < MAX_VOLUMES; i++) { + if (volumes[i].isAllocated()) { + volumes[i].deallocate(); + } + } + + /* synchronous completion */ + initialized = true; + return 1; +} + +int32_t StorageVolumeManager::addVolume(uint64_t addr, uint64_t size, StorageVolume **volumePP) +{ + tr_debug("StorageVolumeManager_addVolume: addr = %" PRIu32 ", size = %" PRIu32, (uint32_t)addr, (uint32_t)size); + + *volumePP = NULL; + + /* + * sanity checks for arguments + */ + ARM_STORAGE_INFO info; + int32_t rc; + rc = storage->GetInfo(&info); + if (rc != ARM_DRIVER_OK) { + tr_error("StorageVolumeManager_addVolume: storage->GetInfo() failed with %" PRId32, rc); + return ARM_DRIVER_ERROR; + } + if (size > info.total_storage) { + tr_error("StorageVolumeManager_addVolume: 'size' parameter too large: %" PRIu32, (uint32_t)size); + return ARM_DRIVER_ERROR; + } + ARM_STORAGE_BLOCK firstBlock; + rc = storage->GetNextBlock(NULL, &firstBlock); + if (rc != ARM_DRIVER_OK) { + tr_error("StorageVolumeManager_addVolume: storage->GetNextBlock() failed with %" PRId32, rc); + return ARM_DRIVER_ERROR; + } + if ((addr < firstBlock.addr) || ((addr + size) > (firstBlock.addr + info.total_storage))) { + tr_error("StorageVolumeManager_addVolume: given range [%" PRIu32 ", %" PRIu32 ") isn't entirely contained within available storage range [%" PRIu32 ", %" PRIu32 ")", + (uint32_t)addr, (uint32_t)(addr + size), (uint32_t)firstBlock.addr, (uint32_t)(firstBlock.addr + info.total_storage)); + return ARM_DRIVER_ERROR; + } + + /* Find an unused volume. */ + uint32_t index = findIndexOfUnusedVolume(); + if (index == MAX_VOLUMES) { + return STORAGE_VOLUME_MANAGER_STATUS_ERROR_EXHASTED_VOLUMES; + } + + /* setup volume */ + StorageVolume *volumeP = &volumes[index]; + volumeP->setup(addr, size, this); + *volumePP = volumeP; + tr_debug("StorageVolumeManager_addVolume: setup volume at index %" PRIu32, index); + + return ARM_DRIVER_OK; +} + +int32_t StorageVolumeManager::addVolume_C(uint64_t addr, uint64_t size, _ARM_DRIVER_STORAGE *mtd) +{ + int32_t rc; + StorageVolume *volumeP; + if ((rc = addVolume(addr, size, &volumeP)) < ARM_DRIVER_OK) { + return rc; + } + + /* locate index of the allocated volume */ + size_t index; + for (index = 0; index < MAX_VOLUMES; index++) { + if (volumes[index].isAllocated() && (&volumes[index] == volumeP)) { + break; + } + } + if (index == MAX_VOLUMES) { + return STORAGE_VOLUME_MANAGER_STATUS_ERROR_EXHASTED_VOLUMES; + } + + if (index == 0) { + *mtd = VIRTUAL_MTD_0; + } else if (index == 1) { + *mtd = VIRTUAL_MTD_1; + } else if (index == 2) { + *mtd = VIRTUAL_MTD_2; + } else if (index == 3) { + *mtd = VIRTUAL_MTD_3; + } else { + return ARM_DRIVER_ERROR; + } + + return ARM_DRIVER_OK; +} + +int32_t StorageVolumeManager::lookupVolume(uint64_t addr, StorageVolume **volumePP) +{ + /* + * Traverse the volumes in reverse order of creation; this allows newly created volumes to supersede the older ones. + */ + for (size_t index = MAX_VOLUMES - 1; index > 0; --index) { + StorageVolume *volume = &volumes[index]; + if ((addr >= volume->getVolumeOffset()) && (addr < (volume->getVolumeOffset() + volume->getVolumeSize()))) { + *volumePP = volume; + return ARM_DRIVER_OK; + } + } + + return ARM_DRIVER_ERROR; +} + +void StorageVolumeManager::storageCallback(int32_t status, ARM_STORAGE_OPERATION operation) +{ + tr_debug("StorageVolumeManager_callback: operation = %u", operation); + StorageVolumeManager *volumeManager = activeVolumeManager; + + switch (operation) { + case ARM_STORAGE_OPERATION_INITIALIZE: + volumeManager->initialized = true; + if (initializeCallback != NULL) { + initializeCallback(status); + } + break; + + case ARM_STORAGE_OPERATION_POWER_CONTROL: + case ARM_STORAGE_OPERATION_READ_DATA: + case ARM_STORAGE_OPERATION_PROGRAM_DATA: + case ARM_STORAGE_OPERATION_ERASE: + case ARM_STORAGE_OPERATION_ERASE_ALL: + if (volumeManager->activeVolume != NULL) { + /* Reset activeVolume and invoke callback. We reset activeVolume before the + * callback because the callback may attempt to launch another asynchronous + * operation, which requires 'activeVolume' to be NULL. */ + StorageVolume *callbackVolume = volumeManager->activeVolume; /* remember the volume which will receive the callback. */ + volumeManager->activeVolume = NULL; + + if (callbackVolume->isAllocated() && callbackVolume->getCallback()) { + (callbackVolume->getCallback())(status, operation); + } + } + break; + + default: + tr_error("StorageVolumeManager_callback: unknown operation %u", operation); + break; + } +} + +size_t StorageVolumeManager::findIndexOfUnusedVolume(void) const { + size_t index; + for (index = 0; index < MAX_VOLUMES; index++) { + if (!volumes[index].isAllocated()) { + break; + } + } + return index; +} diff --git a/features/storage/FEATURE_STORAGE/storage-volume-manager/storage-volume-manager/storage_volume_manager.h b/features/storage/FEATURE_STORAGE/storage-volume-manager/storage-volume-manager/storage_volume_manager.h new file mode 100644 index 00000000000..c3b12e54e48 --- /dev/null +++ b/features/storage/FEATURE_STORAGE/storage-volume-manager/storage-volume-manager/storage_volume_manager.h @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2006-2016, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __STORAGE_VOLUME_MANAGER_H__ +#define __STORAGE_VOLUME_MANAGER_H__ + +#ifndef __cplusplus +#error "This abstraction requires a C++ toolchain" +#endif // __cplusplus + +#include "storage_abstraction/Driver_Storage.h" + +#if !defined(YOTTA_CFG_STORAGE_VOLUME_MANAGER_MAX_VOLUMES) +#define MAX_VOLUMES 4 +#else +#define MAX_VOLUMES YOTTA_CFG_STORAGE_VOLUME_MANAGER_MAX_VOLUMES +#endif +/**< + * A static assert to ensure that the size of SequentialJournal is smaller than + * FlashJournal_t. The caller will only allocate a FlashJournal_t and expect the + * Sequential Strategy to reuse that space for a SequentialFlashJournal_t. + */ +#ifndef TOOLCHAIN_IAR +typedef char AssertStorageVolumeManagerMaxVolumesIsSane[(((MAX_VOLUMES) > 0) && ((MAX_VOLUMES) <= 8)) ? 0:-1]; +#endif + +#define CONCATENATE(A, B) A ## B +#define EXPAND(X) X /* this adds a level of indirection needed to allow macro-expansion following a token-paste operation (see use of CONCATENATE() below). */ + +#define STORAGE_API_EXTERN_C_DECLARATIONS_FOR_VOLUME(N) \ + extern "C" ARM_DRIVER_VERSION GetVersion_ ## N(void); \ + extern "C" ARM_STORAGE_CAPABILITIES GetCapabilities_ ## N(void); \ + extern "C" int32_t Initialize_ ## N(ARM_Storage_Callback_t callback); \ + extern "C" int32_t Uninitialize_ ## N(void); \ + extern "C" int32_t PowerControl_ ## N(ARM_POWER_STATE state); \ + extern "C" int32_t ReadData_ ## N(uint64_t addr, void *data, uint32_t size); \ + extern "C" int32_t ProgramData_ ## N(uint64_t addr, const void *data, uint32_t size); \ + extern "C" int32_t Erase_ ## N(uint64_t addr, uint32_t size); \ + extern "C" int32_t EraseAll_ ## N(void); \ + extern "C" ARM_STORAGE_STATUS GetStatus_ ## N(void); \ + extern "C" int32_t GetInfo_ ## N(ARM_STORAGE_INFO *infoP); \ + extern "C" uint32_t ResolveAddress_ ## N(uint64_t addr); \ + extern "C" int32_t GetNextBlock_ ## N(const ARM_STORAGE_BLOCK* prevP, ARM_STORAGE_BLOCK *nextP); \ + extern "C" int32_t GetBlock_ ## N(uint64_t addr, ARM_STORAGE_BLOCK *blockP); + +#define STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_1 STORAGE_API_EXTERN_C_DECLARATIONS_FOR_VOLUME(0) +#define STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_2 STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_1 STORAGE_API_EXTERN_C_DECLARATIONS_FOR_VOLUME(1) +#define STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_3 STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_2 STORAGE_API_EXTERN_C_DECLARATIONS_FOR_VOLUME(2) +#define STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_4 STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_3 STORAGE_API_EXTERN_C_DECLARATIONS_FOR_VOLUME(3) +/* ... add more of the above if ever needed */ + +#define STORAGE_API_EXTERN_C_DECLARATIONS_LIST(N) EXPAND(CONCATENATE(STORAGE_API_EXTERN_C_DECLARATIONS_LIST_FOR_, N)) + +STORAGE_API_EXTERN_C_DECLARATIONS_LIST(MAX_VOLUMES); + +/** + * Error return codes specific to the Storage volume manager. These extend the + * common error codes from ARM_DRIVER_STORAGE. All Volume-manager APIs return an + * int32_t to allow for both error and success status returns. This enumeration + * contains all possible error status values. + */ +typedef enum _StorageVolumeManager_Status +{ + STORAGE_VOLUME_MANAGER_STATUS_ERROR_EXHASTED_VOLUMES = -7, ///< exhausted the supply of available volumes + STORAGE_VOLUME_MANAGER_STATUS_ERROR_NOT_ERASABLE = -8, ///< Part (or all) of the range provided to Erase() isn't erasable. + STORAGE_VOLUME_MANAGER_STATUS_ERROR_NOT_PROGRAMMABLE = -9, ///< Part (or all) of the range provided to ProgramData() isn't programmable. + STORAGE_VOLUME_MANAGER_STATUS_ERROR_PROTECTED = -10, ///< Part (or all) of the range to Erase() or ProgramData() is protected. + STORAGE_VOLUME_MANAGER_STATUS_ERROR_NOT_INITIALIZED = -11, ///< underlying storage not initialized + STORAGE_VOLUME_MANAGER_STATUS_ERROR_VOLUME_NOT_ALLOCATED = -12, ///< attempt to operate on an unallocated volume +} StorageVolumeManager_Status_t; + +typedef void (*InitializeCallback_t)(int32_t status); +class StorageVolumeManager; /* forward declaration */ + +class StorageVolume { +public: + StorageVolume() : allocated(false) { /* empty */ } + +public: + void setup(uint64_t addr, uint64_t size, StorageVolumeManager *volumeManager); + + /* + * Mimic the API of ARM_DRIVER_STORAGE + */ +public: + ARM_DRIVER_VERSION GetVersion(void); + ARM_STORAGE_CAPABILITIES GetCapabilities(void); + int32_t Initialize(ARM_Storage_Callback_t callback); + int32_t Uninitialize(void); + int32_t PowerControl(ARM_POWER_STATE state); + int32_t ReadData(uint64_t addr, void *data, uint32_t size); + int32_t ProgramData(uint64_t addr, const void *data, uint32_t size); + int32_t Erase(uint64_t addr, uint32_t size); + int32_t EraseAll(void); + ARM_STORAGE_STATUS GetStatus(void); + int32_t GetInfo(ARM_STORAGE_INFO *infoP); + uint32_t ResolveAddress(uint64_t addr); + int32_t GetNextBlock(const ARM_STORAGE_BLOCK* prevP, ARM_STORAGE_BLOCK *nextP); + int32_t GetBlock(uint64_t addr, ARM_STORAGE_BLOCK *blockP); + +public: + bool isAllocated(void) const { + return allocated; + } + + void deallocate(void) { + allocated = false; + } + + /* + * Accessor methods. + */ + + uint64_t getVolumeOffset(void) const { + return volumeOffset; + } + uint64_t getVolumeSize(void) const { + return volumeSize; + } + const ARM_Storage_Callback_t &getCallback(void) const { + return callback; + } + +private: + bool overlapsWithBlock(const ARM_STORAGE_BLOCK* blockP) const { + return (((blockP->addr + blockP->size) <= volumeOffset) || ((volumeOffset + volumeSize) <= blockP->addr)) ? false : true; + } + + void transformBlockToVolume(ARM_STORAGE_BLOCK *blockP) const { + if (blockP->addr < volumeOffset) { + blockP->addr = volumeOffset; + } + if ((blockP->addr + blockP->size) > (volumeOffset + volumeSize)) { + blockP->size = (volumeOffset + volumeSize) - blockP->addr; + } + + blockP->addr -= volumeOffset; + } + +private: + bool allocated; + uint64_t volumeOffset; + uint64_t volumeSize; + ARM_Storage_Callback_t callback; + StorageVolumeManager *volumeManager; +}; + +class StorageVolumeManager { +public: + StorageVolumeManager() { /* empty */ } + ~StorageVolumeManager() { /* empty */ } + + /** + * Initialize the storage MTD and prepare it for operation within the context of the volume manager. + * + * @param[in] storage + * The underlying MTD. + * @param[in] callback + * A callback to be invoked upon completion of initialization. + * + * @return If asynchronous activity is launched, an invocation returns + * ARM_DRIVER_OK, and the caller can expect to receive a + * callback in the future with a status value of ARM_DRIVER_OK or an error- + * code. In the case of synchronous execution, control returns after + * completion with a value of 1. Return values less than + * ARM_DRIVER_OK (0) signify errors. + */ + int32_t initialize(ARM_DRIVER_STORAGE *mtd, InitializeCallback_t callback); + + int32_t addVolume(uint64_t addr, uint64_t size, StorageVolume **volumePP); + int32_t addVolume_C(uint64_t addr, uint64_t size, _ARM_DRIVER_STORAGE *mtd); + int32_t lookupVolume(uint64_t addr, StorageVolume **volumePP); + + /* + * Accessor methods. + */ + + bool isInitialized() const { + return initialized; + } + ARM_DRIVER_STORAGE *getStorage(void) const { + return storage; + } + const ARM_STORAGE_INFO &getStorageInfo(void) const { + return storageInfo; + } + const ARM_STORAGE_CAPABILITIES &getStorageCapabilities(void) const { + return storageCapabilities; + } + StorageVolume *volumeAtIndex(size_t index) { + return &volumes[index]; + } + +public: + static void storageCallback(int32_t status, ARM_STORAGE_OPERATION operation); + +private: + friend int32_t StorageVolume::PowerControl(ARM_POWER_STATE state); + friend int32_t StorageVolume::ReadData(uint64_t addr, void *data, uint32_t size); + friend int32_t StorageVolume::ProgramData(uint64_t addr, const void *data, uint32_t size); + friend int32_t StorageVolume::Erase(uint64_t addr, uint32_t size); + friend int32_t StorageVolume::EraseAll(void); + friend ARM_STORAGE_STATUS StorageVolume::GetStatus(void); + StorageVolume *activeVolume; /* This state-variable is set to point to a volume + * while there is pending activity. It tracks + * the volume which is at the source of the + * activity. Once the activity finishes, this + * variable is reset. Having this variable set + * might be used as an indication that the + * underlying storage is busy. */ + +#define FRIEND_DECLARATIONS_FOR_VOLUME(N) \ + friend ARM_DRIVER_VERSION GetVersion_ ## N(void); \ + friend ARM_STORAGE_CAPABILITIES GetCapabilities_ ## N(void); \ + friend int32_t Initialize_ ## N(ARM_Storage_Callback_t callback); \ + friend int32_t Uninitialize_ ## N(void); \ + friend int32_t PowerControl_ ## N(ARM_POWER_STATE state); \ + friend int32_t ReadData_ ## N(uint64_t addr, void *data, uint32_t size); \ + friend int32_t ProgramData_ ## N(uint64_t addr, const void *data, uint32_t size); \ + friend int32_t Erase_ ## N(uint64_t addr, uint32_t size); \ + friend int32_t EraseAll_ ## N(void); \ + friend ARM_STORAGE_STATUS GetStatus_ ## N(void); \ + friend int32_t GetInfo_ ## N(ARM_STORAGE_INFO *infoP); \ + friend uint32_t ResolveAddress_ ## N(uint64_t addr); \ + friend int32_t GetNextBlock_ ## N(const ARM_STORAGE_BLOCK* prevP, ARM_STORAGE_BLOCK *nextP); \ + friend int32_t GetBlock_ ## N(uint64_t addr, ARM_STORAGE_BLOCK *blockP); + +#define FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_1 FRIEND_DECLARATIONS_FOR_VOLUME(0) +#define FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_2 FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_1 FRIEND_DECLARATIONS_FOR_VOLUME(1) +#define FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_3 FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_2 FRIEND_DECLARATIONS_FOR_VOLUME(2) +#define FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_4 FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_3 FRIEND_DECLARATIONS_FOR_VOLUME(3) +/* ... add more of the above if ever needed */ + +#define FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES(N) EXPAND(CONCATENATE(FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES_FOR_, N)) + + //todo: remove FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES(MAX_VOLUMES); + FRIEND_DECLARATIONS_FOR_STORAGE_API_INSTANCES(MAX_VOLUMES) + +private: + size_t findIndexOfUnusedVolume(void) const; + +private: + bool initialized; + ARM_DRIVER_STORAGE *storage; + ARM_STORAGE_INFO storageInfo; + ARM_STORAGE_CAPABILITIES storageCapabilities; + StorageVolume volumes[MAX_VOLUMES]; +}; + +#endif /* __STORAGE_VOLUME_MANAGER_H__ */ diff --git a/hal/targets/hal/TARGET_Freescale/TARGET_KSDK2_MCUS/TARGET_MCU_K64F/storage_driver.c b/hal/targets/hal/TARGET_Freescale/TARGET_KSDK2_MCUS/TARGET_MCU_K64F/storage_driver.c index a832e239be6..6edb9796e3c 100644 --- a/hal/targets/hal/TARGET_Freescale/TARGET_KSDK2_MCUS/TARGET_MCU_K64F/storage_driver.c +++ b/hal/targets/hal/TARGET_Freescale/TARGET_KSDK2_MCUS/TARGET_MCU_K64F/storage_driver.c @@ -15,8 +15,6 @@ * limitations under the License. */ -#if DEVICE_STORAGE - #include "Driver_Storage.h" #include "cmsis_nvic.h" #include "MK64F12.h" @@ -33,6 +31,10 @@ #include +/* Redefine this macro to a printf equivalent to print trace */ +#define tr_debug(...) + + #ifdef USING_KSDK2 /*! * @name Misc utility defines @@ -142,6 +144,76 @@ extern volatile uint32_t *const kFCCOBx; #define SIZEOF_DOUBLE_PHRASE (16) #endif /* #ifdef USING_KSDK2 */ +/* While the K64F flash controller is capable of launching operations asynchronously and + * allowing program execution to continue while an erase/program is active, it + * doesn't allow simultaneous read accesses while and erase/program is active on + * the same block of flash. + * + * Read/fetch accesses can originate arbitrarily as a result of program + * execution. This means that code which operates on flash should not reside in + * flash; or at least it should not reside in the same bank of flash as it is + * operating upon. The only way to ensure that application code and flash driver + * are residing on separate banks of flash is to reserve bank-0 (or BLOCK0) for + * the application and bank-1 (BLOCK1) for the driver--this also happens to be + * the default setting. + * + * But it is quite likely that this default will be over-ridden by the use of + * config options depending upon the actual application. If we don't have a + * clean separation between the application and the space managed by this + * driver, then we need to enforce the following: + * + * - Force synchronous mode of execution in the storage_driver. + * - Disable interrupts during erase/program operations. + * - Ensure all code and data structures used in the storage driver execute + * out of RAM. Refer to __RAMFUNC (below) which allows for this. + * + * It is difficult to determine the application's span of internal-flash at + * compile time. Therefore we assume that STORAGE_START_ADDR is the + * boundary between application and this driver. When this boundary is set to + * lie at BLOCK1_START_ADDR, there is no possibility of read-while-write run- + * time errors. + * + * In the following, caps.asynchronous_ops is defined to be 1 if and only if + * asynchronous operation mode is requested and there doesn't exist the + * possibility of concurrent reads. + */ + +#if (defined(STORAGE_START_ADDR) && (STORAGE_START_ADDR != BLOCK1_START_ADDR)) +#define EXISTS_POSSIBILITY_OF_CONCURRENT_READ 1 +#else +#define EXISTS_POSSIBILITY_OF_CONCURRENT_READ 0 +#endif + +/* Define '__RAMFUNC' as an attribute to mark a function as residing in RAM. + * Use of __RAMFUNC puts a function in the .data section--i.e. the + * initialized data section. This will be copied into RAM automatically by the + * startup sequence. */ +#ifndef __RAMFUNC +#if defined(__GNUC__) || defined(__clang__) // GCC and llvm/clang +#define __RAMFUNC __attribute__ ((section (".data#"), noinline)) /* The '#' following ".data" needs a bit of + * explanation. Without it, we are liable to get the following warning 'Warning: ignoring + * changed section attributes for .data'. This is because __attribute__((section(".data"))) + * generates the following assembly: + * + * .section .data,"ax",%progbits + * + * But .data doesn't need the 'x' (execute) attribute bit. To remove the warning, we specify + * the attribute with a '#' at the tail, which emits: + * + * .section .data#,"ax",%progbits + * + * Note that '#' (in the above) acts like a comment-start, and masks the additional + * attributes which don't apply to '.data'. + */ +#elif defined (__CC_ARM) +#define __RAMFUNC __attribute__ ((section(".ramfunc"), noinline)) +#elif defined ( __ICCARM__ ) +#define __RAMFUNC __ramfunc +#else // unknown compiler + #error "This compiler is not yet supported. If you can contribute support for defining a function to be RAM resident, please provide a definition for __RAMFUNC" +#endif +#endif /* #ifndef __RAMFUNC */ + /* * forward declarations */ @@ -169,16 +241,16 @@ struct mtd_k64f_data { static const ARM_STORAGE_BLOCK blockTable[] = { { /**< This is the start address of the flash block. */ -#ifdef DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_START_ADDR - .addr = DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_START_ADDR, +#ifdef STORAGE_START_ADDR + .addr = STORAGE_START_ADDR, #else .addr = BLOCK1_START_ADDR, #endif /**< This is the size of the flash block, in units of bytes. * Together with addr, it describes a range [addr, addr+size). */ -#ifdef DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_SIZE - .size = DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_SIZE, +#ifdef STORAGE_SIZE + .size = STORAGE_SIZE, #else .size = BLOCK1_SIZE, #endif @@ -200,7 +272,8 @@ static const ARM_DRIVER_VERSION version = { }; -#if (!defined(DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS) || DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS) +#if ((!defined(STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS) || STORAGE_CONFIG_HARDWARE_MTD_K64F_ASYNC_OPS) && \ + !EXISTS_POSSIBILITY_OF_CONCURRENT_READ) #define ASYNC_OPS 1 #else #define ASYNC_OPS 0 @@ -219,8 +292,8 @@ static const ARM_STORAGE_CAPABILITIES caps = { .asynchronous_ops = ASYNC_OPS, /* Enable chip-erase functionality if we own all of block-1. */ - #if ((!defined (DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_START_ADDR) || (DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_START_ADDR == BLOCK1_START_ADDR)) && \ - (!defined (DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_SIZE) || (DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_SIZE == BLOCK1_SIZE))) + #if ((!defined (STORAGE_START_ADDR) || (STORAGE_START_ADDR == BLOCK1_START_ADDR)) && \ + (!defined (STORAGE_SIZE) || (STORAGE_SIZE == BLOCK1_SIZE))) .erase_all = 1, /**< Supports EraseChip operation. */ #else .erase_all = 0, /**< Supports EraseChip operation. */ @@ -228,8 +301,8 @@ static const ARM_STORAGE_CAPABILITIES caps = { }; static const ARM_STORAGE_INFO info = { -#ifdef DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_SIZE - .total_storage = DEVICE_STORAGE_CONFIG_HARDWARE_MTD_K64F_SIZE, /**< Total available storage, in units of octets. */ +#ifdef STORAGE_SIZE + .total_storage = STORAGE_SIZE, /**< Total available storage, in units of octets. */ #else .total_storage = BLOCK1_SIZE, /**< Total available storage, in units of octets. By default, BLOCK0 is reserved to hold program code. */ #endif @@ -410,6 +483,10 @@ static inline void launchCommand(void) #else /* #if !ASYNC_OPS */ +#if EXISTS_POSSIBILITY_OF_CONCURRENT_READ +/* This function needs to execute from RAM to avoid read-while-write errors. */ +__RAMFUNC +#endif static void launchCommandAndWaitForCompletion() { // It contains the inlined equivalent of the following code snippet: @@ -418,10 +495,17 @@ static void launchCommandAndWaitForCompletion() // /* Spin waiting for the command execution to complete. */ // } +#ifdef USING_KSDK2 FTFx->FSTAT = FTFx_FSTAT_CCIF_MASK; /* launchcommand() */ while ((FTFx->FSTAT & FTFx_FSTAT_CCIF_MASK) == 0) { /* Spin waiting for the command execution to complete. */ } +#else + BW_FTFE_FSTAT_CCIF(FTFE, 1); /* launchCommand() */ + while (BR_FTFE_FSTAT_CCIF(FTFE) == 0) { + /* Spin waiting for the command execution to complete. */ + } +#endif } #endif /* #if !ASYNC_OPS */ @@ -562,6 +646,7 @@ static inline void setupNextProgramData(struct mtd_k64f_data *context) if ((context->amountLeftToOperate == PROGRAM_PHRASE_SIZEOF_INLINE_DATA) || ((context->currentOperatingStorageAddress % SIZEOF_DOUBLE_PHRASE) == PROGRAM_PHRASE_SIZEOF_INLINE_DATA)) { setup8ByteWrite(context->currentOperatingStorageAddress, context->currentOperatingData); + tr_debug("setupNextProgramData: W8, [%lu]", (uint32_t)context->currentOperatingStorageAddress); context->amountLeftToOperate -= PROGRAM_PHRASE_SIZEOF_INLINE_DATA; context->currentOperatingStorageAddress += PROGRAM_PHRASE_SIZEOF_INLINE_DATA; @@ -569,6 +654,7 @@ static inline void setupNextProgramData(struct mtd_k64f_data *context) } else { size_t amount = sizeofLargestProgramSection(context->currentOperatingStorageAddress, context->amountLeftToOperate); setupProgramSection(context->currentOperatingStorageAddress, context->currentOperatingData, amount); + tr_debug("setupNextProgramData: W%u, [%lu]", amount, (uint32_t)context->currentOperatingStorageAddress); context->amountLeftToOperate -= amount; context->currentOperatingStorageAddress += amount; @@ -607,6 +693,8 @@ static inline void setupNextErase(struct mtd_k64f_data *context) static int32_t executeCommand(struct mtd_k64f_data *context) { + tr_debug("executeCommand: top"); + #if ASYNC_OPS /* Asynchronous operation */ (void)context; /* avoid compiler warning about un-used variables */ @@ -624,12 +712,21 @@ static int32_t executeCommand(struct mtd_k64f_data *context) enableCommandCompletionInterrupt(); + tr_debug("executeCommand: async. return"); return ARM_DRIVER_OK; /* signal asynchronous completion. An interrupt will signal completion later. */ #else /* #if ASYNC_OPS */ - /* Synchronous operation. */ + /* Synchronous operation. This is the common case. */ while (1) { + tr_debug("executeCommand: synchronous iteration"); + + #if EXISTS_POSSIBILITY_OF_CONCURRENT_READ + __disable_irq(); + #endif launchCommandAndWaitForCompletion(); + #if EXISTS_POSSIBILITY_OF_CONCURRENT_READ + __enable_irq(); + #endif /* Execution may result in failure. Check for errors */ if (failedWithAccessError() || failedWithProtectionError()) { @@ -664,6 +761,7 @@ static int32_t executeCommand(struct mtd_k64f_data *context) break; } else { /* erase can be skipped since this sector is already erased. */ + tr_debug("fast forward erase"); progressEraseContextByEraseUnit(context); } } @@ -691,6 +789,7 @@ static inline void launchCommandFromIRQ(const struct mtd_k64f_data *context) if (failedWithAccessError() || failedWithProtectionError()) { clearErrorStatusBits(); if (context->commandCompletionCallback) { + tr_debug("irq: invoking callback with error"); context->commandCompletionCallback(ARM_DRIVER_ERROR_PARAMETER, context->currentCommand); } return; @@ -726,6 +825,7 @@ static void ftfe_ccie_irq_handler(void) case ARM_STORAGE_OPERATION_PROGRAM_DATA: if (context->amountLeftToOperate == 0) { if (context->commandCompletionCallback) { + tr_debug("irq: [PROGRAM] invoking callback"); context->commandCompletionCallback(context->sizeofCurrentOperation, ARM_STORAGE_OPERATION_PROGRAM_DATA); } return; @@ -745,11 +845,13 @@ static void ftfe_ccie_irq_handler(void) break; } else { /* erase can be skipped since this sector is already erased. */ + tr_debug("fast forward erase"); progressEraseContextByEraseUnit(context); } } if (context->amountLeftToOperate == 0) { if (context->commandCompletionCallback) { + tr_debug("irq: [ERASE] invoking callback"); context->commandCompletionCallback(context->sizeofCurrentOperation, ARM_STORAGE_OPERATION_ERASE); } return; @@ -761,6 +863,7 @@ static void ftfe_ccie_irq_handler(void) default: if (context->commandCompletionCallback) { + tr_debug("irq: [default] invoking callback"); context->commandCompletionCallback(ARM_DRIVER_OK, context->currentCommand); } break; @@ -827,6 +930,8 @@ static ARM_STORAGE_CAPABILITIES getCapabilities(void) static int32_t initialize(ARM_Storage_Callback_t callback) { + tr_debug("called initialize(%p)", callback); + struct mtd_k64f_data *context = &mtd_k64f_data; memset(context, 0, sizeof(mtd_k64f_data)); context->currentCommand = ARM_STORAGE_OPERATION_INITIALIZE; @@ -860,6 +965,8 @@ static int32_t initialize(ARM_Storage_Callback_t callback) } static int32_t uninitialize(void) { + tr_debug("called uninitialize"); + struct mtd_k64f_data *context = &mtd_k64f_data; context->currentCommand = ARM_STORAGE_OPERATION_UNINITIALIZE; @@ -883,6 +990,8 @@ static int32_t uninitialize(void) { static int32_t powerControl(ARM_POWER_STATE state) { + tr_debug("called powerControl(%u)", state); + struct mtd_k64f_data *context = &mtd_k64f_data; context->currentCommand = ARM_STORAGE_OPERATION_POWER_CONTROL; @@ -892,6 +1001,8 @@ static int32_t powerControl(ARM_POWER_STATE state) static int32_t readData(uint64_t addr, void *data, uint32_t size) { + tr_debug("called ReadData(%lu, %lu)", (uint32_t)addr, size); + struct mtd_k64f_data *context = &mtd_k64f_data; context->currentCommand = ARM_STORAGE_OPERATION_READ_DATA; @@ -914,6 +1025,8 @@ static int32_t readData(uint64_t addr, void *data, uint32_t size) static int32_t programData(uint64_t addr, const void *data, uint32_t size) { + tr_debug("called ProgramData(%lu, %lu)", (uint32_t)addr, size); + struct mtd_k64f_data *context = &mtd_k64f_data; if (!context->initialized) { return (int32_t)ARM_DRIVER_ERROR; /* illegal */ @@ -955,6 +1068,8 @@ static int32_t programData(uint64_t addr, const void *data, uint32_t size) static int32_t erase(uint64_t addr, uint32_t size) { + tr_debug("called erase(%lu, %lu)", (uint32_t)addr, size); + struct mtd_k64f_data *context = &mtd_k64f_data; if (!context->initialized) { @@ -995,6 +1110,8 @@ static int32_t erase(uint64_t addr, uint32_t size) static int32_t eraseAll(void) { + tr_debug("called eraseAll"); + struct mtd_k64f_data *context = &mtd_k64f_data; if (!context->initialized) { @@ -1105,7 +1222,7 @@ int32_t getBlock(uint64_t addr, ARM_STORAGE_BLOCK *blockP) return ARM_DRIVER_ERROR; } -ARM_DRIVER_STORAGE ARM_Driver_Storage_(0) = { +ARM_DRIVER_STORAGE ARM_Driver_Storage_MTD_K64F = { .GetVersion = getVersion, .GetCapabilities = getCapabilities, .Initialize = initialize, @@ -1121,5 +1238,3 @@ ARM_DRIVER_STORAGE ARM_Driver_Storage_(0) = { .GetNextBlock = nextBlock, .GetBlock = getBlock }; - -#endif /* #if DEVICE_STORAGE */