diff --git a/TESTS/cfstore/add_del/add_del.cpp b/TESTS/cfstore/add_del/add_del.cpp new file mode 100644 index 0000000000..00ab84e7c9 --- /dev/null +++ b/TESTS/cfstore/add_del/add_del.cpp @@ -0,0 +1,378 @@ +/** @file add_del.cpp + * + * 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. + * + * Test cases to add and delete KVs in the CFSTORE. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_add_del_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("ADD_DEL_test_00", cfstore_add_del_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + +#else + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +//#include +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static char cfstore_add_del_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +#ifdef YOTTA_CFG_CFSTORE_UVISOR +/* Create the main box ACL list for the application. + * The main ACL gets inherited by all the other boxes + */ +CFSTORE_UVISOR_MAIN_ACL(cfstore_acl_uvisor_box_add_del_g); + +/* Enable uVisor. */ +UVISOR_SET_MODE_ACL(UVISOR_ENABLED, cfstore_acl_uvisor_box_add_del_g); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +static cfstore_kv_data_t cfstore_add_del_test_07_data[] = { + CFSTORE_INIT_1_TABLE_MID_NODE, + { NULL, NULL}, +}; + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_add_del_test_00(const size_t call_count) +{ + (void) call_count; + ARM_CFSTORE_CAPABILITIES caps = cfstore_driver.GetCapabilities(); + CFSTORE_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", caps.asynchronous_ops); + return CaseNext; +} + +/** + * @brief test to open() a pre-existing key and try to write it, which should fail + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_add_del_test_01_end(const size_t call_count) +{ + bool bfound = false; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + CFSTORE_LOG("cfstore_add_del_test_07: Start%s", "\n"); + memset(&kdesc, 0, sizeof(kdesc)); + memset(&flags, 0, sizeof(flags)); + + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(cfstore_add_del_test_07_data[0].value); + + ret = cfstore_test_create(cfstore_add_del_test_07_data[0].key_name, (char*) cfstore_add_del_test_07_data[0].value, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create KV in store (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + + /* now delete KV*/ + ret = drv->Open(cfstore_add_del_test_07_data[0].key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to Open() (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + + if(hkey != NULL){ + ret = drv->Delete(hkey); + drv->Close(hkey); + hkey = NULL; + } + /* check that the KV has been deleted */ + printf("LOG: WARNING: About to look for non-existent key (key_name=%s) (which will generate internal trace reporting errors if debug trace enabled).\n", cfstore_add_del_test_07_data[0].key_name); + ret = cfstore_test_kv_is_found(cfstore_add_del_test_07_data[0].key_name, &bfound); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error failed to delete a key (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, cfstore_add_del_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Test failed: found KV that was previously deleted (key_name=%s)\n", __func__, cfstore_add_del_test_07_data[0].key_name); + TEST_ASSERT_MESSAGE(bfound == false, cfstore_add_del_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + return CaseNext; +} + + +static cfstore_kv_data_t cfstore_add_del_test_08_data[] = { + CFSTORE_INIT_1_TABLE_HEAD, + CFSTORE_INIT_1_TABLE_MID_NODE, + CFSTORE_INIT_1_TABLE_TAIL, + { NULL, NULL}, +}; + + +/** + * @brief test to add small number of KVs e.g. 3, and then delete them. + * basic delete test: + * - add key(s) + * - delete key(s) + * - make sure can't find key in cfstore + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_add_del_test_02_end(const size_t call_count) +{ + bool bResult = true; // We'll do "&=" cumulative checking. + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_KEYDESC kdesc; + cfstore_kv_data_t* node = NULL; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + CFSTORE_LOG("%s: Start\n", __func__); + memset(&kdesc, 0, sizeof(kdesc)); + + /* create */ + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + node = cfstore_add_del_test_08_data; + while(node->key_name != NULL) + { + len = strlen(node->value); + ret = cfstore_test_create(node->key_name, (char*) node->value, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create kv (key_name=%s.\n", __func__, node->key_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + CFSTORE_LOG("Created KV successfully (key_name=\"%s\", value=\"%s\")\n", node->key_name, node->value); + node++; + } + + /* test delete all */ + ret = cfstore_test_delete_all(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to delete all KVs.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + + /* check there are no KVs present as expected */ + node = cfstore_add_del_test_08_data; + while(node->key_name != NULL) + { + ret = cfstore_test_kv_is_found(node->key_name, &bResult); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: found key when should not be present.\n", __func__); + TEST_ASSERT_MESSAGE(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND && bResult == false, cfstore_add_del_utest_msg_g); + CFSTORE_LOG("Found KV successfully (key_name=\"%s\")\n", node->key_name); + node++; + } + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + return CaseNext; +} + +/** + * @brief add ~50 KVs, and then delete entries at the start, middle and end of sram area + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_add_del_test_03_end(const size_t call_count) +{ + bool bfound = false; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_FMODE flags; + cfstore_kv_data_t *node; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(&flags, 0, sizeof(flags)); + + ret = cfstore_test_init_1(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialise cfstore area with entries\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + + /* delete some keys */ + node = cfstore_add_del_test_08_data; + while(node->key_name != NULL) + { + CFSTORE_DBGLOG("%s:about to delete key (key_name=%s).\n", __func__, node->key_name); + cfstore_test_delete(node->key_name); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error failed to delete a key (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + CFSTORE_LOG("Deleted KV successfully (key_name=\"%s\")\n", node->key_name); + node++; + } + /* check the keys have been deleted */ + node = cfstore_add_del_test_08_data; + while(node->key_name != NULL) + { + ret = cfstore_test_kv_is_found(node->key_name, &bfound); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to delete a key (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, cfstore_add_del_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Test failed: found KV that was previously deleted (key_name=%s)\n", __func__, node->key_name); + TEST_ASSERT_MESSAGE(bfound == false, cfstore_add_del_utest_msg_g); + node++; + } + + /* clean up by deleting all remaining KVs. this is not part of the test */ + ret = cfstore_test_delete_all(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error failed to delete a all KVs (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialize CFSTORE (ret=%" PRId32 ")\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_add_del_utest_msg_g); + return CaseNext; +} + + +/** + * @brief test as per test_09 but using delete_all() on all init_1 data. + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_add_del_test_04(const size_t call_count) +{ + (void) call_count; + /*todo: implement test */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_add_del_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Warn: Not implemented\n", __func__); + CFSTORE_LOG("%s: WARN: requires implementation\n", __func__); + TEST_ASSERT_MESSAGE(true, cfstore_add_del_utest_msg_g); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("ADD_DEL_test_00", cfstore_add_del_test_00), + Case("ADD_DEL_test_01_start", cfstore_utest_default_start), + Case("ADD_DEL_test_01_end", cfstore_add_del_test_01_end), + Case("ADD_DEL_test_02_start", cfstore_utest_default_start), + Case("ADD_DEL_test_02_end", cfstore_add_del_test_02_end), + Case("ADD_DEL_test_03_start", cfstore_utest_default_start), + Case("ADD_DEL_test_03_end", cfstore_add_del_test_03_end), + Case("ADD_DEL_test_04", cfstore_add_del_test_04), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/close/close.cpp b/TESTS/cfstore/close/close.cpp new file mode 100644 index 0000000000..44a0db38e9 --- /dev/null +++ b/TESTS/cfstore/close/close.cpp @@ -0,0 +1,321 @@ +/* + * @file close.cpp + * + * 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. + * + * Test cases to close KVs in the CFSTORE using the drv->Cpen() interface. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_close_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("CLOSE_test_00", cfstore_close_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + +#else + + +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +//#include +#include +#include +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static char cfstore_close_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.close.box1"); +UVISOR_BOX_CONFIG(cfstore_close_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +/* KV data for test_01 */ +static cfstore_kv_data_t cfstore_close_test_01_kv_data[] = { + { "yotta.hello-world.animal{wobbly-dog}{foot}frontLeft", "first_data_"}, + { "yotta.hello-world.animal{wobbly-dog}{foot}frontLeft", "second_data"}, + { NULL, NULL}, +}; + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_close_test_00(const size_t call_count) +{ + (void) call_count; + ARM_CFSTORE_CAPABILITIES caps = cfstore_driver.GetCapabilities(); + CFSTORE_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", caps.asynchronous_ops); + return CaseNext; +} + + +/** @brief basic test + * + * @note + * + * The test does the following: + * 01. create a key with handle hkey1 + * 02. write data of hkey + * 03. opens KV with 2nd handle, hkey2 + * 04. read data with hkey2 and make sure its the same as that written with hkey1 + * 05. write new data with hkey2 + * 06. delete hkey2 + * 07. close hkey2 + * 08. read hkey1 and make sure the data is the newly written data i.e. the key hasnt + * been deleted yet + * 09. try to open KV and make sure unable to do so, as KV is deleting + * 10. close hkey1 + * 11. try to open KV and make sure unable to do so because its now been deleted + * 12. create new KV with same key_name to make sure can re-create the key again. + * + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_close_test_01_end(const size_t call_count) +{ + char read_buf[CFSTORE_KEY_NAME_MAX_LENGTH]; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(hkey1); + ARM_CFSTORE_HANDLE_INIT(hkey2); + cfstore_kv_data_t *node; + ARM_CFSTORE_FMODE flags; + + CFSTORE_DBGLOG("%s:entered\n", __func__); + (void) call_count; + node = &cfstore_close_test_01_kv_data[0]; + memset(&kdesc, 0, sizeof(kdesc)); + memset(&flags, 0, sizeof(flags)); + memset(read_buf, 0, CFSTORE_KEY_NAME_MAX_LENGTH); + + /* step 01 */ + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + CFSTORE_DBGLOG("%s:About to create new node (key_name=\"%s\", value=\"%s\")\n", __func__, node->key_name, node->value); + ret = drv->Create(node->key_name, strlen(node->value), &kdesc, hkey1); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create node (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, node->key_name, node->value, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + CFSTORE_DBGLOG("%s:length of KV=%d (key_name=\"%s\", value=\"%s\")\n", __func__, (int) len, node->key_name, node->value); + + /* step 02 */ + len = strlen(node->value); + ret = drv->Write(hkey1, (char*) node->value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write key (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, node->key_name, node->value, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write full value data (key_name=\"%s\", value=\"%s\"), len=%d, (ret=%d)\n", __func__, node->key_name, node->value, (int) len, (int) ret); + TEST_ASSERT_MESSAGE(len == strlen(node->value), cfstore_close_utest_msg_g); + printf("Created KV successfully (key_name=\"%s\", value=\"%s\")\n", node->key_name, node->value); + + /* step 03: Now open second handle while keeping first handle open */ + flags.read = true; + flags.write = true; + ret = drv->Open(node->key_name, flags, hkey2); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open node (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, node->key_name, node->value, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + len = strlen(node->value) + 1; + printf("Opened KV successfully (key_name=\"%s\", value=\"%s\")\n", node->key_name, node->value); + + /* step 04 */ + ret = drv->Read(hkey2, read_buf, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to read key (key_name=\"%s\", value=\"%s\")\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + /* check read data is as expected */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: read value data (%s) != KV value data (key_name=\"%s\", value=\"%s\")\n", __func__, read_buf, node->key_name, node->value); + TEST_ASSERT_MESSAGE(strncmp(read_buf, node->value, strlen(node->value)) == 0, cfstore_close_utest_msg_g); + + /* step 05 write new data using hkey2 */ + node = &cfstore_close_test_01_kv_data[1]; + len = strlen(node->value); + ret = drv->Write(hkey2, (char*) node->value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write key with 2nd handle (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, node->key_name, node->value, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write full value data (key_name=\"%s\", value=\"%s\"), len=%d, (ret=%" PRId32 ")\n", __func__, node->key_name, node->value, (int) len, ret); + TEST_ASSERT_MESSAGE(len == strlen(node->value), cfstore_close_utest_msg_g); + printf("Wrote KV successfully with new data (key_name=\"%s\", value=\"%s\")\n", node->key_name, node->value); + + /* step 06 delete hkey2 */ + ret = drv->Delete(hkey2); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to Delete KV with 2nd handle (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, node->key_name, node->value, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + /* step 07 close hkey2 */ + ret = drv->Close(hkey2); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to Close KV with 2nd handle (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, node->key_name, node->value, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + /* step 08 read hkey1 for updated data */ + len = strlen(node->value) + 1; + memset(read_buf, 0, len); + ret = drv->Read(hkey1, read_buf, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to read key with hkey1 (key_name=\"%s\", value=\"%s\")\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + /* check read data is as expected */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: read value data (%s) != KV value data (key_name=\"%s\", value=\"%s\")\n", __func__, read_buf, node->key_name, node->value); + TEST_ASSERT_MESSAGE(strncmp(read_buf, node->value, strlen(node->value)) == 0, cfstore_close_utest_msg_g); + + /* step 09 */ + ret = drv->Open(node->key_name, flags, hkey2); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Should not be able to open pre-existing key in deleting state (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, node->key_name, node->value, ret); + TEST_ASSERT_MESSAGE(ret == ARM_CFSTORE_DRIVER_ERROR_PREEXISTING_KEY_DELETING || ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, cfstore_close_utest_msg_g); + + /* step 10 close hkey1 */ + ret = drv->Close(hkey1); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to Close KV with 1st handle (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, node->key_name, node->value, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + /* step 11 try to open KV and make sure unable to do so because its now been deleted */ + ret = drv->Open(node->key_name, flags, hkey1); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Should not be able to open key as it should not be present in the CFSTORE (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, node->key_name, node->value, ret); + TEST_ASSERT_MESSAGE(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, cfstore_close_utest_msg_g); + + /* step 12. create new KV with same key_name to make sure can re-create the key again. */ + node = &cfstore_close_test_01_kv_data[0]; + len = strlen(node->value); + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + CFSTORE_DBGLOG("%s:About to create new node (key_name=\"%s\", value=\"%s\")\n", __func__, node->key_name, node->value); + ret = drv->Create(node->key_name, strlen(node->value), &kdesc, hkey1); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create node (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, node->key_name, node->value, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + CFSTORE_DBGLOG("%s:length of KV=%d (key_name=\"%s\", value=\"%s\")\n", __func__, (int) len, node->key_name, node->value); + + len = strlen(node->value); + ret = drv->Write(hkey1, (char*) node->value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write key (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, node->key_name, node->value, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write full value data (key_name=\"%s\", value=\"%s\"), len=%d, (ret=%" PRId32 ")\n", __func__, node->key_name, node->value, (int) len, ret); + TEST_ASSERT_MESSAGE(len == strlen(node->value), cfstore_close_utest_msg_g); + + printf("Created KV successfully (key_name=\"%s\", value=\"%s\")\n", node->key_name, node->value); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_close_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_close_utest_msg_g); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("CLOSE_test_00", cfstore_close_test_00), + Case("CLOSE_test_01_start", cfstore_utest_default_start), + Case("CLOSE_test_01_end", cfstore_close_test_01_end) +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/create/create.cpp b/TESTS/cfstore/create/create.cpp new file mode 100644 index 0000000000..03fb8603e0 --- /dev/null +++ b/TESTS/cfstore/create/create.cpp @@ -0,0 +1,760 @@ +/** @file create.cpp + * + * 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. + * + * Test cases to create KVs in the CFSTORE using the drv->Create() API call. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_create_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("CREATE_test_00", cfstore_create_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + + +#else + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include "cfstore_debug.h" +#include "cfstore_test.h" +//#include +#include +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +#ifdef CFSTORE_DEBUG +#define CFSTORE_CREATE_GREENTEA_TIMEOUT_S 360 +#else +#define CFSTORE_CREATE_GREENTEA_TIMEOUT_S 60 +#endif + +static char cfstore_create_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.create.box1"); +UVISOR_BOX_CONFIG(cfstore_create_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + +#define CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_01 { "Lefkada.Vathi.Meganisi.Atokos.Vathi.Ithaki.PeriPigathi.AgiosAndreas.Sami.Kefalonia.AgiaEffimia.AgiaSofia.Fiskardo.Frikes.Kioni.Meganissi.Lefkada", "Penelope"} +#define CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_02 { "Iolcus.Lemnos.Salmydessus.Cyzicus.Cios.Berbryces.Symplegadese.IsleAres.Colchis.Anenea.Sirens.Scylia.Charybdis.Phaeacia.Triton.Iolcus", "Medea"} +#define CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_03 { "338.Chaeronea.336.Macedonia.334.Granicus.333.Issus.332.Tyre.331.Gaugamela.330.Persepolis.Philotas.Parmenion.329.Bactria.Oxus.Samarkand.328.Cleitus.327.Roxane.326.Hydaspes.Bucephalus.324.Hephaestion.323.AlexanderDies", "TheGreat"} +#define CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_01 { "0123456789abcdef0123456", "abcdefghijklmnopqrstuvwxyz"} +#define CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_02 { "0123456789abcdef0123456", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"} +#define CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_03 { "0123456789abcdef0123456", "nopqrstuvwxyz"} +#define CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_01 { "Time.Will.Explain.It.All", "Aegeus"} +#define CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_02 { "Cleverness.Is.Not.Wisdom", "Bacchae"} +#define CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_03 { "Talk.Sense.To.A.Fool.And.He.Calls.You.Foolish", "Bacchae"} +#define CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_04 { "There.is.in.the.worst.of.fortune.the.best.of.chances.for.a.happy.change", "Iphigenia.in.Tauris"} + +static cfstore_kv_data_t cfstore_create_test_01_data[] = { + CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_03, + { NULL, NULL}, +}; + +/* table 1: to initialise cfstore with CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_01 */ +static cfstore_kv_data_t cfstore_create_test_01_data_step_01[] = { + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_04, + { NULL, NULL}, +}; + +/* table 2: to CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_01 grown to CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_02 */ +static cfstore_kv_data_t cfstore_create_test_01_data_step_02[] = { + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_04, + { NULL, NULL}, +}; + +/* table 3: to CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_02 shrunk to CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_03 */ +static cfstore_kv_data_t cfstore_create_test_01_data_step_03[] = { + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_04, + { NULL, NULL}, +}; + +/* table 3: CFSTORE_CREATE_TEST_01_TABLE_MID_ENTRY_03 deleted */ +static cfstore_kv_data_t cfstore_create_test_01_data_step_04[] = { + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_HEAD_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_01, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_02, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_03, + CFSTORE_CREATE_TEST_01_TABLE_TAIL_ENTRY_04, + { NULL, NULL}, +}; + +/* support functions */ + +/* @brief support function for generating value blob data */ +static int32_t cfstore_create_kv_value_gen(char* value, const size_t len) +{ + size_t i = 0; + size_t cpy_size = 0; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: value pointer is null.\n", __func__); + TEST_ASSERT_MESSAGE(value != NULL, cfstore_create_utest_msg_g); + + while(i < len) + { + cpy_size = len - i > CFSTORE_TEST_BYTE_DATA_TABLE_SIZE ? CFSTORE_TEST_BYTE_DATA_TABLE_SIZE : len - i; + memcpy(value + i, cfstore_test_byte_data_table, cpy_size); + i += cpy_size; + } + return ARM_DRIVER_OK; +} + + +static char* CFSTORE_CREATE_KV_CREATE_NO_TAG = NULL; + +/* @brief + * support function to create a KV + * - a kv name is generated with the length name_len + * - a kv value blob is generated with the length value_len + * + * @param name_len the length of the kv_name + * @param name_tag tag to append to name, intended to enable large number of unique strings + * @param value_buf buffer to use for storing the generated value data + * @param value_len the length of the value to generate + */ +static int32_t cfstore_create_kv_create(size_t name_len, char* name_tag, char* value_buf, size_t value_len) +{ + int32_t ret = ARM_DRIVER_OK; + size_t name_len_ex = name_len; + char kv_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; /* extra char for terminating null */ + ARM_CFSTORE_KEYDESC kdesc; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + memset(kv_name, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + memset(&kdesc, 0, sizeof(kdesc)); + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + + name_len_ex = name_len; + if(name_tag){ + name_len_ex += strlen(name_tag); + } + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: max supported KV name length for testing exceeded.\n", __func__); + TEST_ASSERT_MESSAGE(name_len_ex < CFSTORE_KEY_NAME_MAX_LENGTH+1, cfstore_create_utest_msg_g); + + ret = cfstore_test_kv_name_gen(kv_name, name_len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate kv_name.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK , cfstore_create_utest_msg_g); + + /* append name tag */ + if(name_tag){ + strncat(kv_name, name_tag, CFSTORE_KEY_NAME_MAX_LENGTH); + } + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: kv_name is not the correct length (name_len_ex=%d, expected=%d).\n", __func__, (int) strlen(kv_name), (int) name_len_ex); + TEST_ASSERT_MESSAGE(strlen(kv_name) == name_len_ex, cfstore_create_utest_msg_g); + + ret = cfstore_create_kv_value_gen(value_buf, value_len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate kv_name.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK , cfstore_create_utest_msg_g); + + ret = cfstore_test_create(kv_name, value_buf, &value_len, &kdesc); + + if(ret == ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY){ + CFSTORE_ERRLOG("%s: Error: out of memory\n", __func__); + return ret; + } + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create KV in store for kv_name_good(ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + + return ret; +} + + +/* @brief cfstore_create_test_01() support function change the size of a value blob in the cfstore */ +static int32_t cfstore_create_test_KV_change(const cfstore_kv_data_t* old_node, const cfstore_kv_data_t* new_node ) +{ + int32_t ret = ARM_DRIVER_ERROR; + size_t len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + ARM_CFSTORE_KEYDESC kdesc; + + CFSTORE_DBGLOG("%s:entered\n", __func__); + memset(&flags, 0, sizeof(flags)); + memset(&kdesc, 0, sizeof(kdesc)); + + /* check node key_names are identical */ + if(strncmp(old_node->key_name, new_node->key_name, strlen(old_node->key_name)) != 0){ + CFSTORE_ERRLOG("%s:old and new entries so not have the same key_name (old_key_name=%s, new_key_name=%s).\n", __func__, old_node->key_name, new_node->key_name); + return ret; + } + len = strlen(new_node->value); + /* supply NULL key descriptor to open a pre-existing key for increasing the blob size */ + ret = drv->Create(new_node->key_name, len, NULL, hkey); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to change size of KV (key_name=%s)(ret=%d).\n", __func__, new_node->key_name, (int) ret); + goto out1; + } + len = strlen(new_node->value); + ret = drv->Write(hkey, new_node->value, &len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to write KV (key_name=%s)(ret=%d).\n", __func__, new_node->key_name, (int) ret); + goto out2; + } + if(len != strlen(new_node->value)){ + CFSTORE_DBGLOG("%s:Failed wrote (%d) rather than the correct number of bytes (%d).\n", __func__, (int) len, (int) strlen(cfstore_create_test_01_data[1].value)); + goto out2; + } +out2: + drv->Close(hkey); +out1: + return ret; +} + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_create_test_00(const size_t call_count) +{ + (void) call_count; + CFSTORE_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", cfstore_driver.GetCapabilities().asynchronous_ops); + return CaseNext; +} + + +/** + * @brief test to change the value blob size of pre-existing key + * + * @notes + * + * The test does the following: + * - creates a cfstore with ~10 entries. + * - for a mid-cfstore entry, grow the value blob size + * - check all the cfstore entries can be read correctly and their + * data agrees with the data supplied upon creation. + * - grow the mid-entry value blob size to be ~double the initial size. + * - check all the cfstore entries can be read correctly and their + * data agrees with the data supplied upon creation. + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_create_test_01_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_FMODE flags; + cfstore_kv_data_t* node = NULL; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(&flags, 0, sizeof(flags)); + + ret = cfstore_test_create_table(cfstore_create_test_01_data_step_01); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to add cfstore_create_test_01_data_head (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + + /* find cfstore_create_test_01_data[0] and grow the KV MID_ENTRY_01 to MID_ENTRY_02 */ + ret = cfstore_create_test_KV_change(&cfstore_create_test_01_data[0], &cfstore_create_test_01_data[1]); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to increase size of KV (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + + /* Now check that the KVs are all present and correct */ + node = cfstore_create_test_01_data_step_02; + while(node->key_name != NULL) + { + ret = cfstore_test_check_node_correct(node); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:node (key_name=\"%s\", value=\"%s\") not correct in cfstore\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + node++; + } + CFSTORE_LOG("KV successfully increased in size and other KVs remained unchanged.%s", "\n"); + /* Shrink the KV from KV MID_ENTRY_02 to MID_ENTRY_03 */ + ret = cfstore_create_test_KV_change(&cfstore_create_test_01_data[1], &cfstore_create_test_01_data[2]); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to decrease size of KV (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + + /* Now check that the KVs are all present and correct */ + node = cfstore_create_test_01_data_step_03; + while(node->key_name != NULL) + { + ret = cfstore_test_check_node_correct(node); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:node (key_name=\"%s\", value=\"%s\") not correct in cfstore\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + node++; + } + CFSTORE_LOG("KV successfully decreased in size and other KVs remained unchanged.%s", "\n"); + + /* Delete the KV */ + ret = cfstore_test_delete(cfstore_create_test_01_data[2].key_name); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:failed to delete node(key_name=\"%s\")\n", __func__, node->key_name); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + + /* Now check that the KVs are all present and correct */ + node = cfstore_create_test_01_data_step_04; + while(node->key_name != NULL) + { + ret = cfstore_test_check_node_correct(node); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:node (key_name=\"%s\", value=\"%s\") not correct in cfstore\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + node++; + } + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + return CaseNext; +} + + +/** + * @brief create the 10 kvs. + * @note + * The amount of data store in the cfstore is as follows: + * - 10 kvs + * - kv name lengths are ~220 => ~ 2200 bytes + * - value blob length is 1x256, 2x256, 3x256, ... 10x256)) = 256(1+2+3+4+..10) = 256*10*11/2 = 14080 + * - kv overhead = 8bytes/kv = 8 * 10 = 80bytes + * - total = (220*10)+256*10*11/2 10*8 = 143800 bytes + * + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static int32_t cfstore_create_test_02_core(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + uint32_t i = 0; + uint32_t bytes_stored = 0; + const uint32_t max_num_kvs_create = 10; + const size_t kv_name_min_len = CFSTORE_KEY_NAME_MAX_LENGTH - max_num_kvs_create; + const size_t kv_value_min_len = CFSTORE_TEST_BYTE_DATA_TABLE_SIZE; + const size_t max_value_buf_size = kv_value_min_len * (max_num_kvs_create +1); + char value_buf[max_value_buf_size]; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(value_buf, 0, max_value_buf_size); + + for(i = 0; i < max_num_kvs_create; i++) + { + memset(value_buf, 0, max_value_buf_size); + ret = cfstore_create_kv_create(kv_name_min_len +i, CFSTORE_CREATE_KV_CREATE_NO_TAG, value_buf, kv_value_min_len * (i+1)); + bytes_stored += kv_name_min_len + i; /* kv_name */ + bytes_stored += kv_value_min_len * (i+1); /* kv value blob */ + bytes_stored += 8; /* kv overhead */ + if(ret == ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY){ + CFSTORE_ERRLOG("Out of memory on %" PRId32 "-th KV, trying to allocate memory totalling %" PRIu32 ".\n", i, bytes_stored); + break; + } + CFSTORE_DBGLOG("Successfully stored %" PRId32 "-th KV bytes, totalling %" PRIu32 ".\n", i, bytes_stored); + } + ret = cfstore_test_delete_all(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to delete_all() attributes to clean up after test (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + return ret; +} + +static control_t cfstore_create_test_02_end(const size_t call_count) +{ + int32_t ret; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + ret = cfstore_create_test_02_core(call_count); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: something went wrong (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + return CaseNext; +} + + +static control_t cfstore_create_test_03_end(const size_t call_count) +{ + int32_t i = 0; + int32_t ret; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + for(i = 0; i < 100; i++) + { + ret = cfstore_create_test_02_core(call_count); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: something went wrong (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + CFSTORE_LOG("Successfully completed create/destroy loop %" PRId32 ".\n", i); + } + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + return CaseNext; +} + + +/** + * @brief create the 100 kvs to make the device run out of memory + * @note + * The amount of data store in the cfstore is as follows: + * - total = (220*100)+256*100*101/2 100*8 = 1315600 = 1.315x10^6 + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_create_test_04_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + uint32_t i = 0; + uint32_t bytes_stored = 0; + const uint32_t max_num_kvs_create = 100; + const size_t kv_name_min_len = CFSTORE_KEY_NAME_MAX_LENGTH - max_num_kvs_create; + const size_t kv_value_min_len = CFSTORE_TEST_BYTE_DATA_TABLE_SIZE; + const size_t max_value_buf_size = kv_value_min_len/8 * (max_num_kvs_create +1); + char* value_buf = NULL; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + value_buf = (char*) malloc(max_value_buf_size); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: out of memory.\n", __func__); + TEST_ASSERT_MESSAGE(value_buf != NULL, cfstore_create_utest_msg_g); + + for(i = 0; i < max_num_kvs_create; i++) + { + memset(value_buf, 0, max_value_buf_size); + ret = cfstore_create_kv_create(kv_name_min_len +i, CFSTORE_CREATE_KV_CREATE_NO_TAG, value_buf, kv_value_min_len/8 * (i+1)); + bytes_stored += kv_name_min_len + i; /* kv_name */ + bytes_stored += kv_value_min_len/8 * (i+1); /* kv value blob */ + bytes_stored += 8; /* kv overhead */ + if(ret == ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY){ + CFSTORE_LOG("Out of memory on %" PRId32 "-th KV, trying to allocate memory totalling %" PRIu32 ".\n", i, bytes_stored); + break; + } + CFSTORE_LOG("Successfully stored %" PRId32 "-th KV bytes, totalling %" PRIu32 ".\n", i, bytes_stored); + } + ret = cfstore_test_delete_all(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to delete_all() attributes to clean up after test (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + free(value_buf); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + return CaseNext; +} + +/** + * @brief create the 500 kvs. + * @note + * The amount of data store in the cfstore is as follows: + * - total = (220*500)+(256/64)*500*501/2 500*8 = 8236000 = 8.236M + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_create_test_05_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + uint32_t i = 0; + uint32_t bytes_stored = 0; + const uint32_t max_num_kvs_create = 200; + const size_t kv_name_tag_len = 3; + const size_t kv_name_min_len = 10; + const size_t kv_value_min_len = CFSTORE_TEST_BYTE_DATA_TABLE_SIZE; + const size_t max_value_buf_size = kv_value_min_len/64 * (max_num_kvs_create+1); + char kv_name_tag_buf[kv_name_tag_len+1]; + char* value_buf = NULL; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + value_buf = (char*) malloc(max_value_buf_size); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: out of memory.\n", __func__); + TEST_ASSERT_MESSAGE(value_buf != NULL, cfstore_create_utest_msg_g); + + for(i = 0; i < max_num_kvs_create; i++) + { + memset(value_buf, 0, max_value_buf_size); + snprintf(kv_name_tag_buf, kv_name_tag_len+1, "%0d", (int) i); + ret = cfstore_create_kv_create(kv_name_min_len, kv_name_tag_buf, value_buf, kv_value_min_len/64 * (i+1)); + bytes_stored += kv_name_min_len + i + strlen(kv_name_tag_buf); /* kv_name */ + bytes_stored += kv_value_min_len/64 * (i+1); /* kv value blob */ + bytes_stored += 8; /* kv overhead */ + if(ret == ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY){ + CFSTORE_LOG("Out of memory on %" PRId32 "-th KV, trying to allocate memory totalling %" PRIu32 ".\n", i, bytes_stored); + break; + } + CFSTORE_LOG("Successfully stored %" PRId32 "-th KV bytes, totalling %" PRIu32 ".\n", i, bytes_stored); + } + ret = cfstore_test_delete_all(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to delete_all() attributes to clean up after test.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + free(value_buf); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + return CaseNext; +} + + +typedef struct cfstore_create_key_name_validate_t { + const char* key_name; + uint32_t f_allowed : 1; +} cfstore_create_key_name_validate_t; + +cfstore_create_key_name_validate_t cfstore_create_test_06_data[] = { + /* ruler for measuring text strings */ + /* 1 1 1 1 1 1 1 1 1 1 2 2 2 */ + /* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 */ + { "", false}, + { "abc.{1}.efg", true }, + { "abc.{1.efg", false }, + { "abc.1}.efg", false }, + { "abc.{{1}}.efg", false }, + { "abc.{}.efg", true }, + { "abc.}1{.efg", false }, + { ".piety.demands.us.to.honour.truth.above.our.friends", false }, + { "basement.medicine.pavement.government.trenchcoat.off.cough.off.kid.did.when.again.alleyway.friend.cap.pen.dollarbills.ten.foot.soot.put.but.anyway.say.May.DA.kid.did.toes.bows.those.hose.nose.clothes.man.blows.{100000000}", false }, + { NULL, false}, +}; + + +/** + * @brief function to test whether a key name can be created or not + * + * @param key_name + * name of the key to create in the store + * @param should_create + * if true, then create KV should succeed, otherwise should fail. + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +bool cfstore_create_key_name_validate(const char *key_name, bool should_create) +{ + bool bret = false; + char* test_data = (char*) "test_data"; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_KEYDESC kdesc; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + memset(&kdesc, 0, sizeof(kdesc)); + + /* create */ + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(test_data); + ret = cfstore_test_create((const char*) key_name, test_data, &len, &kdesc); + /* dont not use any functions that require finding the created item as they may not work, + * depending on the construction of the test key_name & match strings */ + if(should_create == true) + { + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to create kv (key_name=%s.\r\n", __func__, key_name); + return bret; + } + CFSTORE_DBGLOG("%s:Success: Create() behaved as expected.\r\n", __func__); + /* delete using the actual name */ + ret = cfstore_test_delete((const char*) key_name); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to delete kv (key_name=%s)(ret=%d).\r\n", __func__, key_name, (int)ret); + } + bret = true; + } + else + { + if(ret >= ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: created kv (key_name=%s) when Create() should have failed.\r\n", __func__, key_name); + return bret; + } + /* the operation failed which was the expected result hence return true*/ + bret = true; + } + return bret; +} + +/** + * @brief check that key names with non-matching braces etc do no get created. + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_create_test_06_end(const size_t call_count) +{ + bool ret = false; + int32_t ret32 = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + cfstore_create_key_name_validate_t* node = cfstore_create_test_06_data; + + (void) call_count; + + while(node->key_name != NULL) + { + ret = cfstore_create_key_name_validate(node->key_name, node->f_allowed); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: test failed (ret=%d, key_name=%s, f_allowed=%d)\n", __func__, (int) ret, node->key_name, node->f_allowed); + TEST_ASSERT_MESSAGE(ret == true, cfstore_create_utest_msg_g); + node++; + } + + ret32 = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_create_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_create_utest_msg_g); + return CaseNext; +} + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(CFSTORE_CREATE_GREENTEA_TIMEOUT_S, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("CREATE_test_00", cfstore_create_test_00), + Case("CREATE_test_01_start", cfstore_utest_default_start), + Case("CREATE_test_01_end", cfstore_create_test_01_end), + Case("CREATE_test_02_start", cfstore_utest_default_start), + Case("CREATE_test_02_end", cfstore_create_test_02_end), + Case("CREATE_test_03_start", cfstore_utest_default_start), + Case("CREATE_test_03_end", cfstore_create_test_03_end), + Case("CREATE_test_04_start", cfstore_utest_default_start), + Case("CREATE_test_04_end", cfstore_create_test_04_end), + Case("CREATE_test_05_start", cfstore_utest_default_start), + Case("CREATE_test_05_end", cfstore_create_test_05_end), + Case("CREATE_test_06_start", cfstore_utest_default_start), + Case("CREATE_test_06_end", cfstore_create_test_06_end), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/example1/example1.cpp b/TESTS/cfstore/example1/example1.cpp new file mode 100644 index 0000000000..6792035f55 --- /dev/null +++ b/TESTS/cfstore/example1/example1.cpp @@ -0,0 +1,868 @@ +/** @file example1.cpp + * + * 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. + * + * Test cases to add and delete KVs in the CFSTORE. + */ + + +/* EXAMPLE1 Notes + * ============== + * + * The example test does the following CFSTORE operations: + * - initialises + * - creates a key-value pair (KV). + * - writes the data for the KV + * - closes KV. + * - flushes the KV to flash + * - opens KV for reading. + * - reads KV and checks the value blob was the same as previously written. + * - closes KV. + * - finds a KV (there is only 1 to find). + * - for the KV returned, get the key name. + * - for the KV returned, get the value length. + * - for the KV returned, delete the KV. + * - find another KV (which fails as there are no more keys to find). + * - flushes the updated state to flash to store the removal of the deleted KV. + * - uninitialises + * - stops + * + * This test is coded so as to work in the following modes: + * - flash sync mode i.e. with caps.asynchronous_ops == false + * - flash async mode i.e. with caps.asynchronous_ops == true + * The dual async/sync mode support with the same code is more complicated + * than if the implementation just supported sync mode for example. However, + * it has the benefit of being more versatile. + * + * The test leaves the flash in the same state as at the beginning of the test so + * it can be run a second time on the device without flashing, and the test should + * still work. + * + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_example1_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE1_test_00", cfstore_example1_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + + +#else + + + +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif +#ifndef YOTTA_CONFIGURATION_STORE_EXAMPLE1_VERSION_STRING +/* when built as Configuration-Store example, include greentea support otherwise omit */ +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#else // YOTTA_CONFIGURATION_STORE_EXAMPLE1_VERSION_STRING +// map utest types for building as stand alone example +#define control_t void +#define CaseNext +#endif // YOTTA_CONFIGURATION_STORE_EXAMPLE1_VERSION_STRING + +#include +#include +#include "cfstore_config.h" +#include +#ifdef YOTTA_CFG_CONFIG_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CONFIG_UVISOR */ + + +#ifndef YOTTA_CONFIGURATION_STORE_EXAMPLE1_VERSION_STRING +using namespace utest::v1; +#endif + + +#define CFSTORE_EX1_TEST_ASSERT(Expr) if (!(Expr)) { printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} +#define CFSTORE_EX1_TEST_ASSERT_EQUAL(expected, actual) if ((expected) != (actual)) {printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} +#define CFSTORE_EX1_TEST_ASSERT_NOT_EQUAL(expected, actual) if ((expected) == (actual)) {printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} + +#define CFSTORE_EX1_TEST_ASSERT_MSG(Expr, _fmt, ...) \ + do \ + { \ + if (!(Expr)) \ + { \ + printf(_fmt, __VA_ARGS__); \ + while (1) ; \ + } \ + }while(0); + +#define CFSTORE_EX1_LOG(_fmt, ...) \ + do \ + { \ + printf(_fmt, __VA_ARGS__); \ + }while(0); + + +const char* cfstore_ex_opcode_str[] = +{ + "UNDEFINED", + "CFSTORE_OPCODE_CLOSE", + "CFSTORE_OPCODE_CREATE", + "CFSTORE_OPCODE_DELETE", + "CFSTORE_OPCODE_FIND", + "CFSTORE_OPCODE_FLUSH", + "CFSTORE_OPCODE_GET_KEY_NAME", + "CFSTORE_OPCODE_GET_STATUS", + "CFSTORE_OPCODE_GET_VALUE_LEN", + "CFSTORE_OPCODE_INITIALIZE", + "CFSTORE_OPCODE_OPEN", + "CFSTORE_OPCODE_POWER_CONTROL", + "CFSTORE_OPCODE_READ", + "CFSTORE_OPCODE_RSEEK", + "CFSTORE_OPCODE_UNINITIALIZE", + "CFSTORE_OPCODE_WRITE", + "CFSTORE_OPCODE_MAX" +}; + +bool cfstore_example1_done = false; +const char* cfstore_ex_kv_name = "basement.medicine.pavement.government.trenchcoat.off.cough.off.kid.did.when.again.alleyway.friend.cap.pen.dollarbills.ten.foot.soot.put.but.anyway.say.May.DA.kid.did.toes.bows.those.hose.nose.clothes.man.blows.well.well"; +const char* cfstore_ex_kv_value = "TheRollingStone"; +#define CFSTORE_EX1_RSEEK_OFFSET 10 /* offset to S of Stone */ + +typedef enum cfstore_ex_state_t { + CFSTORE_EX_STATE_INITIALIZING = 1, + CFSTORE_EX_STATE_INIT_DONE, + CFSTORE_EX_STATE_CREATING, + CFSTORE_EX_STATE_CREATE_DONE, + CFSTORE_EX_STATE_WRITING, + CFSTORE_EX_STATE_WRITE_DONE, + CFSTORE_EX_STATE_CLOSING1, + CFSTORE_EX_STATE_CLOSE_DONE1, + CFSTORE_EX_STATE_FLUSHING1, + CFSTORE_EX_STATE_FLUSH_DONE1, + CFSTORE_EX_STATE_OPENING, + CFSTORE_EX_STATE_OPEN_DONE, + CFSTORE_EX_STATE_READING1, + CFSTORE_EX_STATE_READ_DONE1, + CFSTORE_EX_STATE_RSEEKING, + CFSTORE_EX_STATE_RSEEK_DONE, + CFSTORE_EX_STATE_READING2, + CFSTORE_EX_STATE_READ_DONE2, + CFSTORE_EX_STATE_CLOSING2, + CFSTORE_EX_STATE_CLOSE_DONE2, + CFSTORE_EX_STATE_FINDING1, + CFSTORE_EX_STATE_FIND_DONE1, + CFSTORE_EX_STATE_GETTING_KEY_NAME, + CFSTORE_EX_STATE_GET_KEY_NAME_DONE, + CFSTORE_EX_STATE_GETTING_VALUE_LEN, + CFSTORE_EX_STATE_GET_VALUE_LEN_DONE, + CFSTORE_EX_STATE_DELETING, + CFSTORE_EX_STATE_DELETE_DONE, + CFSTORE_EX_STATE_FINDING2, + CFSTORE_EX_STATE_FIND_DONE2, + CFSTORE_EX_STATE_FLUSHING2, + CFSTORE_EX_STATE_FLUSH_DONE2, + CFSTORE_EX_STATE_UNINITIALIZING, + CFSTORE_EX_STATE_UNINIT_DONE +} cfstore_ex_state_t; + +typedef struct cfstore_example1_ctx_t +{ + ARM_CFSTORE_CAPABILITIES caps;; + uint8_t hkey[CFSTORE_HANDLE_BUFSIZE]; + uint8_t hkey_next_buf[CFSTORE_HANDLE_BUFSIZE]; + uint8_t hkey_prev_buf[CFSTORE_HANDLE_BUFSIZE]; + ARM_CFSTORE_HANDLE hkey_next; + ARM_CFSTORE_HANDLE hkey_prev; + cfstore_ex_state_t state; + ARM_CFSTORE_SIZE len; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_FMODE flags; + char value[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + /* callback attributes*/ + int32_t callback_status; + ARM_CFSTORE_HANDLE callback_handle; +} cfstore_example1_ctx_t; + +static cfstore_example1_ctx_t cfstore_example1_ctx_g; + +extern ARM_CFSTORE_DRIVER cfstore_driver; +ARM_CFSTORE_DRIVER *cfstore_drv = &cfstore_driver; + +/* forward declarations */ +static void cfstore_ex_fms_update(cfstore_example1_ctx_t* ctx); + + +static void cfstore_ex_callback(int32_t status, ARM_CFSTORE_OPCODE cmd_code, void *client_context, ARM_CFSTORE_HANDLE handle) +{ + (void) handle; + + cfstore_example1_ctx_t* ctx = (cfstore_example1_ctx_t*) client_context; + + /* CFSTORE_EX1_LOG("%s:entered: status=%d, cmd_code=%d (%s) handle=%p\r\n", __func__, (int) status, (int) cmd_code, cfstore_ex_opcode_str[cmd_code], handle); */ + switch(cmd_code) + { + case CFSTORE_OPCODE_INITIALIZE: + ctx->state = CFSTORE_EX_STATE_INIT_DONE; + break; + case CFSTORE_OPCODE_CREATE: + ctx->state = CFSTORE_EX_STATE_CREATE_DONE; + break; + case CFSTORE_OPCODE_WRITE: + ctx->state = CFSTORE_EX_STATE_WRITE_DONE; + break; + case CFSTORE_OPCODE_CLOSE: + switch(ctx->state) + { + case(CFSTORE_EX_STATE_CLOSING1): + ctx->state = CFSTORE_EX_STATE_CLOSE_DONE1; + break; + case(CFSTORE_EX_STATE_CLOSING2): + ctx->state = CFSTORE_EX_STATE_CLOSE_DONE2; + break; + default: + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown Close() error (line=%u)\r\n", __func__, __LINE__); + break; + } + break; + case CFSTORE_OPCODE_FLUSH: + switch(ctx->state) + { + case(CFSTORE_EX_STATE_FLUSHING1): + ctx->state = CFSTORE_EX_STATE_FLUSH_DONE1; + break; + case(CFSTORE_EX_STATE_FLUSHING2): + ctx->state = CFSTORE_EX_STATE_FLUSH_DONE2; + break; + default: + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown Find() error (line=%u)\r\n", __func__, __LINE__); + break; + } + break; + case CFSTORE_OPCODE_OPEN: + ctx->state = CFSTORE_EX_STATE_OPEN_DONE; + break; + case CFSTORE_OPCODE_FIND: + switch(ctx->state) + { + case(CFSTORE_EX_STATE_FINDING1): + ctx->state = CFSTORE_EX_STATE_FIND_DONE1; + break; + case(CFSTORE_EX_STATE_FINDING2): + ctx->state = CFSTORE_EX_STATE_FIND_DONE2; + break; + default: + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown Find() error (line=%u)\r\n", __func__, __LINE__); + break; + } + break; + case CFSTORE_OPCODE_GET_KEY_NAME: + ctx->state = CFSTORE_EX_STATE_GET_KEY_NAME_DONE; + break; + case CFSTORE_OPCODE_GET_VALUE_LEN: + ctx->state = CFSTORE_EX_STATE_GET_VALUE_LEN_DONE; + break; + case CFSTORE_OPCODE_READ: + switch(ctx->state) + { + case(CFSTORE_EX_STATE_READING1): + ctx->state = CFSTORE_EX_STATE_READ_DONE1; + break; + case(CFSTORE_EX_STATE_READING2): + ctx->state = CFSTORE_EX_STATE_READ_DONE2; + break; + default: + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown Read() error (line=%u)\r\n", __func__, __LINE__); + break; + } + break; + case CFSTORE_OPCODE_RSEEK: + ctx->state = CFSTORE_EX_STATE_RSEEK_DONE; + break; + case CFSTORE_OPCODE_DELETE: + ctx->state = CFSTORE_EX_STATE_DELETE_DONE; + break; + case CFSTORE_OPCODE_UNINITIALIZE: + ctx->state = CFSTORE_EX_STATE_UNINIT_DONE; + break; + case CFSTORE_OPCODE_GET_STATUS: + case CFSTORE_OPCODE_POWER_CONTROL: + default: + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: received asynchronous notification for opcode=%d (%s)\r\b", __func__, cmd_code, cmd_code < CFSTORE_OPCODE_MAX ? cfstore_ex_opcode_str[cmd_code] : "unknown"); + break; + } + ctx->callback_status = status; + ctx->callback_handle = handle; + cfstore_ex_fms_update(ctx); + return; +} + +static void cfstore_ex_fms_update(cfstore_example1_ctx_t* ctx) +{ + int32_t ret; + + switch (ctx->state) + { + case CFSTORE_EX_STATE_INITIALIZING: + CFSTORE_EX1_LOG("INITIALIZING%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_INITIALIZE can be invoked before Initialize() has returned + ret = cfstore_drv->Initialize(cfstore_ex_callback, ctx); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Initialize() should return ret >= 0 for async/synch modes(ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_INIT_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_INIT_DONE: + CFSTORE_EX1_LOG("INIT_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Initialize() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == NULL, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_INITIALIZE) received non-NULL handle (%p)\r\n", __func__, ctx->callback_handle); + ctx->state = CFSTORE_EX_STATE_CREATING; + // intentional fall-through + + case CFSTORE_EX_STATE_CREATING: + CFSTORE_EX1_LOG("CREATING%s", "\r\n"); + memset(&ctx->kdesc, 0, sizeof(ARM_CFSTORE_KEYDESC)); + ctx->kdesc.drl = ARM_RETENTION_NVM; + ctx->len = strlen(cfstore_ex_kv_value); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_CREATE can be invoked before Create() has returned + ret = cfstore_drv->Create(cfstore_ex_kv_name, ctx->len, &ctx->kdesc, ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Create() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_CREATE_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_CREATE_DONE: + CFSTORE_EX1_LOG("CREATE_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Create() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_CREATE) received handle (%p) is not the hkey supplied to Create()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey); + ctx->state = CFSTORE_EX_STATE_WRITING; + // intentional fall-through + + case CFSTORE_EX_STATE_WRITING: + CFSTORE_EX1_LOG("WRITING%s", "\r\n"); + ctx->len = strlen(cfstore_ex_kv_value); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_WRITE can be invoked before Write() has returned + ret = cfstore_drv->Write(ctx->hkey, cfstore_ex_kv_value, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Write() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_WRITE_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_WRITE_DONE: + CFSTORE_EX1_LOG("WRITE_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Write() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == (int32_t) strlen(cfstore_ex_kv_value), "%s:Error: Write() number of octets written (i.e. completion status (%ld)) != strlen(ctx->value)(%ld)\r\n", __func__, ctx->callback_status, (int32_t) strlen(cfstore_ex_kv_value)); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == (int32_t) ctx->len, "%s:Error: Write() number of octets written (i.e. completion status (%ld)) != updated value of len parameter (%ld)\r\n", __func__, ctx->callback_status, (int32_t) ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_WRITE) received handle (%p) is not the hkey supplied to Write()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey); + ctx->state = CFSTORE_EX_STATE_CLOSING1; + // intentional fall-through + + case CFSTORE_EX_STATE_CLOSING1: + CFSTORE_EX1_LOG("CLOSING1%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_CLOSE can be invoked before Close() has returned + ret = cfstore_drv->Close(ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_CLOSE_DONE1; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_CLOSE_DONE1: + CFSTORE_EX1_LOG("CLOSE_DONE1%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Close() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == NULL, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_CLOSE) received non-NULL handle (%p)\r\n", __func__, ctx->callback_handle); + ctx->state = CFSTORE_EX_STATE_FLUSHING1; + // intentional fall-through + + case CFSTORE_EX_STATE_FLUSHING1: + CFSTORE_EX1_LOG("FLUSHING1%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_FLUSH can be invoked before Flush() has returned + ret = cfstore_drv->Flush(); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Flush() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_FLUSH_DONE1; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_FLUSH_DONE1: + CFSTORE_EX1_LOG("FLUSH_DONE1%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Flush() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == NULL, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_FLUSH) received non-NULL handle (%p)\r\n", __func__, ctx->callback_handle); + ctx->state = CFSTORE_EX_STATE_OPENING; + // intentional fall-through + + case CFSTORE_EX_STATE_OPENING: + CFSTORE_EX1_LOG("OPENING%s", "\r\n"); + memset(&ctx->flags, 0, sizeof(ctx->flags)); + memset(&ctx->hkey, 0, CFSTORE_HANDLE_BUFSIZE); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_OPEN can be invoked before Open() has returned + ret = cfstore_drv->Open(cfstore_ex_kv_name, ctx->flags, ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Open() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_OPEN_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_OPEN_DONE: + CFSTORE_EX1_LOG("OPEN_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Open() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_OPEN) received handle (%p) is not the hkey supplied to Open()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey); + ctx->state = CFSTORE_EX_STATE_READING1; + // intentional fall-through + + case CFSTORE_EX_STATE_READING1: + CFSTORE_EX1_LOG("READING1%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(ctx->value, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_READ can be invoked before Read() has returned + ret = cfstore_drv->Read(ctx->hkey, ctx->value, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Read() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_READ_DONE1; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_READ_DONE1: + CFSTORE_EX1_LOG("READ_DONE1%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Read() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == (int32_t) strlen(cfstore_ex_kv_value), "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != strlen(ctx->value)(%ld)\r\n", __func__, ctx->callback_status, (int32_t) strlen(cfstore_ex_kv_value)); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == (int32_t) ctx->len, "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != updated value of len parameter (%ld)\r\n", __func__, ctx->callback_status, (int32_t) ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_READ) received handle (%p) is not the hkey supplied to Read()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(strncmp(ctx->value, cfstore_ex_kv_value, strlen(cfstore_ex_kv_value)) == 0, "%s:Error: the read value (%s) is not as expected (%s)\r\n", __func__, ctx->value, cfstore_ex_kv_value); + ctx->state = CFSTORE_EX_STATE_RSEEKING; + // intentional fall-through + + case CFSTORE_EX_STATE_RSEEKING: + CFSTORE_EX1_LOG("RSEEKING%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_READ can be invoked before Read() has returned + ret = cfstore_drv->Rseek(ctx->hkey, CFSTORE_EX1_RSEEK_OFFSET); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Rseek() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_RSEEK_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_RSEEK_DONE: + CFSTORE_EX1_LOG("RSEEK_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Read() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_RSEEK) received handle (%p) is not the hkey supplied to Read()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey); + ctx->state = CFSTORE_EX_STATE_READING2; + // intentional fall-through + + case CFSTORE_EX_STATE_READING2: + CFSTORE_EX1_LOG("READING2%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(ctx->value, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_READ can be invoked before Read() has returned + ret = cfstore_drv->Read(ctx->hkey, ctx->value, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Read() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_READ_DONE2; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_READ_DONE2: + CFSTORE_EX1_LOG("READ_DONE2%s", "\r\n"); + CFSTORE_EX1_LOG("%s: value=%s\r\n", __func__, ctx->value); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Read() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == (int32_t) strlen(&cfstore_ex_kv_value[CFSTORE_EX1_RSEEK_OFFSET]), "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != strlen(ctx->value)(%ld)\r\n", __func__, ctx->callback_status, (int32_t) strlen(&cfstore_ex_kv_value[CFSTORE_EX1_RSEEK_OFFSET])); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == (int32_t) ctx->len, "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != updated value of len parameter (%ld)\r\n", __func__, ctx->callback_status, (int32_t) ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_READ) received handle (%p) is not the hkey supplied to Read()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(strncmp(ctx->value, &cfstore_ex_kv_value[CFSTORE_EX1_RSEEK_OFFSET], strlen(&cfstore_ex_kv_value[CFSTORE_EX1_RSEEK_OFFSET])) == 0, "%s:Error: the read value (%s) is not as expected (%s)\r\n", __func__, ctx->value, &cfstore_ex_kv_value[CFSTORE_EX1_RSEEK_OFFSET]); + ctx->state = CFSTORE_EX_STATE_CLOSING2; + // intentional fall-through + + case CFSTORE_EX_STATE_CLOSING2: + CFSTORE_EX1_LOG("CLOSING2%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_CLOSE can be invoked before Close() has returned + ret = cfstore_drv->Close(ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_CLOSE_DONE2; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_CLOSE_DONE2: + CFSTORE_EX1_LOG("CLOSE_DONE2%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Close() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == NULL, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_CLOSE) received non-NULL handle (%p)\r\n", __func__, ctx->callback_handle); + ctx->state = CFSTORE_EX_STATE_FINDING1; + // intentional fall-through + + case CFSTORE_EX_STATE_FINDING1: + CFSTORE_EX1_LOG("FINDING1%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_FIND can be invoked before Find() has returned + ret = cfstore_drv->Find("*", ctx->hkey_next, ctx->hkey_prev); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Find() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_FIND_DONE1; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_FIND_DONE1: + CFSTORE_EX1_LOG("FIND_DONE1%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == ARM_DRIVER_OK, "%s:Error: Find() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey_prev, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_FIND) received handle (%p) is not the hkey supplied to Find()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey_prev); + ctx->state = CFSTORE_EX_STATE_GETTING_KEY_NAME; + // intentional fall-through + + case CFSTORE_EX_STATE_GETTING_KEY_NAME: + CFSTORE_EX1_LOG("GETTING_KEY_NAME%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(ctx->value, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_GET_KEY_NAME can be invoked before GetKeyName() has returned + ret = cfstore_drv->GetKeyName(ctx->hkey_prev, ctx->value, (uint8_t*) &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: GetKeyName() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_GET_KEY_NAME_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_GET_KEY_NAME_DONE: + CFSTORE_EX1_LOG("GET_KEY_NAME_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: GetKeyName() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG( ((int32_t) ctx->len == ((int32_t) strlen(cfstore_ex_kv_name)+1)), "%s:Error: GetKeyName() updated value of len parameter (%ld) != strlen(cfstore_ex_kv_name) (%ld) (\r\n", __func__, (int32_t) ctx->len, (int32_t) strlen(cfstore_ex_kv_name)); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey_prev, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_GET_KEY_NAME) received handle (%p) is not the hkey supplied to GetKeyName()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey_prev); + CFSTORE_EX1_TEST_ASSERT_MSG(strncmp(ctx->value, cfstore_ex_kv_name, strlen(cfstore_ex_kv_name)) == 0, "%s:Error: the key name (%s) is not as expected (%s)\r\n", __func__, ctx->value, cfstore_ex_kv_name); + ctx->state = CFSTORE_EX_STATE_GETTING_VALUE_LEN; + // intentional fall-through + + case CFSTORE_EX_STATE_GETTING_VALUE_LEN: + CFSTORE_EX1_LOG("GETTING_VALUE_LEN%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_GET_VALUE_LEN can be invoked before GetValueLen() has returned + ret = cfstore_drv->GetValueLen(ctx->hkey_prev, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: GetValueLen() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_GET_VALUE_LEN_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_GET_VALUE_LEN_DONE: + CFSTORE_EX1_LOG("GET_VALUE_LEN_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: GetValueLen() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG((int32_t) ctx->len == (int32_t) strlen(cfstore_ex_kv_value), "%s:Error: GetValueLen() updated value of len parameter (%ld) != strlen(cfstore_ex_kv_value)(%ld) \r\n", __func__, (int32_t) ctx->len, (int32_t) strlen(cfstore_ex_kv_value)); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == ctx->hkey_prev, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_GET_VALUE_LEN) received handle (%p) is not the hkey supplied to GetValueLen()(%p)\r\n", __func__, ctx->callback_handle, ctx->hkey_prev); + ctx->state = CFSTORE_EX_STATE_DELETING; + // intentional fall-through + + case CFSTORE_EX_STATE_DELETING: + CFSTORE_EX1_LOG("DELETING%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_DELETE can be invoked before Delete() has returned + ret = cfstore_drv->Delete(ctx->callback_handle); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_DELETE_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_DELETE_DONE: + CFSTORE_EX1_LOG("DELETE_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Delete() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == NULL, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_DELETE) received non-NULL handle (%p)\r\n", __func__, ctx->callback_handle); + CFSTORE_HANDLE_SWAP(ctx->hkey_prev, ctx->hkey_next); + ctx->state = CFSTORE_EX_STATE_FINDING2; + // intentional fall-through + + case CFSTORE_EX_STATE_FINDING2: + CFSTORE_EX1_LOG("FINDING2%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_FIND can be invoked before Find() has returned + ret = cfstore_drv->Find("*", ctx->hkey_next, ctx->hkey_prev); + CFSTORE_EX1_TEST_ASSERT_MSG(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, "%s:Error: Find() failed to return expected value of ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND (ret=%ld)\r\n", __func__, ret); + if(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_FIND_DONE2; + break; + } else if(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_FIND_DONE2: + CFSTORE_EX1_LOG("FIND_DONE2%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, "%s:Error: Find() completion should have been ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND (status=%ld)\r\n", __func__, ctx->callback_status); + ctx->state = CFSTORE_EX_STATE_FLUSHING2; + // intentional fall-through + + case CFSTORE_EX_STATE_FLUSHING2: + CFSTORE_EX1_LOG("FLUSHING2%s", "\r\n"); + // note that cfstore_ex_callback() for cmd_code==CFSTORE_OPCODE_FLUSH can be invoked before Flush() has returned + ret = cfstore_drv->Flush(); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error:2: Flush() failed (ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_FLUSH_DONE2; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_FLUSH_DONE2: + CFSTORE_EX1_LOG("FLUSH_DONE2%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_status >= ARM_DRIVER_OK, "%s:Error: Flush() completion failed (status=%ld)\r\n", __func__, ctx->callback_status); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == NULL, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_FLUSH) received non-NULL handle (%p)\r\n", __func__, ctx->callback_handle); + ctx->state = CFSTORE_EX_STATE_UNINITIALIZING; + // intentional fall-through + + case CFSTORE_EX_STATE_UNINITIALIZING: + CFSTORE_EX1_LOG("UNINITIALIZING%s", "\r\n"); + ret = cfstore_drv->Uninitialize(); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Uninitialize() should return ret >= 0 for async/synch modes(ret=%ld)\r\n", __func__, ret); + if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == false) { + ctx->state = CFSTORE_EX_STATE_UNINIT_DONE; + break; + } else if(ret >= ARM_DRIVER_OK && ctx->caps.asynchronous_ops == true) { + // await pending notification of completion. + break; + } + CFSTORE_EX1_TEST_ASSERT_MSG(false, "%s:Error: unknown error (line=%u)\r\n", __func__, __LINE__); + break; + + case CFSTORE_EX_STATE_UNINIT_DONE: + CFSTORE_EX1_LOG("UNINIT_DONE%s", "\r\n"); + CFSTORE_EX1_TEST_ASSERT_MSG(ctx->callback_handle == NULL, "%s:Error: the cfstore_ex_callback(cmd_code==CFSTORE_OPCODE_UNINITIALIZE) received non-NULL handle (%p)\r\n", __func__, ctx->callback_handle); + cfstore_example1_done = true; + CFSTORE_EX1_LOG("***************%s", "\r\n"); + CFSTORE_EX1_LOG("*** SUCCESS ***%s", "\r\n"); + CFSTORE_EX1_LOG("***************%s", "\r\n"); + break; + } +} + +static control_t cfstore_example1_app_start(const size_t call_count) +{ + cfstore_example1_ctx_t* ctx = &cfstore_example1_ctx_g; + + (void) call_count; + + /* initialise the context */ + memset(ctx, 0, sizeof(cfstore_example1_ctx_t)); + cfstore_example1_done = false; + ctx->hkey_next = ctx->hkey_next_buf; + ctx->hkey_prev = ctx->hkey_prev_buf; + ctx->callback_status = ARM_DRIVER_ERROR; + ctx->state = CFSTORE_EX_STATE_INITIALIZING; + ctx->caps = cfstore_drv->GetCapabilities(); + CFSTORE_EX1_LOG("%s:INITIALIZING: caps.asynchronous_ops=%lu\n", __func__, ctx->caps.asynchronous_ops); + cfstore_ex_fms_update(ctx); + + /* main application worker loop */ + while (!cfstore_example1_done) + { + // do some work + CFSTORE_EX1_LOG("%s: going to sleep!\r\n", __func__); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 + __WFE(); +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 + /* mbedosV3++ + * todo: port __WFE() + */ +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + CFSTORE_EX1_LOG("%s: woke up!\r\n", __func__); + } + return CaseNext; +} + +#ifndef YOTTA_CONFIGURATION_STORE_EXAMPLE1_VERSION_STRING +/* when built as Configuration-Store example, include greentea support otherwise omit */ + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_example1_test_00(const size_t call_count) +{ + (void) call_count; + CFSTORE_EX1_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", cfstore_driver.GetCapabilities().asynchronous_ops); + return CaseNext; +} + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE1_test_00", cfstore_example1_test_00), + Case("EXAMPLE1_test_01_start", cfstore_example1_app_start), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#else // YOTTA_CONFIGURATION_STORE_EXAMPLE1_VERSION_STRING + +// stand alone Configuration-Store-Example +void app_start(int argc __unused, char** argv __unused) +{ + cfstore_example1_app_start(0); +} + + + +#endif // YOTTA_CONFIGURATION_STORE_EXAMPLE1_VERSION_STRING + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/example2/example2.cpp b/TESTS/cfstore/example2/example2.cpp new file mode 100644 index 0000000000..18523686f9 --- /dev/null +++ b/TESTS/cfstore/example2/example2.cpp @@ -0,0 +1,328 @@ +/** @file example2.cpp + * + * 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. + * + */ + + +/* Overview of test: + * - initialises cfstore + * - creates a key called "com.arm.mbed.spv.assets.asset2.payload" with value blob length = 15 = strlen("Grumpy old man")+1. + * - writes the data for the key to be "Grumpy old man" + * - closes kv. + * - opens kv for reading/writing + * - reads the value blob and checks its == "Grumpy old man" + * - writes the first 11 chars of the value blob to be "Grumpy man" plus a NULL; + * - reads the value blob back and checks its as expected + * + * This test is coded so as to work in the following modes: + * - flash sync mode i.e. with caps.asynchronous_ops == false + * - flash async mode i.e. with caps.asynchronous_ops == true + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_example2_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE2_test_00", cfstore_example2_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + + +#else + + +#include +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +//#include +#include +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CONFIG_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CONFIG_UVISOR */ + + +using namespace utest::v1; + +static char cfstore_example2_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* defines */ +#define PvMemSet memset +#define PvStrLen strlen + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_example2_test_00(const size_t call_count) +{ + (void) call_count; + CFSTORE_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", cfstore_driver.GetCapabilities().asynchronous_ops); + return CaseNext; +} + +ARM_CFSTORE_DRIVER *drv = &cfstore_driver; +static int32_t CreateKeyValueStore( + const char *keyName, + const char *data, + ARM_CFSTORE_SIZE *dataLength, + ARM_CFSTORE_KEYDESC *keyDesc) +{ + int32_t cfsStatus = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE valueLength = 0; + ARM_CFSTORE_HANDLE_INIT(hkey); + + valueLength = *dataLength; + cfsStatus = drv->Create(keyName, valueLength, keyDesc, hkey); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, cfsStatus); + + valueLength = *dataLength; + cfsStatus = drv->Write(hkey, data, &valueLength); + /* + * (1) Note the following: + * - if cfsStatus > 0 then Write() has completed synchronously and returned the number of bytes written (irrespective of the caps.asynchronous_ops attribute). + * - if cfsStatus == ARM_DRIVER_OK then: + * - if caps.asynchronous_ops == true then the operation will be completed with registered client callback passed to Initialize(). + * - if caps.asynchronous_ops == false then the operation has completed synchronously and no bytes were written. + * - if cfsStatus < ARM_DRIVER_OK then an error has occurred + */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write key (rc=%" PRId32 ")\n", __func__, cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example2_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%sError: valueLength(%" PRId32 ") does not match the expected dataLength(%" PRId32 ")\n", __func__, (int32_t) valueLength, (int32_t) *dataLength); + TEST_ASSERT_MESSAGE(*dataLength == valueLength, cfstore_example2_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%sError: Write() return value cfsStatus(%" PRId32 ") does not match the expected dataLength(%" PRId32 ")\n", __func__, cfsStatus, (int32_t) *dataLength); + TEST_ASSERT_MESSAGE((int32_t) *dataLength == cfsStatus, cfstore_example2_utest_msg_g); + + drv->Close(hkey); + /* CreateKeyValueStore() returns what was returned from Write(). See (1) above for description. */ + return cfsStatus; +} + +static control_t cfstore_example2_test_01(const size_t call_count) +{ + int32_t cfsStatus; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_HANDLE_INIT(updatedKeyH); + ARM_CFSTORE_FMODE flags; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_SIZE valueLen; + char* ptr = NULL; + + const char key[] = "com.arm.mbed.spv.assets.asset2.payload"; + const char value[] = "Grumpy old man"; + + (void) call_count; + + // It must not exceed the value_len field specified when the Key-Value pair was created + const char newDataToWrite[] = "Grumpy man"; + + char readBuf[CFSTORE_KEY_NAME_MAX_LENGTH + 1]; + ARM_CFSTORE_SIZE len = 0; + + // Write a key-value pair + + PvMemSet(&kdesc, 0, sizeof(kdesc)); + PvMemSet(&flags, 0, sizeof(flags)); + PvMemSet(readBuf, 0, CFSTORE_KEY_NAME_MAX_LENGTH + 1); + + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + /* The length supplied to Write() is the number of octets to store in the blob. + * Specifying the following value for valueLen will store the terminating null to + * a string, for example. + */ + valueLen = PvStrLen(value) + 1; + + cfsStatus = drv->Initialize(NULL, NULL); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Initialize() failed (cfsStatus=%" PRId32 ")\n", __func__, cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example2_utest_msg_g); + + cfsStatus = CreateKeyValueStore(key, value, &valueLen, &kdesc); + + /* CreateKeyValueStore() returns the number of characters written, which can vary between 0 and the supplied arg valueLen + * - in the case that this example is compiled for flash mode sync, CreateKeyValueStore(), on success should always return valueLen + * - in the case that this example is compiled for flash mode async, CreateKeyValueStore() on success may return a value 0 to valueLen + * with async notification of the completed transaction. + */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%sError: valueLen(%" PRId32 ") does not match the expected returned value from CreateKeyValueStore(%" PRId32 ")\n", __func__, (int32_t) valueLen, cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus == (int32_t) valueLen, cfstore_example2_utest_msg_g); + + // Read key-value pair with 'Write' permission + + flags.read = true; + flags.write = true; + cfsStatus = drv->Open(key, flags, hkey); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, cfsStatus); + + len = sizeof(readBuf); + cfsStatus = drv->Read(hkey, readBuf, &len); + /* Read() returns the number of characters read, which can vary between 0 and the size of the value blob, and the size of the supplied buffer */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Read() returned value (%" PRId32 ") does not match the created length of the value blob(%" PRId32 ")\n", __func__, cfsStatus, (int32_t) PvStrLen(value) + 1); + TEST_ASSERT_MESSAGE(cfsStatus == (int32_t) (PvStrLen(value) + 1), cfstore_example2_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Read() returned len value (%" PRId32 ") does not match the created length of the value blob(%" PRId32 ")\n", __func__, (int32_t) len, (int32_t) PvStrLen(value) + 1); + TEST_ASSERT_MESSAGE(len == PvStrLen(value) + 1, cfstore_example2_utest_msg_g); + + /* Note: + * - original data = "Grumpy old man", which is 14+1 chars inc NULL + * - New data = "Grumpy man" which is 10+1 chars inc NULL + * - when the key "com.arm.mbed.spv.assets.asset2.payload"; was created, it was created with a value blob size of 14+1=15 chars. + * - The new data is shorter that the old data so it will be accommodated in the value blob + * - The size of the value blob will stay the same. + */ + // Update the value and value length + /* note len set to sizeof(newDataToWrite) which includes the terminating null of the string */ + len = sizeof(newDataToWrite); + cfsStatus = drv->Write(hkey, newDataToWrite, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Write() returned cfsStatus value (%" PRId32 ") does not match the length of new data written(%" PRId32 ")\n", __func__, cfsStatus, (int32_t) sizeof(newDataToWrite)); + TEST_ASSERT_MESSAGE(cfsStatus == (int32_t) sizeof(newDataToWrite), cfstore_example2_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Write() returned len (%" PRId32 ") does not match the length of new data written(%" PRId32 ")\n", __func__, (int32_t) len, (int32_t) sizeof(newDataToWrite)); + TEST_ASSERT_MESSAGE((int32_t) len == (int32_t) sizeof(newDataToWrite), cfstore_example2_utest_msg_g); + + drv->Close(hkey); + + // Check that the value was updated + flags.write = false; + cfsStatus = drv->Open(key, flags, updatedKeyH); + TEST_ASSERT_EQUAL(ARM_DRIVER_OK, cfsStatus); + + len = CFSTORE_KEY_NAME_MAX_LENGTH; + PvMemSet(readBuf, 0, len); + cfsStatus = drv->Read(updatedKeyH, readBuf, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Read() returned value (%" PRId32 ") does not match the created length of the value blob(%" PRId32 ")\n", __func__, cfsStatus, (int32_t) PvStrLen(value) + 1); + TEST_ASSERT_MESSAGE(cfsStatus == (int32_t) (PvStrLen(value) + 1), cfstore_example2_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Read() returned len value (%" PRId32 ") does not match the created length of the value blob(%" PRId32 ")\n", __func__, (int32_t) len, (int32_t) PvStrLen(value) + 1); + TEST_ASSERT_MESSAGE(len == (PvStrLen(value) + 1), cfstore_example2_utest_msg_g); + + /* convert any terminating nulls to '=' */ + while( (ptr = (char*) memchr(readBuf, 0, (PvStrLen(value) + 1))) != NULL) + { + *ptr = '='; + } + /* check the data is as expected */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Read() returned unexpected string (%s) where nulls have been converted to '=' chars\n", __func__, readBuf); + TEST_ASSERT_MESSAGE(strncmp(readBuf, "Grumpy man=man=", (PvStrLen(value) + 1)) == 0, cfstore_example2_utest_msg_g); + + CFSTORE_LOG("Success: New value of KV (%s) value blob (with nulls converted to '=') = (%s)\n", key, readBuf); + + drv->Close(updatedKeyH); + + cfsStatus = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() failed (cfsStatus=%" PRId32 ")\n", __func__, cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example2_utest_msg_g); + + return CaseNext; +} + + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(400, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE2_test_00", cfstore_example2_test_00), + Case("EXAMPLE2_test_01", cfstore_example2_test_01), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/example3/example3.cpp b/TESTS/cfstore/example3/example3.cpp new file mode 100644 index 0000000000..4b7d3dc195 --- /dev/null +++ b/TESTS/cfstore/example3/example3.cpp @@ -0,0 +1,393 @@ +/** @file example3.cpp + * + * 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. + * + * Test cases to add and delete KVs in the CFSTORE. + */ + + +/* Notes + * ===== + * + * The flash-journal synchronous mode example test does the following CFSTORE operations: + * - initialises + * - creates a key-value pair (KV). + * - writes the data for the KV + * - closes KV. + * - flushes the KV to flash + * - opens KV for reading. + * - reads KV and checks the value blob was the same as previously written. + * - closes KV. + * - finds a KV (there is only 1 to find). + * - for the KV returned, get the key name. + * - for the KV returned, get the value length. + * - for the KV returned, delete the KV. + * - find another KV (which fails as there are no more keys to find). + * - flushes the updated state to flash to store the removal of the deleted KV. + * - uninitialises + * - stops + * + * This test is coded so as to work only in flash journal sync mode + * i.e. with caps.asynchronous_ops == false + * + * The test leaves the flash in the same state as at the beginning of the test so + * it can be run a second time on the device without flashing, and the test should + * still work. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_example3_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE3_test_00", cfstore_example3_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + + +#else + + +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif +#ifndef YOTTA_CONFIGURATION_STORE_EXAMPLE3_VERSION_STRING +/* when built as Configuration-Store example, include greentea support otherwise omit */ +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#else // YOTTA_CONFIGURATION_STORE_EXAMPLE3_VERSION_STRING +// map utest types for building as stand alone example +#define control_t void +#define CaseNext +#endif // YOTTA_CONFIGURATION_STORE_EXAMPLE3_VERSION_STRING + +#include +#include +#include "cfstore_config.h" +#include +#ifdef YOTTA_CFG_CONFIG_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CONFIG_UVISOR */ + + +#ifndef YOTTA_CONFIGURATION_STORE_EXAMPLE3_VERSION_STRING +using namespace utest::v1; +#endif + + +#define CFSTORE_EX1_TEST_ASSERT(Expr) if (!(Expr)) { printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} +#define CFSTORE_EX1_TEST_ASSERT_EQUAL(expected, actual) if ((expected) != (actual)) {printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} +#define CFSTORE_EX1_TEST_ASSERT_NOT_EQUAL(expected, actual) if ((expected) == (actual)) {printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} + +#define CFSTORE_EX1_TEST_ASSERT_MSG(Expr, _fmt, ...) \ + do \ + { \ + if (!(Expr)) \ + { \ + printf(_fmt, __VA_ARGS__); \ + while (1) ; \ + } \ + }while(0); + +#define CFSTORE_EX1_LOG(_fmt, ...) \ + do \ + { \ + printf(_fmt, __VA_ARGS__); \ + }while(0); + + +const char* cfstore_ex3_opcode_str[] = +{ + "UNDEFINED", + "CFSTORE_OPCODE_CLOSE", + "CFSTORE_OPCODE_CREATE", + "CFSTORE_OPCODE_DELETE", + "CFSTORE_OPCODE_FIND", + "CFSTORE_OPCODE_FLUSH", + "CFSTORE_OPCODE_GET_KEY_NAME", + "CFSTORE_OPCODE_GET_STATUS", + "CFSTORE_OPCODE_GET_VALUE_LEN", + "CFSTORE_OPCODE_INITIALIZE", + "CFSTORE_OPCODE_OPEN", + "CFSTORE_OPCODE_POWER_CONTROL", + "CFSTORE_OPCODE_READ", + "CFSTORE_OPCODE_RSEEK", + "CFSTORE_OPCODE_UNINITIALIZE", + "CFSTORE_OPCODE_WRITE", + "CFSTORE_OPCODE_MAX" +}; + +const char* cfstore_ex3_kv_name = "basement.medicine.pavement.government.trenchcoat.off.cough.off.kid.did.when.again.alleyway.friend.cap.pen.dollarbills.ten.foot.soot.put.but.anyway.say.May.DA.kid.did.toes.bows.those.hose.nose.clothes.man.blows.well.well"; +const char* cfstore_ex3_kv_value = "TheRollingStone"; +#define CFSTORE_EX1_RSEEK_OFFSET 10 /* offset to S of Stone */ + +typedef struct cfstore_example3_ctx_t +{ + ARM_CFSTORE_CAPABILITIES caps;; + uint8_t hkey[CFSTORE_HANDLE_BUFSIZE]; + uint8_t hkey_next_buf[CFSTORE_HANDLE_BUFSIZE]; + uint8_t hkey_prev_buf[CFSTORE_HANDLE_BUFSIZE]; + ARM_CFSTORE_HANDLE hkey_next; + ARM_CFSTORE_HANDLE hkey_prev; + ARM_CFSTORE_SIZE len; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_FMODE flags; + char value[CFSTORE_KEY_NAME_MAX_LENGTH+1]; +} cfstore_example3_ctx_t; + +static cfstore_example3_ctx_t cfstore_example3_ctx_g; + +extern ARM_CFSTORE_DRIVER cfstore_driver; +ARM_CFSTORE_DRIVER *cfstore_drv = &cfstore_driver; + + +static void cfstore_ex3_test_01(cfstore_example3_ctx_t* ctx) +{ + int32_t ret; + + CFSTORE_EX1_LOG("INITIALIZING%s", "\r\n"); + ret = cfstore_drv->Initialize(NULL, NULL); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Initialize() should return ret >= 0 for async/synch modes(ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("CREATING%s", "\r\n"); + memset(&ctx->kdesc, 0, sizeof(ARM_CFSTORE_KEYDESC)); + ctx->kdesc.drl = ARM_RETENTION_NVM; + ctx->len = strlen(cfstore_ex3_kv_value); + ret = cfstore_drv->Create(cfstore_ex3_kv_name, ctx->len, &ctx->kdesc, ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Create() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("WRITING%s", "\r\n"); + ctx->len = strlen(cfstore_ex3_kv_value); + ret = cfstore_drv->Write(ctx->hkey, cfstore_ex3_kv_value, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Write() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_TEST_ASSERT_MSG(ret == (int32_t) strlen(cfstore_ex3_kv_value), "%s:Error: Write() number of octets written (i.e. completion status (%ld)) != strlen(ctx->value)(%ld)\r\n", __func__, ret, (int32_t) strlen(cfstore_ex3_kv_value)); + CFSTORE_EX1_TEST_ASSERT_MSG(ret == (int32_t) ctx->len, "%s:Error: Write() number of octets written (i.e. completion status (%ld)) != updated value of len parameter (%ld)\r\n", __func__, ret, (int32_t) ctx->len); + + CFSTORE_EX1_LOG("CLOSING1%s", "\r\n"); + ret = cfstore_drv->Close(ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("FLUSHING1%s", "\r\n"); + ret = cfstore_drv->Flush(); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Flush() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("OPENING%s", "\r\n"); + memset(&ctx->flags, 0, sizeof(ctx->flags)); + memset(&ctx->hkey, 0, CFSTORE_HANDLE_BUFSIZE); + ret = cfstore_drv->Open(cfstore_ex3_kv_name, ctx->flags, ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Open() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("READING1%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(ctx->value, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + ret = cfstore_drv->Read(ctx->hkey, ctx->value, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Read() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_TEST_ASSERT_MSG(ret == (int32_t) strlen(cfstore_ex3_kv_value), "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != strlen(ctx->value)(%ld)\r\n", __func__, ret, (int32_t) strlen(cfstore_ex3_kv_value)); + CFSTORE_EX1_TEST_ASSERT_MSG(ret == (int32_t) ctx->len, "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != updated value of len parameter (%ld)\r\n", __func__, ret, (int32_t) ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(strncmp(ctx->value, cfstore_ex3_kv_value, strlen(cfstore_ex3_kv_value)) == 0, "%s:Error: the read value (%s) is not as expected (%s)\r\n", __func__, ctx->value, cfstore_ex3_kv_value); + + CFSTORE_EX1_LOG("RSEEKING%s", "\r\n"); + ret = cfstore_drv->Rseek(ctx->hkey, CFSTORE_EX1_RSEEK_OFFSET); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Rseek() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("READING2%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(ctx->value, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + ret = cfstore_drv->Read(ctx->hkey, ctx->value, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Read() failed (ret=%ld)\r\n", __func__, ret); + CFSTORE_EX1_TEST_ASSERT_MSG(ret == (int32_t) strlen(&cfstore_ex3_kv_value[CFSTORE_EX1_RSEEK_OFFSET]), "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != strlen(ctx->value)(%ld)\r\n", __func__, ret, (int32_t) strlen(&cfstore_ex3_kv_value[CFSTORE_EX1_RSEEK_OFFSET])); + CFSTORE_EX1_TEST_ASSERT_MSG(ret == (int32_t) ctx->len, "%s:Error: Read() number of octets read (i.e. completion status (%ld)) != updated value of len parameter (%ld)\r\n", __func__, ret, (int32_t) ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(strncmp(ctx->value, &cfstore_ex3_kv_value[CFSTORE_EX1_RSEEK_OFFSET], strlen(&cfstore_ex3_kv_value[CFSTORE_EX1_RSEEK_OFFSET])) == 0, "%s:Error: the read value (%s) is not as expected (%s)\r\n", __func__, ctx->value, &cfstore_ex3_kv_value[CFSTORE_EX1_RSEEK_OFFSET]); + + CFSTORE_EX1_LOG("CLOSING2%s", "\r\n"); + ret = cfstore_drv->Close(ctx->hkey); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("FINDING1%s", "\r\n"); + ret = cfstore_drv->Find("*", ctx->hkey_next, ctx->hkey_prev); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Find() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("GETTING_KEY_NAME%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(ctx->value, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + ret = cfstore_drv->GetKeyName(ctx->hkey_prev, ctx->value, (uint8_t*) &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: GetKeyName() failed (ret=%ld)\r\n", __func__, ret); + CFSTORE_EX1_TEST_ASSERT_MSG( ((int32_t) ctx->len == ((int32_t) strlen(cfstore_ex3_kv_name)+1)), "%s:Error: GetKeyName() updated value of len parameter (%ld) != strlen(cfstore_ex3_kv_name) (%ld) (\r\n", __func__, (int32_t) ctx->len, (int32_t) strlen(cfstore_ex3_kv_name)); + CFSTORE_EX1_TEST_ASSERT_MSG(strncmp(ctx->value, cfstore_ex3_kv_name, strlen(cfstore_ex3_kv_name)) == 0, "%s:Error: the key name (%s) is not as expected (%s)\r\n", __func__, ctx->value, cfstore_ex3_kv_name); + + CFSTORE_EX1_LOG("GETTING_VALUE_LEN%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + ret = cfstore_drv->GetValueLen(ctx->hkey_prev, &ctx->len); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: GetValueLen() failed (ret=%ld)\r\n", __func__, ret); + CFSTORE_EX1_TEST_ASSERT_MSG((int32_t) ctx->len == (int32_t) strlen(cfstore_ex3_kv_value), "%s:Error: GetValueLen() updated value of len parameter (%ld) != strlen(cfstore_ex3_kv_value)(%ld) \r\n", __func__, (int32_t) ctx->len, (int32_t) strlen(cfstore_ex3_kv_value)); + + CFSTORE_EX1_LOG("DELETING%s", "\r\n"); + ret = cfstore_drv->Delete(ctx->hkey_prev); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + CFSTORE_HANDLE_SWAP(ctx->hkey_prev, ctx->hkey_next); + + CFSTORE_EX1_LOG("FINDING2%s", "\r\n"); + ret = cfstore_drv->Find("*", ctx->hkey_next, ctx->hkey_prev); + CFSTORE_EX1_TEST_ASSERT_MSG(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, "%s:Error: Find() failed to return expected value of ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("FLUSHING2%s", "\r\n"); + ret = cfstore_drv->Flush(); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error:2: Flush() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX1_LOG("UNINITIALIZING%s", "\r\n"); + ret = cfstore_drv->Uninitialize(); + CFSTORE_EX1_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Uninitialize() should return ret >= 0 for synch mode(ret=%ld)\r\n", __func__, ret); + CFSTORE_EX1_LOG("***************%s", "\r\n"); + CFSTORE_EX1_LOG("*** SUCCESS ***%s", "\r\n"); + CFSTORE_EX1_LOG("***************%s", "\r\n"); + return; +} + +static control_t cfstore_example3_app_start(const size_t call_count) +{ + cfstore_example3_ctx_t* ctx = &cfstore_example3_ctx_g; + + (void) call_count; + + /* initialise the context */ + memset(ctx, 0, sizeof(cfstore_example3_ctx_t)); + ctx->hkey_next = ctx->hkey_next_buf; + 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){ + /* 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"); + return CaseNext; + } + cfstore_ex3_test_01(ctx); + return CaseNext; +} + +#ifndef YOTTA_CONFIGURATION_STORE_EXAMPLE3_VERSION_STRING +/* when built as Configuration-Store example, include greentea support otherwise omit */ + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_example3_test_00(const size_t call_count) +{ + (void) call_count; + CFSTORE_EX1_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", cfstore_driver.GetCapabilities().asynchronous_ops); + return CaseNext; +} + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE3_test_00", cfstore_example3_test_00), + Case("EXAMPLE3_test_01_start", cfstore_example3_app_start), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + +#else // YOTTA_CONFIGURATION_STORE_EXAMPLE3_VERSION_STRING + +// stand alone Configuration-Store-Example +void app_start(int argc __unused, char** argv __unused) +{ + cfstore_example3_app_start(0); +} + + + +#endif // YOTTA_CONFIGURATION_STORE_EXAMPLE3_VERSION_STRING + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/example4/example4.cpp b/TESTS/cfstore/example4/example4.cpp new file mode 100644 index 0000000000..6ffbd11bf6 --- /dev/null +++ b/TESTS/cfstore/example4/example4.cpp @@ -0,0 +1,278 @@ +/** @file example3.cpp + * + * 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. + * + * Test cases to add and delete KVs in the CFSTORE. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_example4_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE4_test_00", cfstore_example4_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + + +#else + + +#include +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +//#include +#include +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CONFIG_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CONFIG_UVISOR */ + + +using namespace utest::v1; + +static char cfstore_example4_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* Notes + * Test case created from Issue 10 code supplied by Motti Gondabi. The code: + * - creates a KV + * - writes the KV + * - closes the KV + * - flushes the KV + * - opens the KV. + * - deletes the KV. + * - flushes empty configuration store. + * + * The test case makes sure that the implementation can flush an empty configuration store + * without causing errors. This has only been possible since flash-journal-strategy-sequential + * v0.4.0. + */ + +/* defines */ +#define PvMemSet memset +#define PvStrLen strlen +#define PvKeyValue_t cfstore_kv_data_t + +ARM_CFSTORE_DRIVER *gCfStoreDriver = &cfstore_driver; + +static const PvKeyValue_t testDataKeyValue[] = { + { "com.arm.mbed.spv.assets.dtls", "This Is my DTLS Secret" }, + { "com.arm.mbed.spv.assets.asset1.payload", "The Rolling Stone" }, + { "com.arm.mbed.spv.assets.asset2.payload", "Grumpy old man" }, + { "com.arm.mbed.spv.assets.asset3.payload", "Delete this asset payload" }, +}; + + +static control_t cfstore_example4_test_00(const size_t call_count) +{ + ARM_CFSTORE_CAPABILITIES caps;; + + (void) 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){ + /* 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"); + return CaseNext; + } + return CaseNext; +} + + +static int32_t CreateKeyValueStore( + const char *keyName, + const char *data, + ARM_CFSTORE_SIZE *dataLength, + ARM_CFSTORE_KEYDESC *keyDesc) +{ + int32_t cfsStatus = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE valueLength = 0; + ARM_CFSTORE_HANDLE_INIT(hkey); + + valueLength = *dataLength; + cfsStatus = gCfStoreDriver->Create(keyName, valueLength, keyDesc, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Create() failed (cfsStatus=%" PRId32 ")\n", __func__, cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + valueLength = *dataLength; + cfsStatus = gCfStoreDriver->Write(hkey, data, &valueLength); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Write() failed (cfsStatus=%" PRId32 ")\n", __func__, cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: valueLength != *dataLength\n", __func__); + TEST_ASSERT_MESSAGE(valueLength == *dataLength, cfstore_example4_utest_msg_g); + + cfsStatus = gCfStoreDriver->Close(hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Close() failed (cfsStatus=%" PRId32 ")\n", __func__, cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + cfsStatus = gCfStoreDriver->Flush(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Flush() failed (cfsStatus=%" PRId32 ")\n", __func__, cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + return ARM_DRIVER_OK; +} + + +// main test. +static control_t cfstore_example4_test_01(const size_t call_count) +{ + int32_t cfsStatus = ARM_DRIVER_ERROR; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_SIZE valueLen; + ARM_CFSTORE_FMODE flags; + ARM_CFSTORE_HANDLE_INIT(hkey); + + (void) call_count; + PvMemSet(&kdesc, 0, sizeof(kdesc)); + + kdesc.drl = ARM_RETENTION_NVM; + valueLen = PvStrLen(testDataKeyValue[0].value); + + cfsStatus = gCfStoreDriver->Initialize(NULL, NULL); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Initialize() failed (cfsStatus=%" PRId32 ")\n", __func__, cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + cfsStatus = CreateKeyValueStore(testDataKeyValue[0].key_name, testDataKeyValue[0].value, &valueLen, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: CreateKeyValueStore() failed (cfsStatus=%" PRId32 ")\n", __func__, cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + PvMemSet(&flags, 0, sizeof(flags)); + + cfsStatus = gCfStoreDriver->Open(testDataKeyValue[0].key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Open() failed (cfsStatus=%" PRId32 ")\n", __func__, cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + cfsStatus = gCfStoreDriver->Delete(hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Delete() failed (cfsStatus=%" PRId32 ")\n", __func__, cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + cfsStatus = gCfStoreDriver->Close(hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Close() failed (cfsStatus=%" PRId32 ")\n", __func__, cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + cfsStatus = gCfStoreDriver->Flush(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Flush() failed (cfsStatus=%" PRId32 ")\n", __func__, cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + cfsStatus = gCfStoreDriver->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_example4_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() failed (cfsStatus=%" PRId32 ")\n", __func__, cfsStatus); + TEST_ASSERT_MESSAGE(cfsStatus >= ARM_DRIVER_OK, cfstore_example4_utest_msg_g); + + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(400, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE4_test_00", cfstore_example4_test_00), +#if defined YOTTA_CFG_CONFIG_HARDWARE_MTD_ASYNC_OPS && YOTTA_CFG_CONFIG_HARDWARE_MTD_ASYNC_OPS == 0 + Case("EXAMPLE4_test_01", cfstore_example4_test_01), +#endif // YOTTA_CFG_CONFIG_HARDWARE_MTD_ASYNC_OPS +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/example5/example5.cpp b/TESTS/cfstore/example5/example5.cpp new file mode 100644 index 0000000000..9e234c2ef6 --- /dev/null +++ b/TESTS/cfstore/example5/example5.cpp @@ -0,0 +1,365 @@ +/** @file EXAMPLE5.cpp + * + * 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. + * + * Test cases to add and delete KVs in the CFSTORE. + */ + + +/* Notes + * ===== + * + * The flash-journal synchronous mode example test does the following CFSTORE operations: + * - initialises + * - creates a key-value pair (KV). + * - writes the data for the KV + * - closes KV. + * - flushes the KV to flash + * - flushes the KV to flash again + * - uninitialises + * - initialises + * - opens KV for reading. + * - deletes KV + * - closes KV. + * - flushes the updated state to flash to store the removal of the deleted KV. + * - uninitialises + * - stops + * + * This test is coded so as to work only in flash journal sync mode + * i.e. with caps.asynchronous_ops == false + * + * The test leaves the flash in the same state as at the beginning of the test so + * it can be run a second time on the device without flashing, and the test should + * still work. + */ + +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_example5_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE5_test_00", cfstore_example5_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + +#else + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +//#include +#include + +#ifndef YOTTA_CONFIGURATION_STORE_EXAMPLE5_VERSION_STRING +/* when built as Configuration-Store example, include greentea support otherwise omit */ +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#else // YOTTA_CONFIGURATION_STORE_EXAMPLE5_VERSION_STRING +// map utest types for building as stand alone example +#define control_t void +#define CaseNext +#endif // YOTTA_CONFIGURATION_STORE_EXAMPLE5_VERSION_STRING + +#include "cfstore_config.h" +#include + + +#ifndef YOTTA_CONFIGURATION_STORE_EXAMPLE5_VERSION_STRING +using namespace utest::v1; +#endif + + +#define CFSTORE_EX5_TEST_ASSERT(Expr) if (!(Expr)) { printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} +#define CFSTORE_EX5_TEST_ASSERT_EQUAL(expected, actual) if ((expected) != (actual)) {printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} +#define CFSTORE_EX5_TEST_ASSERT_NOT_EQUAL(expected, actual) if ((expected) == (actual)) {printf("%s:%u: assertion failure\r\n", __FUNCTION__, __LINE__); while (1) ;} + +#define CFSTORE_EX5_TEST_ASSERT_MSG(Expr, _fmt, ...) \ + do \ + { \ + if (!(Expr)) \ + { \ + printf(_fmt, __VA_ARGS__); \ + while (1) ; \ + } \ + }while(0); + +#define CFSTORE_EX5_LOG(_fmt, ...) \ + do \ + { \ + printf(_fmt, __VA_ARGS__); \ + }while(0); + + +const char* cfstore_ex5_opcode_str[] = +{ + "UNDEFINED", + "CFSTORE_OPCODE_CLOSE", + "CFSTORE_OPCODE_CREATE", + "CFSTORE_OPCODE_DELETE", + "CFSTORE_OPCODE_FIND", + "CFSTORE_OPCODE_FLUSH", + "CFSTORE_OPCODE_GET_KEY_NAME", + "CFSTORE_OPCODE_GET_STATUS", + "CFSTORE_OPCODE_GET_VALUE_LEN", + "CFSTORE_OPCODE_INITIALIZE", + "CFSTORE_OPCODE_OPEN", + "CFSTORE_OPCODE_POWER_CONTROL", + "CFSTORE_OPCODE_READ", + "CFSTORE_OPCODE_RSEEK", + "CFSTORE_OPCODE_UNINITIALIZE", + "CFSTORE_OPCODE_WRITE", + "CFSTORE_OPCODE_MAX" +}; + +const char* cfstore_ex5_kv_name = "basement.medicine.pavement.government.trenchcoat.off.cough.off.kid.did.when.again.alleyway.friend.cap.pen.dollarbills.ten.foot.soot.put.but.anyway.say.May.DA.kid.did.toes.bows.those.hose.nose.clothes.man.blows.well.well"; +const char* cfstore_ex5_kv_value = "TheRollingStone"; +#define CFSTORE_EX5_RSEEK_OFFSET 10 /* offset to S of Stone */ + +typedef struct cfstore_EXAMPLE5_ctx_t +{ + ARM_CFSTORE_CAPABILITIES caps;; + uint8_t hkey[CFSTORE_HANDLE_BUFSIZE]; + uint8_t hkey_next_buf[CFSTORE_HANDLE_BUFSIZE]; + uint8_t hkey_prev_buf[CFSTORE_HANDLE_BUFSIZE]; + ARM_CFSTORE_HANDLE hkey_next; + ARM_CFSTORE_HANDLE hkey_prev; + ARM_CFSTORE_SIZE len; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_FMODE flags; + char value[CFSTORE_KEY_NAME_MAX_LENGTH+1]; +} cfstore_EXAMPLE5_ctx_t; + +static cfstore_EXAMPLE5_ctx_t cfstore_EXAMPLE5_ctx_g; + +extern ARM_CFSTORE_DRIVER cfstore_driver; +ARM_CFSTORE_DRIVER *cfstore_drv = &cfstore_driver; + + +static void cfstore_ex5_test_01(cfstore_EXAMPLE5_ctx_t* ctx) +{ + int32_t ret; + + CFSTORE_EX5_LOG("INITIALIZING1%s", "\r\n"); + ret = cfstore_drv->Initialize(NULL, NULL); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Initialize() should return ret >= 0 for async/synch modes(ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("CREATING%s", "\r\n"); + memset(&ctx->kdesc, 0, sizeof(ARM_CFSTORE_KEYDESC)); + ctx->kdesc.drl = ARM_RETENTION_NVM; + ctx->len = strlen(cfstore_ex5_kv_value); + ret = cfstore_drv->Create(cfstore_ex5_kv_name, ctx->len, &ctx->kdesc, ctx->hkey); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Create() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("WRITING%s", "\r\n"); + ctx->len = strlen(cfstore_ex5_kv_value); + ret = cfstore_drv->Write(ctx->hkey, cfstore_ex5_kv_value, &ctx->len); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Write() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_TEST_ASSERT_MSG(ret == (int32_t) strlen(cfstore_ex5_kv_value), "%s:Error: Write() number of octets written (i.e. completion status (%ld)) != strlen(ctx->value)(%ld)\r\n", __func__, ret, (int32_t) strlen(cfstore_ex5_kv_value)); + CFSTORE_EX5_TEST_ASSERT_MSG(ret == (int32_t) ctx->len, "%s:Error: Write() number of octets written (i.e. completion status (%ld)) != updated value of len parameter (%ld)\r\n", __func__, ret, (int32_t) ctx->len); + + CFSTORE_EX5_LOG("CLOSING1%s", "\r\n"); + ret = cfstore_drv->Close(ctx->hkey); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("FLUSHING1%s", "\r\n"); + ret = cfstore_drv->Flush(); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Flush() failed (ret=%ld)\r\n", __func__, ret); + + /* commenting out the 2nd flush() means the test works.*/ + //CFSTORE_EX5_LOG("FLUSHING2%s", "\r\n"); + //ret = cfstore_drv->Flush(); + //CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Flush() failed (ret=%ld)\r\n", __func__, ret); + + /* todo: re-instate when Flush() really persists to flash on mbedOSv3+++. + * currently in the supported sram version Uninitialize() clears the sram + CFSTORE_EX5_LOG("UNINITIALIZING1%s", "\r\n"); + ret = cfstore_drv->Uninitialize(); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Uninitialize() should return ret >= 0 for synch mode(ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("INITIALIZING2%s", "\r\n"); + ret = cfstore_drv->Initialize(NULL, NULL); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Initialize() should return ret >= 0 for async/synch modes(ret=%ld)\r\n", __func__, ret); + */ + + CFSTORE_EX5_LOG("OPENING%s", "\r\n"); + memset(&ctx->flags, 0, sizeof(ctx->flags)); + memset(&ctx->hkey, 0, CFSTORE_HANDLE_BUFSIZE); + ret = cfstore_drv->Open(cfstore_ex5_kv_name, ctx->flags, ctx->hkey); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Open() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("DELETE1%s", "\r\n"); + ctx->len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(ctx->value, 0, CFSTORE_KEY_NAME_MAX_LENGTH + 1); + ret = cfstore_drv->Delete(ctx->hkey); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Delete() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("CLOSING2%s", "\r\n"); + ret = cfstore_drv->Close(ctx->hkey); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Close() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("FLUSHING3%s", "\r\n"); + ret = cfstore_drv->Flush(); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Flush() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("OPEN2 %s", "\r\n"); + memset(&ctx->flags, 0, sizeof(ctx->flags)); + memset(&ctx->hkey, 0, CFSTORE_HANDLE_BUFSIZE); + ret = cfstore_drv->Open(cfstore_ex5_kv_name, ctx->flags, ctx->hkey); + CFSTORE_EX5_TEST_ASSERT_MSG(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND, "%s:Error: Find() failed to return expected value of ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("FLUSHING2%s", "\r\n"); + ret = cfstore_drv->Flush(); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error:2: Flush() failed (ret=%ld)\r\n", __func__, ret); + + CFSTORE_EX5_LOG("UNINITIALIZING3%s", "\r\n"); + ret = cfstore_drv->Uninitialize(); + CFSTORE_EX5_TEST_ASSERT_MSG(ret >= ARM_DRIVER_OK, "%s:Error: Uninitialize() should return ret >= 0 for synch mode(ret=%ld)\r\n", __func__, ret); + CFSTORE_EX5_LOG("***************%s", "\r\n"); + CFSTORE_EX5_LOG("*** SUCCESS ***%s", "\r\n"); + CFSTORE_EX5_LOG("***************%s", "\r\n"); + return; +} + +static control_t cfstore_EXAMPLE5_app_start(const size_t call_count) +{ + cfstore_EXAMPLE5_ctx_t* ctx = &cfstore_EXAMPLE5_ctx_g; + + (void) call_count; + + /* initialise the context */ + memset(ctx, 0, sizeof(cfstore_EXAMPLE5_ctx_t)); + ctx->hkey_next = ctx->hkey_next_buf; + 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){ + /* 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"); + return CaseNext; + } + cfstore_ex5_test_01(ctx); + return CaseNext; +} + +#ifndef YOTTA_CONFIGURATION_STORE_EXAMPLE5_VERSION_STRING +/* when built as Configuration-Store example, include greentea support otherwise omit */ + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("EXAMPLE5_test_01_start", cfstore_EXAMPLE5_app_start), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#else // YOTTA_CONFIGURATION_STORE_EXAMPLE5_VERSION_STRING + +// stand alone Configuration-Store-Example +void app_start(int argc __unused, char** argv __unused) +{ + cfstore_EXAMPLE5_app_start(0); +} + + +#endif // YOTTA_CONFIGURATION_STORE_EXAMPLE5_VERSION_STRING + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/find/find.cpp b/TESTS/cfstore/find/find.cpp new file mode 100644 index 0000000000..b8b58a926c --- /dev/null +++ b/TESTS/cfstore/find/find.cpp @@ -0,0 +1,443 @@ +/** @file find.cpp + * + * 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. + * + * Test cases to find KVs in the CFSTORE using the drv->Find() interface. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_find_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("FIND_test_00", cfstore_find_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + + +#else + + +#include +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +//#include +#include +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static char cfstore_find_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.find.box1"); +UVISOR_BOX_CONFIG(cfstore_find_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_find_test_00(const size_t call_count) +{ + (void) call_count; + CFSTORE_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", cfstore_driver.GetCapabilities().asynchronous_ops); + return CaseNext; +} + +/** + * @brief test to call cfstore_find() with a key_name string that exceeds + * the maximum length + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_find_test_01(const size_t call_count) +{ + (void) call_count; + /*todo: implement test */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Warn: Not implemented\n", __func__); + CFSTORE_LOG("%s: WARN: requires implementation\n", __func__); + TEST_ASSERT_MESSAGE(true, cfstore_find_utest_msg_g); + return CaseNext; +} + + +/** + * @brief test to call cfstore_find() with key_name that in includes + * illegal characters + * - the character can be at the beginning of the key_name + * - the character can be at the end of the key_name + * - the character can be somewhere within the key_name string + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_find_test_02(const size_t call_count) +{ + /*todo: implement test + * + * specify the allowable characters in a set. + * e.g. "0123456789ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvwxyz.[]*" + * and use this set as the sell of allowable character codes. + * All other 0-255 values for character codes should not be found in the key_name string + * but the function should be tested for what happens in that case. + * + * Some code may be common here with other functions requiring a key_name e.g. cfstore_find( + */ + (void) call_count; + /*todo: implement test */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Warn: Not implemented\n", __func__); + CFSTORE_LOG("%s: WARN: requires implementation\n", __func__); + TEST_ASSERT_MESSAGE(true, cfstore_find_utest_msg_g); + return CaseNext; +} + + + +static control_t cfstore_find_test_03_end(const size_t call_count) +{ + char* read_buf = NULL; + const uint8_t key_name_max_len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + char key_name_buf[key_name_max_len]; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_SIZE max_len = 0; + cfstore_kv_data_t* node; + cfstore_kv_data_t* client_node = cfstore_test_init_1_data; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(prev); + ARM_CFSTORE_HANDLE_INIT(next); + + CFSTORE_DBGLOG("%s:entered\r\n", __func__); + (void) call_count; + memset(&kdesc, 0, sizeof(kdesc)); + memset(key_name_buf, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + + /*scan for max length of value blob*/ + node = client_node; + while(node->key_name != NULL) + { + len = strlen(node->value); + if(len > max_len){ + max_len = len; + } + node++; + } + max_len++; /* space for a terminating null, if required */ + read_buf = (char*) malloc(max_len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to allocated read buffer \r\n", __func__); + TEST_ASSERT_MESSAGE(read_buf != NULL, cfstore_find_utest_msg_g); + + ret = cfstore_test_init_1(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialise cfstore area with entries\r\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_find_utest_msg_g); + + /* now find and read back the key values */ + ret = ARM_DRIVER_ERROR; + node = client_node; + while(node->key_name != NULL) + { + CFSTORE_DBGLOG("%s:About to find node (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + ret = drv->Find(node->key_name, prev, next); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to find node (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_find_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Find failed to return valid key handle\r\n", __func__); + TEST_ASSERT_MESSAGE(next != NULL, cfstore_find_utest_msg_g); + + ret = drv->GetValueLen(next, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write key (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_find_utest_msg_g); + + if(len > 0) { + ret = drv->Read(next, read_buf, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to read value (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_find_utest_msg_g); + + /* check read data is as expected */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Read value data (%s) != KV value data (key_name=\"%s\", value=\"%s\")\r\n", __func__, read_buf, node->key_name, node->value); + TEST_ASSERT_MESSAGE(strncmp(read_buf, node->value, strlen(node->value)) == 0, cfstore_find_utest_msg_g); + + } + read_buf[len] = '\0'; + printf("Successfully found KV and read value data (key_name=\"%s\", value=\"%s\")\r\n", node->key_name, read_buf); + memset(read_buf, 0, len); + drv->Close(next); + node++; + } + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_find_utest_msg_g); + return CaseNext; +} + + +static cfstore_kv_data_t cfstore_find_test_04_kv_data[] = { + { "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "abcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyzabcdefghjklmnopqrstuvwxyz"}, + { NULL, NULL}, +}; + +/** + * @brief TODO: write test that uses cfstore_find_test_04_kv_data to grow {key, value} + * from 1 char to 221 chars long. + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_find_test_04(const size_t call_count) +{ + /*todo: implement test + * + * */ + (void) cfstore_find_test_04_kv_data; + (void) call_count; + /*todo: implement test */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Warn: Not implemented\n", __func__); + CFSTORE_LOG("%s: WARN: requires implementation\n", __func__); + TEST_ASSERT_MESSAGE(true, cfstore_find_utest_msg_g); + return CaseNext; +} + + +/** + * @brief function to test whether a key name is valid, can be added to the + * cfstore and found by a wildcard string + * + * @param key_name + * name of the key to create in the store + * @param match + * string to use to try and find key_name in the cfstore + * @param should_find + * if true, then 'match' should find 'key_name' in the store + * if false, then 'match' should not find 'key_name' in the store + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +bool cfstore_find_key_name_validate(const char *key_name, const char *match, bool should_find) +{ + bool bret = true; + bool btest_status = false; + char* test_data = (char*) "test_data"; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_KEYDESC kdesc; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + memset(&kdesc, 0, sizeof(kdesc)); + + /* create */ + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(test_data); + ret = cfstore_test_create((const char*) key_name, test_data, &len, &kdesc); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to create kv (key_name=%s.\r\n", "cfstore_find_test_05_ex", key_name); + return ret; + } + ret = cfstore_test_kv_is_found(match, &bret); + if(ret < ARM_DRIVER_OK && ret != ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND){ + CFSTORE_ERRLOG("%s:Error: cfstore_test_kv_is_found() failed.\r\n", "cfstore_find_test_05_ex"); + return ret; + } + /* dont not use any functions that require finding the created item as they may not work, + * depending on the construction of the test key_name & match strings */ + if(should_find == bret) + { + CFSTORE_DBGLOG("%s:Success: Find() behaved as expected.\r\n", "cfstore_find_test_05_ex"); + btest_status = true; + } + else + { + CFSTORE_ERRLOG("cfstore_find_test_05_ex: Failed match on %s vs. %s\n", key_name, match); + } + /*delete using the actual name */ + ret = cfstore_test_delete((const char*) key_name); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to delete kv (key_name=%s)(ret=%d).\r\n", "cfstore_find_test_05_ex", key_name, (int)ret); + } + return btest_status; +} + + +typedef struct cfstore_find_key_name_validate_t { + const char* key_name; + const char* match; + uint32_t f_allowed : 1; +} cfstore_find_key_name_validate_t; + +cfstore_find_key_name_validate_t cfstore_find_test_05_data[] = { + { "yotta.hello-world.animal{wobbly-dog}{foot}backLeft", "yotta.hello-world.animal{*}{foot}backLeft", true}, + { "yotta.hello-world.animal{wobbly-dog}{foot}backLeft", "yotta.hello-world.animal{wobbly-dog}{*}backLeft", true}, + { "yotta.hello-world.animal{wobbly-dog}{foot}backLeft", "yotta.hello-world.animal{wobbly-dog}{*}*", true}, + { "yotta.hello-world.animal{1}{foot}backLeft", "yotta.hello-world.animal{?}{foot}backLeft", false}, + { "xyz", "xyz", true}, + { "xyzijkXYZ", "XYZ", false}, + { "xyzijkXYZ", "*XYZ", true}, + { "xyzijkXYZ", "xyz*XYZ", true}, + { "xyzijkXYZ", "*yz*XYZ", true}, + { "xyzijkXYZ", "*ijk*", true}, + { NULL, NULL, false}, +}; + + +/** + * @brief test whether a key name in the above table are valid, can be added to the + * cfstore and found by a wildcard string + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_find_test_05_end(const size_t call_count) +{ + bool ret = false; + int32_t ret32 = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + cfstore_find_key_name_validate_t* node = cfstore_find_test_05_data; + + (void) call_count; + + while(node->key_name != NULL) + { + ret = cfstore_find_key_name_validate(node->key_name, node->match, node->f_allowed); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: test failed (ret=%d)\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret == true, cfstore_find_utest_msg_g); + node++; + } + + ret32 = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_find_utest_msg_g); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(400, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("FIND_test_00", cfstore_find_test_00), + Case("FIND_test_01", cfstore_find_test_01), + Case("FIND_test_02", cfstore_find_test_02), + Case("FIND_test_03_start", cfstore_utest_default_start), + Case("FIND_test_03_end", cfstore_find_test_03_end), + Case("FIND_test_04", cfstore_find_test_04), + Case("FIND_test_05_start", cfstore_utest_default_start), + Case("FIND_test_05_end", cfstore_find_test_05_end), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/find2/find2.cpp b/TESTS/cfstore/find2/find2.cpp new file mode 100644 index 0000000000..e0e02fcf5d --- /dev/null +++ b/TESTS/cfstore/find2/find2.cpp @@ -0,0 +1,310 @@ +/** @file find.cpp + * + * 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. + * + * Test cases to find KVs in the CFSTORE using the drv->Find() interface. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_find2_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("FIND2_test_00", cfstore_find2_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + + +#else + + +#include +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +//#include +#include +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static char cfstore_find2_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.find2.box1"); +UVISOR_BOX_CONFIG(cfstore_find2_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + +extern ARM_CFSTORE_DRIVER cfstore_driver; + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#define CFSTORE_FIND_MBED_HOSTTEST_TIMEOUT 60 +#endif + +void cfstore_find2_callback(int32_t status, ARM_CFSTORE_OPCODE cmd_code, void *client_context, ARM_CFSTORE_HANDLE handle) +{ + (void) status; + (void) cmd_code; + (void) client_context; + (void) handle; + return; +} + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_find2_test_00(const size_t call_count) +{ + (void) call_count; + CFSTORE_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", cfstore_driver.GetCapabilities().asynchronous_ops); + return CaseNext; +} + + +static control_t cfstore_find2_test_01(const size_t call_count) +{ + char keyBuffer[128] = "com.arm.mbed.manifest-manager.root.AQAAAAAAAAA-.manifest"; + int32_t rc; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_HANDLE_INIT(prev); + + // Initialize the config store + (void) call_count; + cfstore_driver.Initialize(cfstore_find2_callback, NULL); + cfstore_driver.PowerControl(ARM_POWER_FULL); + + // Find the target key + rc = cfstore_driver.Find(keyBuffer, prev, hkey); + + // If the target key was not found + if (rc == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND) { + ARM_CFSTORE_KEYDESC kdesc = { + .acl = { + .perm_owner_read = 1, + .perm_owner_write = 1, + .perm_owner_execute = 0, + .perm_other_read = 1, + .perm_other_write = 0, + .perm_other_execute = 0, + /* added initialisers */ + .reserved = 0 + }, .drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE, + .security = { + .acls = 1, + .rollback_protection = 0, + .tamper_proof = 0, + .internal_flash = 0, + /* added initialisers */ + .reserved1 = 0, + .software_attacks = 0, + .board_level_attacks = 0, + .chip_level_attacks = 0, + .side_channel_attacks = 0, + .reserved2 = 0 + }, + .flags = { + .continuous = 0, + .lazy_flush = 1, + .flush_on_close = 1, + .read = 0, + .write = 1, + .execute = 0, + .storage_detect = 1, + /* added initialisers */ + .reserved = 0 + } + }; + + // Create the target key + rc = cfstore_driver.Create(keyBuffer, 191, &kdesc, hkey); + } + return CaseNext; +} + + +/* fixed version of brendans code */ +#define CFSTORE_FIND2_TEST_02_VALUE_SIZE 191 +static control_t cfstore_find2_test_02(const size_t call_count) +{ + char keyBuffer[128] = "com.arm.mbed.manifest-manager.root.AQAAAAAAAAA-.manifest"; + + int32_t rc; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_HANDLE_INIT(prev); + ARM_CFSTORE_SIZE length; + char value[CFSTORE_FIND2_TEST_02_VALUE_SIZE]; + + // Initialize the config store + (void) call_count; + cfstore_driver.Initialize(NULL, NULL); /* non-null client_callback not supported for MBED_V_0_1_x */ + cfstore_driver.PowerControl(ARM_POWER_FULL); + memset(value, 0, 191); + + // Find the target key + rc = cfstore_driver.Find(keyBuffer, prev, hkey); + + // If the target key was not found + if (rc == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND) { + ARM_CFSTORE_KEYDESC kdesc = { + .acl = { + .perm_owner_read = 1, + .perm_owner_write = 1, + .perm_owner_execute = 0, + .perm_other_read = 1, + .perm_other_write = 0, + .perm_other_execute = 0, + .reserved = 0 + }, .drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE, /* DATA_RETENTION_NVM not supported for MBED_V_0_1_x */ + .security = { + .acls = 0, /* protection against internal software attacks using ACLs not supported for MBED_V_0_1_x */ + .rollback_protection = 0, + .tamper_proof = 0, + .internal_flash = 0, + .reserved1 = 0, + .software_attacks = 0, + .board_level_attacks = 0, + .chip_level_attacks = 0, + .side_channel_attacks = 0, + .reserved2 = 0 + }, + .flags = { + .continuous = 0, + .lazy_flush = 0, /* lazy flush not supported for MBED_V_0_1_x */ + .flush_on_close = 0, /* flush on close not supported for MBED_V_0_1_x */ + .read = 0, + .write = 1, + .execute = 0, + .storage_detect = 0, /* storage detect supported for MBED_V_0_1_x */ + /* added initialisers */ + .reserved = 0 + + } + }; + + // Create the target key + rc = cfstore_driver.Create(keyBuffer, 191, &kdesc, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%sError: failed to create key\n", __func__); + TEST_ASSERT_MESSAGE(rc >= ARM_DRIVER_OK, cfstore_find2_utest_msg_g); + + strncpy(value, "MyValueData", CFSTORE_FIND2_TEST_02_VALUE_SIZE); + length = strlen(value); + rc = cfstore_driver.Write(hkey, value, &length); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_find2_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%sError: failed to write key\n", __func__); + TEST_ASSERT_MESSAGE(rc >= ARM_DRIVER_OK, cfstore_find2_utest_msg_g); + printf("Success!\n"); + } + return CaseNext; +} + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(400, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("FIND2_test_00", cfstore_find2_test_00), + Case("FIND2_test_01", cfstore_find2_test_01), + Case("FIND2_test_02", cfstore_find2_test_02), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/flash/flash.cpp b/TESTS/cfstore/flash/flash.cpp new file mode 100644 index 0000000000..1f2dc7c6d6 --- /dev/null +++ b/TESTS/cfstore/flash/flash.cpp @@ -0,0 +1,752 @@ +/* @file flush.cpp + * + * 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. + * + * Test cases to flush KVs in the CFSTORE using the drv->Flush() interface. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_flash_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("FLASH_test_00", cfstore_flash_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + +#else + + + +#include +#include +#include +#include + +#if defined CFSTORE_CONFIG_BACKEND_FLASH_ENABLED && CFSTORE_CONFIG_BACKEND_FLASH_ENABLED == 1 +#include +#include +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +//#include +#include +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.flash.box1"); +UVISOR_BOX_CONFIG(cfstore_flash_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + + +/* shared code common to both sync and async test cases */ + +/* + * Defines + * + */ +#define CFSTORE_FREE free +#define CFSTORE_MALLOC malloc +#define CFSTORE_REALLOC realloc +#define CFSTORE_FLASH_STACK_BUF_SIZE 64 +#define CFSTORE_FLASH_K64F_CURRENT_PROGRAM_UNIT_SIZE 8 +#define CFSTORE_TEST_DATA_KEYNAME "com.arm.mbed.configurationstore.test.flush.cfstoreflushtest02" +#define CFSTORE_TEST_DATA_KEYNAME_SHORT "com.arm" +#define CFSTORE_TEST_DATA_VALUE_INIT "\1" +#define CFSTORE_TEST_DATA_KEYNAME_SIZE (sizeof(CFSTORE_TEST_DATA_KEYNAME) - 1) +#define CFSTORE_TEST_DATA_VALUE_SIZE (sizeof(CFSTORE_TEST_DATA_VALUE_INIT) - 1) +#define CFSTORE_FLASH_UTEST_MSG_BUF_SIZE 256 +#define CFSTORE_FLASH_MTD_ASYNC_OPS_ON 1 +#define CFSTORE_FLASH_MTD_ASYNC_OPS_OFF 0 +#define CFSTORE_FLASH_CASE_TIMEOUT_MS 5000 + +/* + * Globals + */ + +#if defined CFSTORE_CONFIG_BACKEND_FLASH_ENABLED && CFSTORE_CONFIG_BACKEND_FLASH_ENABLED == 1 +char cfstore_flash_utest_msg_g[CFSTORE_FLASH_UTEST_MSG_BUF_SIZE]; +uint16_t cfstore_flash_mtd_async_ops_g = 0; +extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0); + +/* KV data for test_01 */ +static cfstore_kv_data_t cfstore_flush_test_01_kv_data[] = { + { CFSTORE_TEST_DATA_KEYNAME, CFSTORE_TEST_DATA_VALUE_INIT}, + { NULL, NULL}, +}; + + +/* @brief key value header structure defining key_name length, value length + * @note + * 8 bytes long */ +typedef struct cfstore_area_header_t +{ + uint32_t vlength; + uint8_t klength; + uint8_t perm_owner_read : 1; + uint8_t perm_owner_write : 1; + uint8_t perm_owner_execute : 1; + uint8_t perm_other_read : 1; + uint8_t perm_other_write : 1; + uint8_t perm_other_execute : 1; + uint8_t reserved : 2; + uint8_t refcount; + struct flags_t { + uint8_t deleting : 1; + uint8_t reserved : 7; + } flags ; +} cfstore_area_header_t; + + +/* @brief data structure for managing test data */ +typedef struct cfstore_flash_data_blob_t { + cfstore_area_header_t hdr; + uint8_t data[CFSTORE_TEST_DATA_KEYNAME_SIZE + CFSTORE_TEST_DATA_VALUE_SIZE]; /* 61 bytes for key_name, 1 byte for value */ +} cfstore_flash_data_blob_t; + +/* + * Defines + * + * CFSTORE_FLASH_AREA_SIZE_MIN + * 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 + */ +#define CFSTORE_FLASH_AREA_SIZE_MIN (sizeof(cfstore_area_header_t) - 1) + +/* + * Shared implementation between sync and async tests + */ + +/* print key name string from area where key_name is not null terminated*/ +static void cfstore_dump_key_name(uint8_t* keyname, uint8_t len, const char* tag) +{ + char blob_data[CFSTORE_KEY_NAME_MAX_LENGTH]; + + (void) tag; + assert(keyname != NULL); + assert(tag != NULL); + assert(len > 0); + memcpy(blob_data, keyname, len); + blob_data[len] = '\0'; + CFSTORE_DBGLOG("%s:keyname=%s\r\n", tag, blob_data); +} + +/* @brief test fsm states and events */ +typedef enum cfstore_flash_fsm_state_t { + cfstore_flash_fsm_state_initializing = 0, + cfstore_flash_fsm_state_reading, + cfstore_flash_fsm_state_writing, + cfstore_flash_fsm_state_committing, + cfstore_flash_fsm_state_max +} cfstore_flash_fsm_state_t; + +/* @brief test fsm events */ +typedef enum cfstore_flash_fsm_event_t { + cfstore_flash_fsm_event_init_done = 0, + cfstore_flash_fsm_event_read_done, + cfstore_flash_fsm_event_write_done, + cfstore_flash_fsm_event_commit_done, + cfstore_flash_fsm_event_max, +} cfstore_flash_fsm_event_t; + +typedef void (*cfstore_flash_fsm_handler)(void* ctx); + +typedef struct cfstore_fsm_t +{ + cfstore_flash_fsm_state_t state; + cfstore_flash_fsm_event_t event; +} cfstore_fsm_t; + +typedef struct cfstore_flash_ctx_t +{ + uint8_t* area_0_head; + uint8_t* area_0_tail; + FlashJournal_t jrnl; + uint64_t expected_blob_size; + cfstore_fsm_t fsm; + int32_t status; + FlashJournal_OpCode_t cmd_code; + uint64_t expected_read_size; +} cfstore_flash_ctx_t; + +/* + * Globals + */ +static cfstore_flash_ctx_t cfstore_flash_ctx_g; +static const char* cfstore_flash_opcode_str[] = +{ + "FLASH_JOURNAL_OPCODE_INITIALIZE", + "FLASH_JOURNAL_OPCODE_GET_INFO", + "FLASH_JOURNAL_OPCODE_READ_BLOB", + "FLASH_JOURNAL_OPCODE_LOG_BLOB", + "FLASH_JOURNAL_OPCODE_COMMIT", + "FLASH_JOURNAL_OPCODE_RESET", +}; + +#ifdef CFSTORE_DEBUG +static const char* cfstore_flash_state_str[] = +{ + "initializing", + "reading", + "writing", + "committing", + "unknown" +}; + +static const char* cfstore_flash_event_str[] = +{ + "init_done", + "read_done", + "write_done", + "commit_done", + "unknown" +}; +#endif /* CFSTORE_DEBUG */ + +/* + * Forward decl + */ +static void cfstore_fsm_state_handle_event(cfstore_fsm_t* fsm, cfstore_flash_fsm_event_t event, void* context); +static void cfstore_fsm_state_set(cfstore_fsm_t* fsm, cfstore_flash_fsm_state_t new_state, void* ctx); + +/* + * context related methods + */ + +/* @brief get a pointer to the global context data structure */ +static cfstore_flash_ctx_t* cfstore_flash_ctx_get(void) +{ + return &cfstore_flash_ctx_g; +} + + +/* @brief flash journal asynchronous callback handler */ +void cfstore_flash_test_01_callback(int32_t status, FlashJournal_OpCode_t cmd_code) +{ + cfstore_flash_ctx_t* ctx = cfstore_flash_ctx_get(); + + CFSTORE_FENTRYLOG("%s:entered: status=%d, cmd_code=%d (%s)\r\n", __func__, (int) status, (int) cmd_code, cfstore_flash_opcode_str[cmd_code]); + switch(cmd_code) + { + case FLASH_JOURNAL_OPCODE_INITIALIZE: + ctx->fsm.event = cfstore_flash_fsm_event_init_done; + break; + case FLASH_JOURNAL_OPCODE_READ_BLOB: + ctx->fsm.event = cfstore_flash_fsm_event_read_done; + break; + case FLASH_JOURNAL_OPCODE_LOG_BLOB: + ctx->fsm.event = cfstore_flash_fsm_event_write_done; + break; + case FLASH_JOURNAL_OPCODE_COMMIT: + ctx->fsm.event = cfstore_flash_fsm_event_commit_done; + break; + case FLASH_JOURNAL_OPCODE_GET_INFO: + case FLASH_JOURNAL_OPCODE_RESET: + default: + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: received asynchronous notification for opcode=%d (%s) when api call should have been synchronous", __func__, cmd_code, cmd_code <= FLASH_JOURNAL_OPCODE_RESET ? cfstore_flash_opcode_str[cmd_code] : "unknown"); + TEST_ASSERT_MESSAGE(false, cfstore_flash_utest_msg_g) + return; + } + ctx->status = status; + ctx->cmd_code = cmd_code; + cfstore_fsm_state_handle_event(&ctx->fsm, ctx->fsm.event, (void*) ctx); + return; +} + + +/* @brief fsm handler called on entry to initializing state */ +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); + FlashJournal_Info_t info; + FlashJournal_Status_t status = JOURNAL_STATUS_ERROR; + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + /* check that the mtd is in synchronous mode */ + CFSTORE_FENTRYLOG("%s:entered: \r\n", __func__); + memset(&info, 0, sizeof(info)); + + /* FlashJournal_initialize() is potentially asynchronous */ + status = (FlashJournal_Status_t) FlashJournal_initialize(&ctx->jrnl, drv, &FLASH_JOURNAL_STRATEGY_SEQUENTIAL, cfstore_flash_test_01_callback); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialize flash journaling layer (status=%d)\r\n", __func__, status); + TEST_ASSERT_MESSAGE(status >= JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + /* if status > 0, expect async callback, otherwise initialisation has been completed */ + if(status > 0) { + cfstore_flash_test_01_callback(status, FLASH_JOURNAL_OPCODE_INITIALIZE); + } + return; +} + +/* brief callback handler when in state initializing */ +static void cfstore_flash_fsm_initializing(void* context) +{ + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in initializing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flash_fsm_state_initializing, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: cmd_code code (%d) is not as expected (%d)\r\n", __func__, ctx->cmd_code, FLASH_JOURNAL_OPCODE_INITIALIZE); + TEST_ASSERT_MESSAGE(ctx->cmd_code == FLASH_JOURNAL_OPCODE_INITIALIZE, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: status=%" PRId32 "\r\n", __func__, ctx->status); + TEST_ASSERT_MESSAGE(ctx->status >= JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + /* only change state if status > 0*/ + if(ctx->status > 0){ + cfstore_fsm_state_set(&ctx->fsm, cfstore_flash_fsm_state_reading, ctx); + } +} + +/* static void cfstore_flash_fsm_init_on_exit(void* context){ (void) context;} */ + +/* brief callback handler called when entering the reading state + * note + * flash journal has initialised successfully. now + */ +static void cfstore_flash_fsm_read_on_entry(void* context) +{ + uint8_t* ptr = NULL; + int32_t ret = 0; + FlashJournal_Info_t info; + FlashJournal_Status_t status = JOURNAL_STATUS_ERROR; + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + CFSTORE_ASSERT(ctx != NULL); + /* drv->GetInfo() is synchronous */ + status = FlashJournal_getInfo(&ctx->jrnl, &info); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: failed get journal info (status=%d)\r\n", __func__, (int) status); + TEST_ASSERT_MESSAGE(status == JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + CFSTORE_DBGLOG("%s:FlashJournal_getInfo() done. info.sizeofJournaledBlob=%lu\r\n", __func__, (long unsigned int) info.sizeofJournaledBlob); + + if(info.sizeofJournaledBlob > 0) + { + /* setup the expected blob size for writing + * This is a multiple of program unit so the write doesnt fail due to unaligned log */ + ctx->expected_blob_size = sizeof(cfstore_flash_data_blob_t); + if(ctx->expected_blob_size % CFSTORE_FLASH_K64F_CURRENT_PROGRAM_UNIT_SIZE > 0){ + ctx->expected_blob_size += (CFSTORE_FLASH_K64F_CURRENT_PROGRAM_UNIT_SIZE - (sizeof(cfstore_flash_data_blob_t) % CFSTORE_FLASH_K64F_CURRENT_PROGRAM_UNIT_SIZE)); + } + /* test that a blob size is the expected size for flash data that has been written before */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: info.sizeofJournaledBlob does not match expect size. expected_blob_size (%lu) != info.sizeofJournaledBlob (%lu)\r\n", __func__, (unsigned long int) ctx->expected_blob_size, (unsigned long int) info.sizeofJournaledBlob); + TEST_ASSERT_EQUAL_INT64_MESSAGE(ctx->expected_blob_size, info.sizeofJournaledBlob, cfstore_flash_utest_msg_g); + + /* grow the area by the size of the stored blob */ + ptr = (uint8_t*) CFSTORE_REALLOC((void*) ctx->area_0_head, ctx->expected_blob_size); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:realloc failed flash blob (size=%d)\r\n", __func__, (int)info.sizeofJournaledBlob); + TEST_ASSERT_MESSAGE(ptr != NULL, cfstore_flash_utest_msg_g); + memset(ptr, 0, ctx->expected_blob_size); + if(ptr != ctx->area_0_head){ + CFSTORE_DBGLOG("%s:cfstore_ctx_g.area_0_head pointer changed (cfstore_ctx_g.area_0_head=%p, ptr=%p)\r\n", __func__, ctx->area_0_head, ptr); + ctx->area_0_head = ptr; + ctx->area_0_tail = ctx->area_0_head + info.sizeofJournaledBlob; + } + ret = FlashJournal_read(&ctx->jrnl, (void*) ctx->area_0_head, info.sizeofJournaledBlob); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: unable to read flash journal (ret=%" PRId32 ". info.sizeofJournaledBlob=%d)\r\n", __func__, ret, (int) info.sizeofJournaledBlob); + TEST_ASSERT_MESSAGE(ret >= JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + if(ret > 0){ + /* read has completed synchronously*/ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: unable to read all data from flash journal (expected size=%lu, read=%" PRId32 ")\r\n", __func__, (unsigned long int)info.sizeofJournaledBlob, ret); + TEST_ASSERT_EQUAL_INT32_MESSAGE( (int32_t) info.sizeofJournaledBlob, ret, cfstore_flash_utest_msg_g); + cfstore_flash_test_01_callback(ret, FLASH_JOURNAL_OPCODE_READ_BLOB); + } + } else { + /* there is no blob, move to next state. need a +ve status value to indicate async completion + * to the fsm reading state handler. use CFSTORE_FLASH_AREA_SIZE_MIN for this value */ + ctx->expected_blob_size = CFSTORE_FLASH_AREA_SIZE_MIN; + status = (FlashJournal_Status_t) CFSTORE_FLASH_AREA_SIZE_MIN; + cfstore_flash_test_01_callback(status, FLASH_JOURNAL_OPCODE_READ_BLOB); + } + return; +} + +/* @brief fsm handler when in reading state */ +void cfstore_flash_fsm_reading(void* context) +{ + int32_t ret = 0; + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + cfstore_flash_data_blob_t *blob = NULL; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in reading state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flash_fsm_state_reading, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: cmd_code code (%d) is not as expected (%d)\r\n", __func__, ctx->cmd_code, FLASH_JOURNAL_OPCODE_READ_BLOB); + TEST_ASSERT_MESSAGE(ctx->cmd_code == FLASH_JOURNAL_OPCODE_READ_BLOB, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: status=%" PRId32 "\r\n", __func__, ctx->status); + TEST_ASSERT_MESSAGE(ctx->status >= JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + + if(ctx->status > 0) + { + if(ctx->status > (int32_t) CFSTORE_FLASH_AREA_SIZE_MIN) + { + /* check the correct amount of data was read, which is the status code */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: unable to read all data from flash journal (expected size=%lu, read=%" PRId32 ")\r\n", __func__, (unsigned long int) ctx->expected_blob_size, ctx->status); + /* ctx->status contains the status of the read that was completed */ + TEST_ASSERT_EQUAL_INT64_MESSAGE(ctx->expected_blob_size, (int32_t) ctx->status, cfstore_flash_utest_msg_g); + if(ctx->area_0_head != NULL) + { + /* check the key_name read from flash is correct */ + blob = (cfstore_flash_data_blob_t*) ctx->area_0_head; + cfstore_dump_key_name(blob->data, CFSTORE_TEST_DATA_KEYNAME_SIZE, __func__); + ret = memcmp(blob->data, cfstore_flush_test_01_kv_data[0].key_name, strlen(cfstore_flush_test_01_kv_data[0].key_name)); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: incorrect key_name read from flash (expected 0 from memcpy(keyname, flash_data), actual was non-zero)", __func__); + TEST_ASSERT_EQUAL_INT_MESSAGE(0, ret, cfstore_flash_utest_msg_g); + } + } + cfstore_fsm_state_set(&ctx->fsm, cfstore_flash_fsm_state_writing, ctx); + } +} + +/* void cfstore_flash_fsm_read_on_exit(void* context){ (void) context;} */ + +/* @brief on entry to writing state, update value */ +void cfstore_flash_fsm_write_on_entry(void* context) +{ + uint8_t value = 0; + int32_t ret = 0; + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + cfstore_flash_data_blob_t *blob = NULL; + + CFSTORE_FENTRYLOG("%s:entered:\r\n", __func__); + /* allocate memory for data if not already done so */ + if(ctx->area_0_head == NULL) + { + /* This is a multiple of program unit so the write doesnt fail due to unaligned log */ + ctx->expected_blob_size = sizeof(cfstore_flash_data_blob_t); + if(ctx->expected_blob_size % CFSTORE_FLASH_K64F_CURRENT_PROGRAM_UNIT_SIZE > 0){ + ctx->expected_blob_size += (CFSTORE_FLASH_K64F_CURRENT_PROGRAM_UNIT_SIZE - (sizeof(cfstore_flash_data_blob_t) % CFSTORE_FLASH_K64F_CURRENT_PROGRAM_UNIT_SIZE)); + } + ctx->area_0_head = (uint8_t*) CFSTORE_REALLOC((void*) ctx->area_0_head, ctx->expected_blob_size); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: unable to allocate memory for context\r\n", __func__); + TEST_ASSERT_MESSAGE(ctx->area_0_head != NULL, cfstore_flash_utest_msg_g); + ctx->area_0_tail = ctx->area_0_head + ctx->expected_blob_size; + memset(ctx->area_0_head, 0, ctx->expected_blob_size); + /* setup data to write to flash */ + blob = (cfstore_flash_data_blob_t*) ctx->area_0_head; + blob->hdr.klength = strlen(cfstore_flush_test_01_kv_data->key_name); + blob->hdr.vlength= 1; + blob->hdr.perm_owner_read = true; + blob->hdr.perm_owner_write = true; + blob->hdr.refcount = 1; + memcpy((void*) blob->data, (const void*) cfstore_flush_test_01_kv_data->key_name, strlen(cfstore_flush_test_01_kv_data->key_name)); + memcpy((void*) &blob->data[CFSTORE_TEST_DATA_KEYNAME_SIZE], (const void*) cfstore_flush_test_01_kv_data->value, strlen(cfstore_flush_test_01_kv_data->value)); + } + if(ctx->area_0_head != NULL) + { + /* data has been read */ + /* check the key_name read from flash is correct */ + blob = (cfstore_flash_data_blob_t*) ctx->area_0_head; + value = (uint8_t) blob->data[CFSTORE_TEST_DATA_KEYNAME_SIZE]; + printf("INFO: value read from flash = %u\r\n", value); + /* update the value */ + value++; + memcpy((void*) &blob->data[CFSTORE_TEST_DATA_KEYNAME_SIZE], (const void*) &value, sizeof(uint8_t)); + } + + ret = FlashJournal_log(&ctx->jrnl, (const void*) ctx->area_0_head, ctx->expected_blob_size); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: ret = JOURNAL_STATUS_SMALL_LOG_REQUEST, bailing out.", __func__); + TEST_ASSERT_MESSAGE(ret != (int32_t) JOURNAL_STATUS_SMALL_LOG_REQUEST, cfstore_flash_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform log operation to flash journal (ret=%" PRId32 ")\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= 0, cfstore_flash_utest_msg_g); + if(ret > 0){ + /* write has completed synchronously*/ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: unable to write all data from flash journal (expected size=%lu, read=%d)\r\n", __func__, (unsigned long int) ctx->expected_blob_size, (int) ret); + TEST_ASSERT_EQUAL_INT32_MESSAGE(ret, (int32_t) ctx->expected_blob_size, cfstore_flash_utest_msg_g); + cfstore_flash_test_01_callback(ret, FLASH_JOURNAL_OPCODE_LOG_BLOB); + } + /* wait for async completion handler*/ +} + +/* @brief fsm handler when in reading state */ +void cfstore_flash_fsm_writing(void* context) +{ + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered:\r\n", __func__); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in writing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flash_fsm_state_writing, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: cmd_code code (%d) is not as expected (%d)\r\n", __func__, ctx->cmd_code, FLASH_JOURNAL_OPCODE_LOG_BLOB); + TEST_ASSERT_MESSAGE(ctx->cmd_code == FLASH_JOURNAL_OPCODE_LOG_BLOB, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: status=%" PRId32 "\r\n", __func__, ctx->status); + TEST_ASSERT_MESSAGE(ctx->status >= JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + + if(ctx->status > 0){ + /* check the correct amount of data was written */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: unable to write all data from flash journal (expected size=%lu, read=%" PRId32 ")\r\n", __func__, (unsigned long int) ctx->expected_blob_size, ctx->status); + TEST_ASSERT_EQUAL_INT64_MESSAGE(ctx->expected_blob_size, ctx->status, cfstore_flash_utest_msg_g); + cfstore_fsm_state_set(&ctx->fsm, cfstore_flash_fsm_state_committing, ctx); + } + return; +} + +/* void cfstore_flash_fsm_write_on_exit(void* ctx){(void) ctx;} */ + +/* @brief fsm handler when entering committing state */ +void cfstore_flash_fsm_commit_on_entry(void* context) +{ + int32_t ret = 0; + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered:\r\n", __func__); + ret = FlashJournal_commit(&ctx->jrnl); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to perform commit operation to flash journal (ret=%d)\r\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= (int32_t) JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + /* for flash-journal-strategy-sequential version >0.4.0, commit() return no longer reports size of commit block */ + if(ret > 0){ + Harness::validate_callback(); + } + /* wait for async completion handler*/ + return; +} + + +/* @brief fsm handler when in committing state */ +void cfstore_flash_fsm_committing(void* context) +{ + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in committing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flash_fsm_state_committing, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: cmd_code code (%d) is not as expected (%d)\r\n", __func__, ctx->cmd_code, FLASH_JOURNAL_OPCODE_COMMIT); + TEST_ASSERT_MESSAGE(ctx->cmd_code == FLASH_JOURNAL_OPCODE_COMMIT, cfstore_flash_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: status=%" PRId32 "\r\n", __func__, ctx->status); + TEST_ASSERT_MESSAGE(ctx->status >= JOURNAL_STATUS_OK, cfstore_flash_utest_msg_g); + + /* check the correct amount of data was written */ + /* for flash-journal-strategy-sequential version >0.4.0, commit() return no longer reports size of commit block */ + Harness::validate_callback(); +} + +/* @brief fsm handler when exiting committing state */ +void cfstore_flash_fsm_commit_on_exit(void* context) +{ + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + /* test done. clean up*/ + if(ctx->area_0_head){ + CFSTORE_FREE(ctx->area_0_head); + ctx->area_0_head = NULL; + } + Harness::validate_callback(); + } + +#define cfstore_flash_fsm_null NULL + +/* handler functions while in state */ +static cfstore_flash_fsm_handler cfstore_flash_fsm[cfstore_flash_fsm_state_max][cfstore_flash_fsm_event_max] = +{ +/* state\event: init_done read_done write_done commit_done */ +/* initialising */ {cfstore_flash_fsm_initializing, cfstore_flash_fsm_null, cfstore_flash_fsm_null, cfstore_flash_fsm_null }, +/* reading */ {cfstore_flash_fsm_null, cfstore_flash_fsm_reading, cfstore_flash_fsm_null, cfstore_flash_fsm_null }, +/* writing */ {cfstore_flash_fsm_null, cfstore_flash_fsm_null, cfstore_flash_fsm_writing, cfstore_flash_fsm_null }, +/* committing */ {cfstore_flash_fsm_null, cfstore_flash_fsm_null, cfstore_flash_fsm_null, cfstore_flash_fsm_committing }, +}; + + +/* handler functions for entering the state*/ +cfstore_flash_fsm_handler cfstore_flash_fsm_on_entry[cfstore_flash_fsm_state_max] = +{ + cfstore_flash_fsm_init_on_entry, + cfstore_flash_fsm_read_on_entry, + cfstore_flash_fsm_write_on_entry, + cfstore_flash_fsm_commit_on_entry, +}; + +/* handler functions for exiting state, currently none used */ +cfstore_flash_fsm_handler cfstore_flash_fsm_on_exit[cfstore_flash_fsm_state_max] = +{ + NULL, NULL, NULL, NULL, +}; + + +/* @brief inject event into fsm */ +static void cfstore_fsm_state_handle_event(cfstore_fsm_t* fsm, cfstore_flash_fsm_event_t event, void* context) +{ + cfstore_flash_ctx_t* ctx = (cfstore_flash_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered: fsm=%p, event=%d (%s), ctx=%p\r\n", __func__, fsm, (int) event, cfstore_flash_event_str[event], ctx); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flash_utest_msg_g, CFSTORE_FLASH_UTEST_MSG_BUF_SIZE, "%s:Error: invalid event (%d)\r\n", __func__, (int) event); + TEST_ASSERT_MESSAGE(event < cfstore_flash_fsm_event_max, cfstore_flash_utest_msg_g); + fsm->event = event; + if(cfstore_flash_fsm[fsm->state][fsm->event] != NULL){ + cfstore_flash_fsm[fsm->state][fsm->event](ctx); + } + + /* do not clear context data set by caller as it may be used later + * fsm->event = cfstore_flash_fsm_event_max; + * ctx->status = 0; + * ctx->cmd_code = (FlashJournal_OpCode_t)((int) FLASH_JOURNAL_OPCODE_RESET+1); + */ + return; +} + + +/* @brief function to move to new fsm state, calling state exit function for old state and entry function for new state */ +static void cfstore_fsm_state_set(cfstore_fsm_t* fsm, cfstore_flash_fsm_state_t new_state, void* ctx) +{ + CFSTORE_FENTRYLOG("%s:entered: fsm=%p, new_state=%d, ctx=%p\r\n", __func__, fsm, (int) new_state, ctx); + CFSTORE_DBGLOG("%s:FSM:REQ RX:%s:%s\r\n", __func__, cfstore_flash_state_str[fsm->state], cfstore_flash_state_str[new_state]); + TEST_ASSERT_MESSAGE(fsm != NULL, "fsm is not a valid pointer"); + TEST_ASSERT_MESSAGE(new_state < cfstore_flash_fsm_state_max, "new_state is not a valid state"); + TEST_ASSERT_MESSAGE(ctx != NULL, "ctx is not a valid pointer"); + TEST_ASSERT_MESSAGE(fsm->state < cfstore_flash_fsm_state_max, "fsm->state is not a valid state"); + + if(cfstore_flash_fsm_on_exit[fsm->state] != NULL){ + cfstore_flash_fsm_on_exit[fsm->state](ctx); + } + fsm->state = new_state; + if(cfstore_flash_fsm_on_entry[new_state] != NULL){ + cfstore_flash_fsm_on_entry[new_state](ctx); + } + CFSTORE_DBGLOG("%s:FSM:REQ DONE:\r\n", __func__); + return; +} + +/* @brief initialize global context data structure */ +static void cfstore_flash_ctx_init(cfstore_flash_ctx_t* ctx) +{ + TEST_ASSERT_MESSAGE(ctx != NULL, "ctx is NULL"); + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + memset(&cfstore_flash_ctx_g, 0, sizeof(cfstore_flash_ctx_g)); +} + + +/* @brief asynchronous test 01 */ +static control_t cfstore_flash_journal_async_test_01(void) +{ + cfstore_flash_ctx_t* ctx = cfstore_flash_ctx_get(); + + CFSTORE_FENTRYLOG("%s:entered: \r\n", __func__); + cfstore_flash_mtd_async_ops_g = CFSTORE_FLASH_MTD_ASYNC_OPS_ON; + cfstore_flash_ctx_init(ctx); + cfstore_fsm_state_set(&ctx->fsm, cfstore_flash_fsm_state_initializing, ctx); + + /* allow time for work to be done */ + return CaseTimeout(CFSTORE_FLASH_CASE_TIMEOUT_MS); +} + +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_flash_test_00(const size_t call_count) +{ + (void) call_count; + CFSTORE_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", cfstore_driver.GetCapabilities().asynchronous_ops); +#ifdef YOTTA_CFG_CFSTORE_BACKEND_SRAM + CFSTORE_LOG("INITIALIZING: BACKEND=SRAM. Skipping flash test%s", "\n"); +#endif + return CaseNext; +} + +/* Specify all your test cases here */ +Case cases[] = { + Case("flash_journal_async_test_00", cfstore_flash_test_00), +#if defined CFSTORE_CONFIG_BACKEND_FLASH_ENABLED && CFSTORE_CONFIG_BACKEND_FLASH_ENABLED == 1 + Case("flash_journal_async_test_01", cfstore_flash_journal_async_test_01), +#endif +}; + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/flush/flush.cpp b/TESTS/cfstore/flush/flush.cpp new file mode 100644 index 0000000000..177789c336 --- /dev/null +++ b/TESTS/cfstore/flush/flush.cpp @@ -0,0 +1,638 @@ +/* @file flush.cpp + * + * 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. + * + * Test cases to flush KVs in the CFSTORE using the drv->Flush() interface. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_flush_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("FLUSH_test_00", cfstore_flush_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + +#else + + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +//#include +#include +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +/* + * Defines + */ +#define CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE 256 +#define cfstore_flush_fsm_null NULL +#define CFSTORE_FLUSH_CASE_TIMEOUT_MS 10000 +#define CFSTORE_FLUSH_FSM_LOOPS 20 + +#ifdef CFSTORE_DEBUG +#define CFSTORE_FLUSH_GREENTEA_TIMEOUT_S 360 +#else +#define CFSTORE_FLUSH_GREENTEA_TIMEOUT_S 60 +#endif + +/* + * Globals + * + * cfstore_flush_utest_msg_g + * buffer for storing TEST_ASSERT_xxx_MESSAGE messages + */ +char cfstore_flush_utest_msg_g[CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.flush.box1"); +UVISOR_BOX_CONFIG(cfstore_flush_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + + + +#ifdef TARGET_LIKE_X86_LINUX_NATIVE + +/** @brief basic Flush() test + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret != ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static int32_t cfstore_flush_test_01_x86_sync(void) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_LOG("cfstore_flush_test_01: Start%s", "\r\n"); + ret = drv->Initialize(NULL, NULL); + if(ret != ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Initialize() call failed (ret=%" PRId32 ").\r\n", __func__, ret); + goto out0; + } + ret = drv->Flush(); + if(ret != ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Flush() call failed (ret=%" PRId32 ").\r\n", __func__, ret); + } + ret = drv->Uninitialize(); + if(ret != ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Initialize() call failed to Uninitialise(ret=%" PRId32 ").\r\n", __func__, ret); + goto out0; + } + out0: + if(ret == ARM_DRIVER_OK){ + CFSTORE_LOG("cfstore_flush_test_01: End: Success%s", "\r\n"); + } else { + CFSTORE_LOG("cfstore_flush_test_01: End: Failed%s", "\r\n"); + } + return ret; +} +#endif /* TARGET_LIKE_X86_LINUX_NATIVE */ + +/* KV data for test_03 */ +static cfstore_kv_data_t cfstore_flush_test_02_kv_data[] = { + { "com.arm.mbed.configurationstore.test.flush.cfstoreflushtest02", "1"}, + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + { NULL, NULL}, +}; + + +/* async test version */ + +/* @brief test fsm states and events */ +typedef enum cfstore_flush_fsm_state_t { + cfstore_flush_fsm_state_stopped = 0, + cfstore_flush_fsm_state_initializing, + cfstore_flush_fsm_state_flushing, + cfstore_flush_fsm_state_uninitializing, + cfstore_flush_fsm_state_max +} cfstore_flush_fsm_state_t; + +/* @brief test fsm events */ +typedef enum cfstore_flush_fsm_event_t { + cfstore_flush_fsm_event_init_done = 0, + cfstore_flush_fsm_event_flush_done, + cfstore_flush_fsm_event_uninit_done, + cfstore_flush_fsm_event_max, +} cfstore_flush_fsm_event_t; + +typedef void (*cfstore_flush_fsm_handler)(void* ctx); + +typedef struct cfstore_fsm_t +{ + cfstore_flush_fsm_state_t state; + cfstore_flush_fsm_event_t event; +} cfstore_fsm_t; + +typedef struct cfstore_flush_ctx_t +{ + int32_t loops_done; + cfstore_fsm_t fsm; + int32_t status; + ARM_CFSTORE_OPCODE cmd_code; +} cfstore_flush_ctx_t; + + +/* + * Globals + */ +static cfstore_flush_ctx_t cfstore_flush_ctx_g; + +#ifdef CFSTORE_DEBUG +static const char* cfstore_flush_state_str[] = +{ + "stopped", + "initializing", + "flushing", + "uninitializing", + "unknown" +}; + +static const char* cfstore_flush_event_str[] = +{ + "init_done", + "flush_done", + "uninit_done", + "unknown" +}; +#endif + +/* + * Forward decl + */ +static void cfstore_flush_fsm_state_handle_event(cfstore_fsm_t* fsm, cfstore_flush_fsm_event_t event, void* context); +static void cfstore_flush_fsm_state_set(cfstore_fsm_t* fsm, cfstore_flush_fsm_state_t new_state, void* ctx); +/* + * context related methods + */ + +/* @brief get a pointer to the global context data structure */ +static cfstore_flush_ctx_t* cfstore_flush_ctx_get(void) +{ + return &cfstore_flush_ctx_g; +} + +/* @brief initialize global context data structure */ +static void cfstore_flush_ctx_init(cfstore_flush_ctx_t* ctx) +{ + TEST_ASSERT_MESSAGE(ctx != NULL, "ctx is NULL"); + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + memset(&cfstore_flush_ctx_g, 0, sizeof(cfstore_flush_ctx_g)); +} + + +/* @brief cfstore asynchronous callback handler */ +void cfstore_flush_test_01_callback(int32_t status, ARM_CFSTORE_OPCODE cmd_code, void *client_context, ARM_CFSTORE_HANDLE handle) +{ + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) client_context; + + CFSTORE_FENTRYLOG("%s:entered: status=%d, cmd_code=%d (%s) ctx=%p, handle=%p\r\n", __func__, (int) status, (int) cmd_code, cfstore_test_opcode_str[cmd_code], ctx, handle); + (void) handle; + switch(cmd_code) + { + case CFSTORE_OPCODE_INITIALIZE: + ctx->fsm.event = cfstore_flush_fsm_event_init_done; + break; + case CFSTORE_OPCODE_FLUSH: + ctx->fsm.event = cfstore_flush_fsm_event_flush_done; + break; + case CFSTORE_OPCODE_UNINITIALIZE: + ctx->fsm.event = cfstore_flush_fsm_event_uninit_done; + break; + case CFSTORE_OPCODE_CLOSE: + case CFSTORE_OPCODE_CREATE: + case CFSTORE_OPCODE_DELETE: + case CFSTORE_OPCODE_FIND: + case CFSTORE_OPCODE_GET_KEY_NAME: + case CFSTORE_OPCODE_GET_STATUS: + case CFSTORE_OPCODE_GET_VALUE_LEN: + case CFSTORE_OPCODE_OPEN: + case CFSTORE_OPCODE_POWER_CONTROL: + case CFSTORE_OPCODE_READ: + case CFSTORE_OPCODE_RSEEK: + case CFSTORE_OPCODE_WRITE: + default: + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:WARN: received asynchronous notification for opcode=%d (%s) when api call should have been synchronous", __func__, cmd_code, cmd_code < CFSTORE_OPCODE_MAX ? cfstore_test_opcode_str[cmd_code] : "unknown"); + CFSTORE_DBGLOG("%s:WARN: received asynchronous notification for opcode=%d (%s) when api call should have been synchronous", __func__, cmd_code, cmd_code < CFSTORE_OPCODE_MAX ? cfstore_test_opcode_str[cmd_code] : "unknown"); + /* TEST_ASSERT_MESSAGE(false, cfstore_flush_utest_msg_g) */ + return; + } + ctx->status = status; + ctx->cmd_code = cmd_code; + cfstore_flush_fsm_state_handle_event(&ctx->fsm, ctx->fsm.event, client_context); + return; +} + +/* @brief fsm handler called on entry to stopping state */ +static void cfstore_flush_fsm_stop_on_entry(void* context) +{ + (void) context; + CFSTORE_FENTRYLOG("%s:entered:\r\n", __func__); + Harness::validate_callback(); + return; +} + +/* @brief fsm handler called on entry to initializing state */ +static void cfstore_flush_fsm_init_on_entry(void* context) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) context; + const ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + /* check that the mtd is in synchronous mode */ + CFSTORE_FENTRYLOG("%s:entered: callback=%p, ctx=%p\r\n", __func__, cfstore_flush_test_01_callback, ctx); + ret = drv->Initialize(cfstore_flush_test_01_callback, ctx); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialize CFSTORE (ret=%" PRId32 ")\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + CFSTORE_DBGLOG("%s:debug: ret=%" PRId32 "\r\n", __func__, ret); + return; +} + +/* brief callback handler when in state initializing */ +static void cfstore_flush_fsm_initializing(void* context) +{ + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + CFSTORE_FENTRYLOG("%s:entered: status = %d\r\n", __func__, (int) ctx->status); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in initializing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flush_fsm_state_initializing, cfstore_flush_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: cmd_code code (%d) is not as expected (%d)\r\n", __func__, ctx->cmd_code, CFSTORE_OPCODE_INITIALIZE); + TEST_ASSERT_MESSAGE(ctx->cmd_code == CFSTORE_OPCODE_INITIALIZE, cfstore_flush_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: status=%" PRId32 "\r\n", __func__, ctx->status); + TEST_ASSERT_MESSAGE(ctx->status >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + /* only change state if status >= 0*/ + if(ctx->status >= 0){ + cfstore_flush_fsm_state_set(&ctx->fsm, cfstore_flush_fsm_state_flushing, ctx); + } + return; +} + +/* static void cfstore_flush_fsm_init_on_exit(void* context){ (void) context;} */ + + +/* @brief fsm handler called on entry to flushing state */ +static void cfstore_flush_fsm_flush_on_entry(void* context) +{ + bool bfound = false; + int32_t ivalue = 0; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + const char* key_name_query = "*"; + char value[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + ARM_CFSTORE_SIZE len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + ARM_CFSTORE_HANDLE_INIT(next); + ARM_CFSTORE_HANDLE_INIT(prev); + ARM_CFSTORE_KEYDESC kdesc; + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) context; + + /* check that the mtd is in synchronous mode */ + CFSTORE_FENTRYLOG("%s:entered: \r\n", __func__); + memset(&kdesc, 0, sizeof(kdesc)); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in flushing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flush_fsm_state_flushing, cfstore_flush_utest_msg_g); + /* try to read key; should not be found */ + ret = cfstore_test_kv_is_found(cfstore_flush_test_02_kv_data->key_name, &bfound); + if(ret != ARM_DRIVER_OK && ret != ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND){ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: cfstore_test_kv_is_found() call failed (ret=%" PRId32 ").\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(false, cfstore_flush_utest_msg_g); + } + + if(!bfound) + { + /* first time start up. nothing is stored in cfstore flash. check this is the case */ + while(drv->Find(key_name_query, prev, next) == ARM_DRIVER_OK) + { + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: have found an entry in cfstore when none should be present", __func__); + TEST_ASSERT_MESSAGE(false, cfstore_flush_utest_msg_g); + } + /* no entries found, which is correct. + * store a value */ + len = strlen(cfstore_flush_test_02_kv_data->value); + ret = cfstore_test_create(cfstore_flush_test_02_kv_data->key_name, cfstore_flush_test_02_kv_data->value, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error:1: failed to write kv data (ret=%" PRId32 ").\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + /* flush to flash */ + ret = drv->Flush(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to flush data to cfstore flash (ret=%" PRId32 ").\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + CFSTORE_LOG("FLUSH: Success pending for new KV creation (name=%s, value=%s)\n", cfstore_flush_test_02_kv_data->key_name, cfstore_flush_test_02_kv_data->value) + } else { + /*read the value, increment by 1 and write value back */ + len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + ret = cfstore_test_read(cfstore_flush_test_02_kv_data->key_name, value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to read kv data (ret=%" PRId32 ").\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + + ivalue = atoi(value); + CFSTORE_LOG("FLUSH: Read KV from flash (name=%s, value=%" PRId32 ")\n", cfstore_flush_test_02_kv_data->key_name, ivalue); + /* increment value */ + ++ivalue; + snprintf(value, CFSTORE_KEY_NAME_MAX_LENGTH+1, "%" PRId32 "", ivalue); + len = strlen(value); + ret = cfstore_test_write(cfstore_flush_test_02_kv_data->key_name, value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write kv data (ret=%" PRId32 ").\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + + /* flush to flash */ + ret = drv->Flush(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to flush data to cfstore flash (ret=%" PRId32 ").\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + CFSTORE_LOG("FLUSH: Success pending for new KV value to flash (name=%s, value=%" PRId32 ")\n", cfstore_flush_test_02_kv_data->key_name, ivalue); + } + return; +} + + +/* brief callback handler when in state flushing */ +static void cfstore_flush_fsm_flushing(void* context) +{ + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in flushing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flush_fsm_state_flushing, cfstore_flush_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: cmd_code code (%d) is not as expected (%d)\r\n", __func__, ctx->cmd_code, CFSTORE_OPCODE_FLUSH); + TEST_ASSERT_MESSAGE(ctx->cmd_code == CFSTORE_OPCODE_FLUSH, cfstore_flush_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: status=%" PRId32 "\r\n", __func__, ctx->status); + TEST_ASSERT_MESSAGE(ctx->status >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + /* only change state if status >= 0*/ + if(ctx->status >= 0){ + CFSTORE_LOG("FLUSH: Successfully flushed data to flash.%s", "\n"); + cfstore_flush_fsm_state_set(&ctx->fsm, cfstore_flush_fsm_state_uninitializing, ctx); + } + return; +} + +/* static void cfstore_flush_fsm_flush_on_exit(void* context){ (void) context;} */ + + +/* @brief fsm handler called on entry to uninitializing state */ +static void cfstore_flush_fsm_uninit_on_entry(void* context) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) context; + const ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + /* check that the mtd is in synchronous mode */ + CFSTORE_FENTRYLOG("%s:entered: \r\n", __func__); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in uninitializing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flush_fsm_state_uninitializing, cfstore_flush_utest_msg_g); + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to uninitialize CFSTORE (ret=%" PRId32 ")\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + return; +} + +/* brief callback handler when in state uninitializing */ +static void cfstore_flush_fsm_uninitializing(void* context) +{ + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + CFSTORE_DBGLOG("%s:ctx->status=%" PRId32 ", ctx->loops_done=%" PRId32 "\r\n", __func__, ctx->status, ctx->loops_done); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: entered handler(%s) but not in uninitializing state (fsm->state=%d)\r\n", __func__, __func__, (int) ctx->fsm.state); + TEST_ASSERT_MESSAGE(ctx->fsm.state == cfstore_flush_fsm_state_uninitializing, cfstore_flush_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: cmd_code code (%d) is not as expected (%d)\r\n", __func__, ctx->cmd_code, CFSTORE_OPCODE_UNINITIALIZE); + TEST_ASSERT_MESSAGE(ctx->cmd_code == CFSTORE_OPCODE_UNINITIALIZE, cfstore_flush_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: status=%" PRId32 "\r\n", __func__, ctx->status); + TEST_ASSERT_MESSAGE(ctx->status >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + /* only change state if status >= 0*/ + if(ctx->status >= ARM_DRIVER_OK){ + ++ctx->loops_done; + if(ctx->loops_done < CFSTORE_FLUSH_FSM_LOOPS){ + cfstore_flush_fsm_state_set(&ctx->fsm, cfstore_flush_fsm_state_initializing, ctx); + } else { + /* move to init state */ + cfstore_flush_fsm_state_set(&ctx->fsm, cfstore_flush_fsm_state_stopped, ctx); + } + } + return; +} + +/* static void cfstore_flush_fsm_uninit_on_exit(void* context) {(void)context;} */ + + + +/* handler functions while in state */ +static cfstore_flush_fsm_handler cfstore_flush_fsm[cfstore_flush_fsm_state_max][cfstore_flush_fsm_event_max] = +{ +/* state\event: init_done flush_done unint_done */ +/* stopped */ {cfstore_flush_fsm_null, cfstore_flush_fsm_null, cfstore_flush_fsm_null }, +/* initialising */ {cfstore_flush_fsm_initializing, cfstore_flush_fsm_null, cfstore_flush_fsm_null }, +/* flushing */ {cfstore_flush_fsm_null, cfstore_flush_fsm_flushing, cfstore_flush_fsm_null }, +/* uninitializing */ {cfstore_flush_fsm_null, cfstore_flush_fsm_null, cfstore_flush_fsm_uninitializing } +}; + + +/* handler functions for entering the state*/ +static cfstore_flush_fsm_handler cfstore_flush_fsm_on_entry[cfstore_flush_fsm_state_max] = +{ + cfstore_flush_fsm_stop_on_entry, + cfstore_flush_fsm_init_on_entry, + cfstore_flush_fsm_flush_on_entry, + cfstore_flush_fsm_uninit_on_entry +}; + +/* handler functions for exiting state, currently none used */ +static cfstore_flush_fsm_handler cfstore_flush_fsm_on_exit[cfstore_flush_fsm_state_max] = +{ + cfstore_flush_fsm_null, + cfstore_flush_fsm_null, + cfstore_flush_fsm_null, + cfstore_flush_fsm_null +}; + + +/* @brief inject event into fsm */ +static void cfstore_flush_fsm_state_handle_event(cfstore_fsm_t* fsm, cfstore_flush_fsm_event_t event, void* context) +{ + cfstore_flush_ctx_t* ctx = (cfstore_flush_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered: fsm=%p, state=(%s), event=%d (%s), ctx=%p\r\n", __func__, fsm, cfstore_flush_state_str[fsm->state], (int) event, cfstore_flush_event_str[event], ctx); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: invalid event (%d)\r\n", __func__, (int) event); + TEST_ASSERT_MESSAGE(event < cfstore_flush_fsm_event_max, cfstore_flush_utest_msg_g); + fsm->event = event; + if(cfstore_flush_fsm[fsm->state][fsm->event] != NULL){ + cfstore_flush_fsm[fsm->state][fsm->event](ctx); + } + + /* do not clear context data set by caller as it may be used later + * fsm->event = cfstore_flush_fsm_event_max; + * ctx->status = 0; + * ctx->cmd_code = (FlashJournal_OpCode_t)((int) FLASH_JOURNAL_OPCODE_RESET+1); + */ + return; +} + + +/* @brief function to move to new fsm state, calling state exit function for old state and entry function for new state */ +static void cfstore_flush_fsm_state_set(cfstore_fsm_t* fsm, cfstore_flush_fsm_state_t new_state, void* ctx) +{ + #ifdef CFSTORE_DEBUG + cfstore_flush_fsm_state_t old_state = fsm->state; + #endif + + CFSTORE_FENTRYLOG("%s:entered: fsm=%p, ctx=%p\r\n", __func__, fsm, ctx); + #ifdef CFSTORE_DEBUG + CFSTORE_DBGLOG("%s:FSM:REQ RX:%s:%s\r\n", __func__, cfstore_flush_state_str[fsm->state], cfstore_flush_state_str[new_state]); + #endif + TEST_ASSERT_MESSAGE(fsm != NULL, "fsm is not a valid pointer"); + TEST_ASSERT_MESSAGE(new_state < cfstore_flush_fsm_state_max, "new_state is not a valid state"); + TEST_ASSERT_MESSAGE(ctx != NULL, "ctx is not a valid pointer"); + TEST_ASSERT_MESSAGE(fsm->state < cfstore_flush_fsm_state_max, "fsm->state is not a valid state"); + + if(cfstore_flush_fsm_on_exit[fsm->state] != NULL){ + cfstore_flush_fsm_on_exit[fsm->state](ctx); + } + fsm->state = new_state; + if(cfstore_flush_fsm_on_entry[new_state] != NULL){ + cfstore_flush_fsm_on_entry[new_state](ctx); + } + #ifdef CFSTORE_DEBUG + CFSTORE_DBGLOG("%s:FSM:REQ DONE fsm=%p, fsm->state (old_state)=%s, new_state=%s, ctx=%p\r\n", __func__, fsm, cfstore_flush_state_str[old_state], cfstore_flush_state_str[new_state], ctx); + #endif + return; +} + + +/* @brief asynchronous test 01 */ +static control_t cfstore_flush_test_02_k64f(void) +{ + cfstore_flush_ctx_t* ctx = cfstore_flush_ctx_get(); + + CFSTORE_FENTRYLOG("%s:entered: \r\n", __func__); + cfstore_flush_ctx_init(ctx); + cfstore_flush_fsm_state_set(&ctx->fsm, cfstore_flush_fsm_state_initializing, ctx); + return CaseTimeout(CFSTORE_FLUSH_GREENTEA_TIMEOUT_S*1000); +} + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_flush_test_00(const size_t call_count) +{ + (void) call_count; + CFSTORE_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", cfstore_driver.GetCapabilities().asynchronous_ops); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(CFSTORE_FLUSH_GREENTEA_TIMEOUT_S, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("FLUSH_test_00", cfstore_flush_test_00), + Case("FLUSH_test_01", cfstore_flush_test_02_k64f), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/flush2/flush2.cpp b/TESTS/cfstore/flush2/flush2.cpp new file mode 100644 index 0000000000..ca868d18af --- /dev/null +++ b/TESTS/cfstore/flush2/flush2.cpp @@ -0,0 +1,333 @@ +/* @file flush.cpp + * + * 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. + * + * Test cases to flush KVs in the CFSTORE using the drv->Flush() interface. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_flush2_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("FLUSH2_test_00", cfstore_flush2_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + + +#else + + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "utest/utest.h" +#include "unity/unity.h" +#include "cfstore_config.h" +#include +#include "greentea-client/test_env.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +/* + * Defines + */ +#define CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE 256 +#define cfstore_flush_fsm_null NULL +#define CFSTORE_FLUSH_CASE_TIMEOUT_MS 10000 + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.flush2.box1"); +UVISOR_BOX_CONFIG(cfstore_flush2_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + +/* + * Globals + * + * cfstore_flush_utest_msg_g + * buffer for storing TEST_ASSERT_xxx_MESSAGE messages + */ +static char cfstore_flush_utest_msg_g[CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE]; + +/* KV data for test_03 */ +static cfstore_kv_data_t cfstore_flush_test_02_kv_data[] = { + { "com.arm.mbed.configurationstore.test.flush.cfstoreflushtest02", "1"}, + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + { NULL, NULL}, +}; + +/* async test version */ + +typedef struct cfstore_flush_ctx_t +{ + int32_t status; + ARM_CFSTORE_OPCODE cmd_code; +} cfstore_flush_ctx_t; + + +/* + * Globals + */ +static cfstore_flush_ctx_t cfstore_flush_ctx_g; + +/* + * context related methods + */ + +/* @brief get a pointer to the global context data structure */ +static cfstore_flush_ctx_t* cfstore_flush_ctx_get(void) +{ + return &cfstore_flush_ctx_g; +} + +/* @brief initialize global context data structure */ +static void cfstore_flush_ctx_init(cfstore_flush_ctx_t* ctx) +{ + TEST_ASSERT_MESSAGE(ctx != NULL, "ctx is NULL"); + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + memset(&cfstore_flush_ctx_g, 0, sizeof(cfstore_flush_ctx_g)); +} + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_flush2_test_00(const size_t call_count) +{ + (void) call_count; + CFSTORE_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", cfstore_driver.GetCapabilities().asynchronous_ops); + return CaseNext; +} + +control_t cfstore_flush2_test_01_start(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_flush_ctx_t* ctx = cfstore_flush_ctx_get(); + const ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + /* check that the mtd is in synchronous mode */ + CFSTORE_FENTRYLOG("%s:entered:\r\n", __func__); + (void) call_count; + cfstore_flush_ctx_init(ctx); + ret = drv->Initialize(cfstore_utest_default_callback, ctx); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialize CFSTORE (ret=%" PRId32 ")\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + return CaseTimeout(100000); +} + + +static control_t cfstore_flush2_test_01_mid(const size_t call_count) +{ + + bool bfound = false; + int32_t ivalue = 0; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + const char* key_name_query = "*"; + char value[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + ARM_CFSTORE_SIZE len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + ARM_CFSTORE_HANDLE_INIT(next); + ARM_CFSTORE_HANDLE_INIT(prev); + ARM_CFSTORE_KEYDESC kdesc; + + /* check that the mtd is in synchronous mode */ + CFSTORE_FENTRYLOG("%s:entered: \r\n", __func__); + (void) call_count; + memset(&kdesc, 0, sizeof(kdesc)); + + /* try to read key; should not be found */ + ret = cfstore_test_kv_is_found(cfstore_flush_test_02_kv_data->key_name, &bfound); + if(ret != ARM_DRIVER_OK && ret != ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND){ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: cfstore_test_kv_is_found() call failed (ret=%" PRId32 ").\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(false, cfstore_flush_utest_msg_g); + } + + if(!bfound) + { + /* first time start up. nothing is stored in cfstore flash. check this is the case */ + while(drv->Find(key_name_query, prev, next) == ARM_DRIVER_OK) + { + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: have found an entry in cfstore when none should be present", __func__); + TEST_ASSERT_MESSAGE(false, cfstore_flush_utest_msg_g); + } + /* no entries found, which is correct. + * store a value */ + len = strlen(cfstore_flush_test_02_kv_data->value); + ret = cfstore_test_create(cfstore_flush_test_02_kv_data->key_name, cfstore_flush_test_02_kv_data->value, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error:1: failed to write kv data (ret=%" PRId32 ").\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + /* flush to flash */ + ret = drv->Flush(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to flush data to cfstore flash (ret=%" PRId32 ").\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + + } else { + /*read the value, increment by 1 and write value back */ + len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + ret = cfstore_test_read(cfstore_flush_test_02_kv_data->key_name, value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to read kv data (ret=%" PRId32 ").\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + + /* increment value */ + ivalue = atoi(value); + ++ivalue; + snprintf(value, CFSTORE_KEY_NAME_MAX_LENGTH+1, "%d", (int) ivalue); + len = strlen(value); + ret = cfstore_test_write(cfstore_flush_test_02_kv_data->key_name, value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write kv data (ret=%" PRId32 ").\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + + /* flush to flash */ + ret = drv->Flush(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_FLUSH_UTEST_MSG_BUF_SIZE, "%s:Error: failed to flush data to cfstore flash (ret=%" PRId32 ").\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + } + return CaseTimeout(100000); +} + +control_t cfstore_flush2_test_01_end(const size_t call_count) +{ + const ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered:\r\n", __func__); + (void) call_count; + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_flush_utest_msg_g); + return CaseNext; +} + + +/** + * @brief test to open(create) a very large value, write the data, close, then flush. + * for a N keys simultaneously. + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_flush2_test_02(const size_t call_count) +{ + (void) call_count; + //todo: implement test + CFSTORE_TEST_UTEST_MESSAGE(cfstore_flush_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Warn: Not implemented\n", __func__); + CFSTORE_LOG("%s: WARN: requires implementation\n", __func__); + TEST_ASSERT_MESSAGE(true, cfstore_flush_utest_msg_g); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + // Call the default reporting function- + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("CFSTORE_FLUSH2_test_00", cfstore_flush2_test_00), + Case("CFSTORE_FLUSH2_test_01_start", cfstore_flush2_test_01_start), + Case("CFSTORE_FLUSH2_test_01_mid", cfstore_flush2_test_01_mid), + Case("CFSTORE_FLUSH2_test_01_end", cfstore_flush2_test_01_end), + Case("CFSTORE_FLUSH2_test_02_start", cfstore_utest_default_start), + Case("CFSTORE_FLUSH2_test_02_end", cfstore_flush2_test_02), +}; + + +// Declare your test specification with a custom setup handler +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/init/init.cpp b/TESTS/cfstore/init/init.cpp new file mode 100644 index 0000000000..109438a76b --- /dev/null +++ b/TESTS/cfstore/init/init.cpp @@ -0,0 +1,223 @@ +/** @file init.cpp + * + * 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. + * + * Test cases to test initialization/uninitialization code. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_init_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("INIT_test_00", cfstore_init_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + + +#else + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +//#include +#include +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CONFIG_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CONFIG_UVISOR */ + + +using namespace utest::v1; + +static char cfstore_init_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +typedef struct cfstore_init_ctx_t +{ + ARM_CFSTORE_CAPABILITIES caps; +} cfstore_init_ctx_t; + +static cfstore_init_ctx_t cfstore_init_ctx_g; +extern ARM_CFSTORE_DRIVER cfstore_driver; +ARM_CFSTORE_DRIVER *cfstore_drv = &cfstore_driver; + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_init_test_00(const size_t call_count) +{ + (void) call_count; + CFSTORE_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", cfstore_driver.GetCapabilities().asynchronous_ops); + return CaseNext; +} + +static void cfstore_init_test_01(cfstore_init_ctx_t* ctx) +{ + int32_t ret; + + (void) ctx; + CFSTORE_LOG("INITIALIZING%s", "\r\n"); + ret = cfstore_drv->Initialize(NULL, NULL); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_init_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Initialize() should return ret >= 0 for async/synch modes(ret=%ld)\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_init_utest_msg_g); + + CFSTORE_LOG("FLUSHING1%s", "\r\n"); + ret = cfstore_drv->Flush(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_init_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Flush() failed (ret=%ld)\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_init_utest_msg_g); + + CFSTORE_LOG("UNINITIALIZING%s", "\r\n"); + ret = cfstore_drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_init_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() should return ret >= 0 for synch mode(ret=%ld)\r\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_init_utest_msg_g); + + CFSTORE_LOG("***************%s", "\r\n"); + CFSTORE_LOG("*** SUCCESS ***%s", "\r\n"); + CFSTORE_LOG("***************%s", "\r\n"); + return; +} + +static control_t cfstore_init_app_start(const size_t call_count) +{ + cfstore_init_ctx_t* ctx = &cfstore_init_ctx_g; + + (void) call_count; + + /* initialise the context */ + 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){ + /* 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"); + return CaseNext; + } + cfstore_init_test_01(ctx); + return CaseNext; +} + +#ifndef YOTTA_CONFIGURATION_STORE_INIT_VERSION_STRING + + +/* when built as Configuration-Store example, include greentea support otherwise omit */ + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("INIT_test_00", cfstore_init_test_00), + Case("INIT_test_01_start", cfstore_init_app_start), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#else // YOTTA_CONFIGURATION_STORE_INIT_VERSION_STRING + + +// stand alone Configuration-Store-Example +void app_start(int argc __unused, char** argv __unused) +{ + cfstore_init_app_start(0); +} + + +#endif // YOTTA_CONFIGURATION_STORE_INIT_VERSION_STRING + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/misc/misc.cpp b/TESTS/cfstore/misc/misc.cpp new file mode 100644 index 0000000000..56443c7d67 --- /dev/null +++ b/TESTS/cfstore/misc/misc.cpp @@ -0,0 +1,460 @@ +/* + * @file misc.cpp + * + * 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. + * + * Test cases for miscellaneious API drv->Xxx() functions. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_misc_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("MISC_test_00", cfstore_misc_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + + +#else + + +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +#include +#include "cfstore_config.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static char cfstore_misc_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +#ifdef YOTTA_CFG_CFSTORE_UVISOR +/* Create the main box ACL list for the application. + * The main ACL gets inherited by all the other boxes + */ +CFSTORE_UVISOR_MAIN_ACL(cfstore_acl_uvisor_box_misc_g); + +/* Enable uVisor. */ +UVISOR_SET_MODE_ACL(UVISOR_ENABLED, cfstore_acl_uvisor_box_misc_g); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_misc_test_00(const size_t call_count) +{ + (void) call_count; + CFSTORE_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", cfstore_driver.GetCapabilities().asynchronous_ops); + return CaseNext; +} + + +/** @brief basic PowerControl() test + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_misc_test_00_start(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_POWER_STATE state = ARM_POWER_OFF; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + /* try setting the power control state before initialised, which should fail */ + ret = drv->PowerControl(state); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Eror: PowerControl() call should have failed as CFSTORE not initialised, but the call succeeded\r\n", __func__); + TEST_ASSERT_MESSAGE(ret < ARM_DRIVER_OK, 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=%" PRId32 ")\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + return CaseTimeout(CFSTORE_UTEST_DEFAULT_TIMEOUT_MS); +} + +static control_t cfstore_misc_test_00_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_POWER_STATE state = ARM_POWER_OFF; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + while(state <= ARM_POWER_FULL) + { + ret = drv->PowerControl(state); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: PowerControl() call failed\r\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + state = (ARM_POWER_STATE)((uint32_t) state + 1); + } + /*try invalid value which should fail*/ + ret = drv->PowerControl((ARM_POWER_STATE ) 0xffffffff); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:ERror: PowerControl() did not fail with bad value 0xffffffff (not as expected)\r\n", __func__); + TEST_ASSERT_MESSAGE(ret < ARM_DRIVER_OK, 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__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + return CaseNext; +} + + +/** @brief basic GetVersion() test + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_misc_test_01(const size_t call_count) +{ + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_DRIVER_VERSION version; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(&version, 0, sizeof(version)); + + version = drv->GetVersion(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:GetVersion() failed to return correct API version.\r\n", __func__); + TEST_ASSERT_MESSAGE(version.api == ARM_CFSTORE_API_VERSION, cfstore_misc_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:GetVersion() to return correct driver version.\r\n", __func__); + TEST_ASSERT_MESSAGE(version.drv == ARM_CFSTORE_DRV_VERSION, cfstore_misc_utest_msg_g); + return CaseNext; +} + + +/** @brief basic GetCapabilities() test + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_misc_test_02(const size_t call_count) +{ + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_CAPABILITIES caps; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(&caps, 0, sizeof(caps)); + caps = drv->GetCapabilities(); + //CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Capabilities() failed to return asynchronous_ops == false.\r\n", __func__); + //TEST_ASSERT_MESSAGE(caps.asynchronous_ops == false, cfstore_misc_utest_msg_g); + +#ifdef YOTTA_CFG_CONFIG_HARDWARE_MTD_ASYNC_OPS + /* sync mode */ + printf("%s:sync mode: caps.asynchronous_ops =%" PRIu32 "\n", __func__, caps.asynchronous_ops); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: GetCapabilities() reported caps.asynchronous_ops != false but system built for sync operation.\r\n", __func__); + TEST_ASSERT_MESSAGE(caps.asynchronous_ops == false, cfstore_misc_utest_msg_g); +#else + /* async mode */ + printf("%s:async mode: caps.asynchronous_ops =%" PRIu32 "\n", __func__, caps.asynchronous_ops); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: GetCapabilities() reported caps.asynchronous_ops != true but system built for async operation.\r\n", __func__); + TEST_ASSERT_MESSAGE(caps.asynchronous_ops == true, cfstore_misc_utest_msg_g); +#endif + + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Capabilities() failed to return uvisor_support_enabled == false.\r\n", __func__); + TEST_ASSERT_MESSAGE(caps.uvisor_support_enabled == false, cfstore_misc_utest_msg_g); + return CaseNext; +} + + +/* KV data for test_03 */ +static cfstore_kv_data_t cfstore_misc_test_03_kv_data[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + { "The.principles.of.least.action.in.quantum.mechanics", "DoctoralThesis"}, + { "Space.Time.Approach.to.Quantum.Electrodynamic", " PhysicalReview766)"}, + { "An.Operator.Calculus.Having.Applications.in.Quantum.Electrodynamics", "PhysicalReview84)"}, + { NULL, NULL}, +}; + + +/** @brief basic GetKeyName() test + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_misc_test_03_end(const size_t call_count) +{ + uint8_t key_name_len = 0; + char key_name_buf[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + cfstore_kv_data_t* node = NULL; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + (void) call_count; + memset(key_name_buf, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + /* dont set any flags to get default settings */ + memset(&flags, 0, sizeof(flags)); + + ret = cfstore_test_create_table(cfstore_misc_test_03_kv_data); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to create keys from table.\r\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + + node = cfstore_misc_test_03_kv_data; + while(node->key_name != NULL) + { + CFSTORE_DBGLOG("%s:About to open KV (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + ret = drv->Open(node->key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to open node (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\r\n", __func__, node->key_name, node->value, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + + key_name_len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + memset(key_name_buf, 0, key_name_len); + drv->GetKeyName(hkey, key_name_buf, &key_name_len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to GetKeyName() (key_name (expected)=\"%s\", key_name (returned)=\"%s\", value=\"%s\"), len return=%d, len expected=%d\r\n", __func__, node->key_name, key_name_buf, node->value, (int) key_name_len, (int) strlen(node->key_name)); + TEST_ASSERT_MESSAGE(key_name_len == strlen(node->key_name)+1, cfstore_misc_utest_msg_g); + + CFSTORE_LOG("GetKeyName() successfully reported key_name (key_name=\"%s\")\r\n", key_name_buf); + ret = drv->Close(hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to close key (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + node++; + } + + cfstore_test_delete_all(); + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + return CaseNext; +} + + +/** @brief basic GetValueLen() test + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_misc_test_04_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + cfstore_kv_data_t* node = NULL; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + /* dont set any flags to get default settings */ + memset(&flags, 0, sizeof(flags)); + + ret = cfstore_test_create_table(cfstore_misc_test_03_kv_data); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to create keys from table.\r\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + + node = cfstore_misc_test_03_kv_data; + while(node->key_name != NULL) + { + CFSTORE_DBGLOG("%s:About to open KV (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + ret = drv->Open(node->key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to open node (key_name=\"%s\", value=\"%s\")(ret=%d)\r\n", __func__, node->key_name, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + + drv->GetValueLen(hkey, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Errro: GetValueLen() retrieve incorrect length of value blob(expected=%d, returned=%d)\r\n", __func__, (int) strlen(node->value), (int) len); + TEST_ASSERT_MESSAGE(len == strlen(node->value), cfstore_misc_utest_msg_g); + CFSTORE_LOG("GetValueLen() successfully reported correct value blob length%s", "\r\n"); + + ret = drv->Close(hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Failed to close key (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + node++; + } + cfstore_test_delete_all(); + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + return CaseNext; +} + + +/** @brief basic GetStatus() test + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_misc_test_05_start(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_STATUS status; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) 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); + + 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=%" PRId32 ")\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + return CaseTimeout(CFSTORE_UTEST_DEFAULT_TIMEOUT_MS); +} + +static control_t cfstore_misc_test_05_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_STATUS status; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) 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); + + 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); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_misc_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_misc_utest_msg_g); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(200, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("MISC_test_00", cfstore_misc_test_00), + Case("MISC_test_00_start", cfstore_misc_test_00_start), + Case("MISC_test_00_end", cfstore_misc_test_00_end), + Case("MISC_test_01", cfstore_misc_test_01), + Case("MISC_test_02", cfstore_misc_test_02), + Case("MISC_test_03_start", cfstore_utest_default_start), + Case("MISC_test_03_end", cfstore_misc_test_03_end), + Case("MISC_test_04_start", cfstore_utest_default_start), + Case("MISC_test_04_end", cfstore_misc_test_04_end), + Case("MISC_test_05_start", cfstore_misc_test_05_start), + Case("MISC_test_05_end", cfstore_misc_test_05_end), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/open/open.cpp b/TESTS/cfstore/open/open.cpp new file mode 100644 index 0000000000..653e25ea6f --- /dev/null +++ b/TESTS/cfstore/open/open.cpp @@ -0,0 +1,702 @@ +/* + * @file open.cpp + * + * 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. + * + * Test cases to open KVs in the CFSTORE using the drv->Open() interface. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_open_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("OPEN_test_00", cfstore_open_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + +#else + + +#include +#include +#include /*rand()*/ +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +//#include +#include +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static char cfstore_open_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.open.box1"); +UVISOR_BOX_CONFIG(cfstore_open_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + +#ifdef CFSTORE_DEBUG +#define CFSTORE_OPEN_GREENTEA_TIMEOUT_S 3000 +#else +#define CFSTORE_OPEN_GREENTEA_TIMEOUT_S 1000 +#endif + + +/* support functions */ + +/* + * open tests that focus on testing cfstore_open() + * cfstore_handle_t cfstore_open(const char* key_name, char* data, ARM_CFSTORE_SIZE* len, cfstore_key_desc_t* kdesc) + */ + +/* KV data for test_01 */ +static cfstore_kv_data_t cfstore_open_test_01_kv_data[] = { + { "yotta.hello-world.animal{wobbly-dog}{foot}frontLeft", "missing"}, + { NULL, NULL}, +}; + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_open_test_00(const size_t call_count) +{ + (void) call_count; + CFSTORE_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", cfstore_driver.GetCapabilities().asynchronous_ops); + return CaseNext; +} + +/** @brief + * Basic open test which does the following: + * - creates KV with default rw perms and writes some data to the value blob. + * - closes the newly created KV. + * - opens the KV with the default permissions (r-only) + * - reads the KV data and checks its the same as the previously created data. + * - closes the opened key + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_open_test_01_end(const size_t call_count) +{ + char* read_buf; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(hkey); + cfstore_kv_data_t *node; + ARM_CFSTORE_FMODE flags; + + CFSTORE_DBGLOG("%s:entered\n", __func__); + (void) call_count; + node = cfstore_open_test_01_kv_data; + memset(&kdesc, 0, sizeof(kdesc)); + memset(&flags, 0, sizeof(flags)); + + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + CFSTORE_DBGLOG("%s:About to create new node (key_name=\"%s\", value=\"%s\")\n", __func__, node->key_name, node->value); + ret = drv->Create(node->key_name, strlen(node->value), &kdesc, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create node (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, node->key_name, node->value, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + CFSTORE_DBGLOG("%s:length of KV=%d (key_name=\"%s\", value=\"%s\")\n", __func__, (int) len, node->key_name, node->value); + len = strlen(node->value); + ret = drv->Write(hkey, (char*) node->value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write key (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, node->key_name, node->value, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write full value data (key_name=\"%s\", value=\"%s\"), len=%d, (ret=%" PRId32 ")\n", __func__, node->key_name, node->value, (int) len, ret); + TEST_ASSERT_MESSAGE(len == strlen(node->value), cfstore_open_utest_msg_g); + + printf("Created KV successfully (key_name=\"%s\", value=\"%s\")\n", node->key_name, node->value); + ret = drv->Close(hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to close handle (ret=%" PRId32 ")\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + /* now open the newly created key */ + ret = drv->Open(node->key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open node (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, node->key_name, node->value, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + len = strlen(node->value) + 1; + read_buf = (char*) malloc(len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to allocated read buffer \n", __func__); + TEST_ASSERT_MESSAGE(read_buf != NULL, cfstore_open_utest_msg_g); + + printf("Opened KV successfully (key_name=\"%s\", value=\"%s\")\n", node->key_name, node->value); + memset(read_buf, 0, len); + ret = drv->Read(hkey, read_buf, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write key (key_name=\"%s\", value=\"%s\")\n", __func__, node->key_name, node->value); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + /* check read data is as expected */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: read value data (%s) != KV value data (key_name=\"%s\", value=\"%s\")\n", __func__, read_buf, node->key_name, node->value); + TEST_ASSERT_MESSAGE(strncmp(read_buf, node->value, strlen(node->value)) == 0, cfstore_open_utest_msg_g); + + if(read_buf){ + free(read_buf); + } + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Close() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Close(hkey) >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + return CaseNext; +} + +static cfstore_kv_data_t cfstore_open_test_02_data[] = { + CFSTORE_INIT_1_TABLE_MID_NODE, + { NULL, NULL}, +}; + +/** + * @brief test to open() a pre-existing key and try to write it, which should fail + * as by default pre-existing keys are opened read-only + * + * Basic open test which does the following: + * - creates KV with default rw perms and writes some data to the value blob. + * - closes the newly created KV. + * - opens the KV with the default permissions (read-only) + * - tries to write the KV data which should fail because KV was not opened with write flag set. + * - closes the opened key + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_open_test_02_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(&kdesc, 0, sizeof(kdesc)); + /* dont set any flags to get default settings */ + memset(&flags, 0, sizeof(flags)); + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(cfstore_open_test_02_data[0].value); + ret = cfstore_test_create(cfstore_open_test_02_data[0].key_name, (char*) cfstore_open_test_02_data[0].value, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create KV in store (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + /* by default, owner of key opens with read-only permissions*/ + ret = drv->Open(cfstore_open_test_02_data[0].key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open node (key_name=\"%s\")(ret=%" PRId32 ")\n", __func__, cfstore_open_test_02_data[0].key_name, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + len = strlen(cfstore_open_test_02_data[0].value); + ret = drv->Write(hkey, cfstore_open_test_02_data[0].value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: call to Write() succeeded when should have failed (key_name=\"%s\")(ret=%" PRId32 ").\n", __func__, cfstore_open_test_02_data[0].key_name, ret); + TEST_ASSERT_MESSAGE(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_READ_ONLY, cfstore_open_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Close() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Close(hkey) >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + return CaseNext; +} + + +/** + * @brief test to open() a pre-existing key and try to write it, which should succeed + * because the key was opened read-write permissions explicitly + * + * Basic open test which does the following: + * - creates KV with default rw perms and writes some data to the value blob. + * - closes the newly created KV. + * - opens the KV with the rw permissions (non default) + * - tries to write the KV data which should succeeds because KV was opened with write flag set. + * - closes the opened key + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_open_test_03_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(&kdesc, 0, sizeof(kdesc)); + /* dont set any flags to get default settings */ + memset(&flags, 0, sizeof(flags)); + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(cfstore_open_test_02_data[0].value); + ret = cfstore_test_create(cfstore_open_test_02_data[0].key_name, (char*) cfstore_open_test_02_data[0].value, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create KV in store (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + /* opens with read-write permissions*/ + flags.read = true; + flags.write = true; + ret = drv->Open(cfstore_open_test_02_data[0].key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open node (key_name=\"%s\")(ret=%" PRId32 ")\n", __func__, cfstore_open_test_02_data[0].key_name, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + len = strlen(cfstore_open_test_02_data[0].value); + ret = drv->Write(hkey, cfstore_open_test_02_data[0].value, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: call to Write() failed when should have succeeded (key_name=\"%s\")(ret=%" PRId32 ").\n", __func__, cfstore_open_test_02_data[0].key_name, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Close() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Close(hkey) >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + return CaseNext; +} + + + +/** @brief test to call cfstore_open() with a key_name string that exceeds + * the maximum length + * + * @return status code + * CFSTORE_ERR_SUCCESS, the test passed successfully + * CFSTORE_ERR_FAILED, the test failed + * + */ +static control_t cfstore_open_test_04_end(const size_t call_count) +{ + char kv_name_good[CFSTORE_KEY_NAME_MAX_LENGTH+1]; /* extra char for terminating null */ + char kv_name_bad[CFSTORE_KEY_NAME_MAX_LENGTH+2]; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + memset(kv_name_good, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + memset(kv_name_bad, 0, CFSTORE_KEY_NAME_MAX_LENGTH+2); + memset(&kdesc, 0, sizeof(kdesc)); + /* dont set any flags to get default settings */ + memset(&flags, 0, sizeof(flags)); + + len = CFSTORE_KEY_NAME_MAX_LENGTH; + ret = cfstore_test_kv_name_gen(kv_name_good, len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate kv_name_good.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK , cfstore_open_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: kv_name_good is not the correct length (len=%d, expected=%d).\n", __func__, (int) strlen(kv_name_good), (int) len); + TEST_ASSERT_MESSAGE(strlen(kv_name_good) == CFSTORE_KEY_NAME_MAX_LENGTH, cfstore_open_utest_msg_g); + + ret = cfstore_test_create(kv_name_good, kv_name_good, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create KV in store for kv_name_good(ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + ret = cfstore_test_kv_name_gen(kv_name_bad, len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate kv_name_bad.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK , cfstore_open_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: kv_name_bad is not the correct length (len=%d, expected=%d).\n", __func__, (int) strlen(kv_name_bad), (int) len); + TEST_ASSERT_MESSAGE(strlen(kv_name_bad) == CFSTORE_KEY_NAME_MAX_LENGTH+1, cfstore_open_utest_msg_g); + + memset(&kdesc, 0, sizeof(kdesc)); + ret = cfstore_test_create(kv_name_bad, kv_name_bad, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: created KV in store for kv_name_bad when should have failed(ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret < ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + return CaseNext; +} + + +typedef struct cfstore_open_kv_name_ascii_node { + uint32_t code; + uint32_t f_allowed : 1; +} cfstore_open_kv_name_ascii_node; + +static const uint32_t cfstore_open_kv_name_ascii_table_code_sentinel_g = 256; + +/*@brief table recording ascii character codes permitted in kv names */ +static cfstore_open_kv_name_ascii_node cfstore_open_kv_name_ascii_table[] = +{ + {0, false}, /* codes 0-44 not allowed */ + {45, true}, /* codes 45-46 == [-.] allowed */ + {47, false}, /* code 47 not allowed */ + {48, true}, /* codes 48-57 not allowed */ + {58, false}, /* codes 46-64 not allowed */ + {64, true}, /* codes 64-91 allowed [@A-Z] */ + {91, false}, /* code 91-96 not allowed */ + {95, true}, /* code 95 allowed '_' */ + {96, false}, /* codes 96 not allowed */ + {97, true}, /* codes 65-90 allowed [A-Z] and {*/ + {123, false}, /* codes 123 '}' not allowed on its own*/ + {124, false}, /* codes 124 not allowed */ + {125, false}, /* code 125 '}' not allowed on its own*/ + {126, false}, /* codes 126-255 not allowed */ + {cfstore_open_kv_name_ascii_table_code_sentinel_g, false}, /* sentinel */ +}; + +enum cfstore_open_kv_name_pos { + cfstore_open_kv_name_pos_start = 0x0, + cfstore_open_kv_name_pos_mid, + cfstore_open_kv_name_pos_end, + cfstore_open_kv_name_pos_max +}; + +/** @brief test to call cfstore_open() with key_name that in includes + * illegal characters + * - the character(s) can be at the beginning of the key_name + * - the character(s) can be at the end of the key_name + * - the character(s) can be somewhere within the key_name string + * - a max-length string of random characters (legal and illegal) + * - a max-length string of random illegal characters only + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_open_test_05_end(const size_t call_count) +{ + bool f_allowed = false; + const char* pos_str = NULL; + char kv_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; /* extra char for terminating null */ + uint32_t j = 0; + int32_t ret = ARM_DRIVER_OK; + size_t name_len = CFSTORE_KEY_NAME_MAX_LENGTH; + ARM_CFSTORE_KEYDESC kdesc; + cfstore_open_kv_name_ascii_node* node = NULL; + uint32_t pos; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + /* create bad keyname strings with invalid character code at start of keyname */ + node = cfstore_open_kv_name_ascii_table; + while(node->code != cfstore_open_kv_name_ascii_table_code_sentinel_g) + { + /* loop over range */ + for(j = node->code; j < (node+1)->code; j++) + { + /* set the start, mid, last character of the name to the test char code */ + for(pos = (uint32_t) cfstore_open_kv_name_pos_start; pos < (uint32_t) cfstore_open_kv_name_pos_max; pos++) + { + name_len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(kv_name, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + memset(&kdesc, 0, sizeof(kdesc)); + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + + ret = cfstore_test_kv_name_gen(kv_name, name_len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate kv_name.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK , cfstore_open_utest_msg_g); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: kv_name incorrect length (len=%d, expected= %d).\n", __func__, (int) strlen(kv_name), (int) name_len); + TEST_ASSERT_MESSAGE(strlen(kv_name) == name_len, cfstore_open_utest_msg_g); + + /* overwrite a char at the pos start, mid, end of the kv_name with an ascii char code (both illegal and legal)*/ + switch(pos) + { + case cfstore_open_kv_name_pos_start: + kv_name[0] = (char) j; + pos_str = "start"; + break; + case cfstore_open_kv_name_pos_mid: + /* create bad keyname strings with invalid character code in the middle of keyname */ + kv_name[name_len/2] = (char) j; + pos_str = "middle"; + break; + case cfstore_open_kv_name_pos_end: + /* create bad keyname strings with invalid character code at end of keyname */ + kv_name[name_len-1] = (char) j; + pos_str = "end"; + break; + default: + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unexpected value of pos (pos=%d).\n", __func__, (int) pos); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + break; + } + ret = cfstore_test_create(kv_name, kv_name, &name_len, &kdesc); + + /* special cases */ + switch(j) + { + case 0 : + case 46 : + switch(pos) + { + /* for code = 0 (null terminator). permitted at mid and end of string */ + /* for code = 46 ('.'). permitted at mid and end of string but not at start */ + case cfstore_open_kv_name_pos_start: + f_allowed = false; + break; + case cfstore_open_kv_name_pos_mid: + case cfstore_open_kv_name_pos_end: + default: + f_allowed = true; + break; + } + break; + default: + f_allowed = node->f_allowed; + break; + } + if(f_allowed == true) + { + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create KV in store when kv_name contains valid characters (code=%" PRId32 ", ret=%" PRId32 ").\n", __func__, j, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + CFSTORE_LOG("Successfully created a KV with valid keyname containing ascii character code %" PRIu32 " (%c) at the %s of the keyname.\n", j, (int) j, pos_str); + + ret = cfstore_test_delete(kv_name); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to delete KV previously created (code=%" PRId32 ", ret=%" PRId32 ").\n", __func__, j, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + } + else + { /*node->f_allowed == false => not allowed to create kv name with ascii code */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: created KV in store when kv_name contains an invalid character (code=%" PRId32 ", ret=%" PRId32 ").\n", __func__, j, ret); + TEST_ASSERT_MESSAGE(ret < ARM_DRIVER_OK, cfstore_open_utest_msg_g); + CFSTORE_LOG("Successfully failed to create a KV with an invalid keyname containing ascii character code %" PRId32 " at the %s of the keyname.\n", j, pos_str); + } + } + } + node++; + } + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + return CaseNext; +} + + +static const char cfstore_open_ascii_illegal_buf_g[] = "!\"�$%&'()*+,./:;<=>?@[\\]^_`{|}~"; /* 31 chars */ + +/** @brief test to call cfstore_open() with key_name that in includes + * illegal characters + * - a max-length string of random illegal characters only + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_open_test_06_end(const size_t call_count) +{ + char kv_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; /* extra char for terminating null */ + size_t i = 0; + uint32_t pos = 0; + int32_t ret = ARM_DRIVER_OK; + size_t name_len = CFSTORE_KEY_NAME_MAX_LENGTH; + ARM_CFSTORE_KEYDESC kdesc; + size_t buf_data_max = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + /* create bad keyname strings with invalid character code at start of keyname */ + buf_data_max = strlen(cfstore_open_ascii_illegal_buf_g); + name_len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(kv_name, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + + /* generate a kv name of illegal chars*/ + for(i = 0; i < name_len; i++) + { + pos = rand() % (buf_data_max+1); + kv_name[i] = cfstore_open_ascii_illegal_buf_g[pos]; + } + + ret = cfstore_test_create(kv_name, kv_name, &name_len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: created KV in store when kv_name contains invalid characters (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret < ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + return CaseNext; +} + +/** @brief test to call cfstore_open() with key_name that in includes + * illegal characters + * - a max-length string of random characters (legal and illegal) + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_open_test_07_end(const size_t call_count) +{ + char kv_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; /* extra char for terminating null */ + size_t i = 0; + int32_t ret = ARM_DRIVER_OK; + size_t name_len = CFSTORE_KEY_NAME_MAX_LENGTH; + ARM_CFSTORE_KEYDESC kdesc; + size_t buf_data_max = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + /* create bad keyname strings with invalid character code at start of keyname */ + buf_data_max = strlen(cfstore_open_ascii_illegal_buf_g); + name_len = CFSTORE_KEY_NAME_MAX_LENGTH; + memset(kv_name, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + + ret = cfstore_test_kv_name_gen(kv_name, name_len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate kv_name.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK , cfstore_open_utest_msg_g); + + /* pepper the illegal chars across the string*/ + for(i++; i < buf_data_max; i++){ + kv_name[rand() % (name_len+1)] = cfstore_open_ascii_illegal_buf_g[i]; + } + ret = cfstore_test_create(kv_name, kv_name, &name_len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: created KV in store when kv_name contains invalid characters (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret < ARM_DRIVER_OK, cfstore_open_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_open_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Uninitialize() >= ARM_DRIVER_OK, cfstore_open_utest_msg_g); + return CaseNext; +} + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(CFSTORE_OPEN_GREENTEA_TIMEOUT_S, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("OPEN_test_00", cfstore_open_test_00), + Case("OPEN_test_01_start", cfstore_utest_default_start), + Case("OPEN_test_01_end", cfstore_open_test_01_end), + Case("OPEN_test_02_start", cfstore_utest_default_start), + Case("OPEN_test_02_end", cfstore_open_test_02_end), + Case("OPEN_test_03_start", cfstore_utest_default_start), + Case("OPEN_test_03_end", cfstore_open_test_03_end), + Case("OPEN_test_04_start", cfstore_utest_default_start), + Case("OPEN_test_04_end", cfstore_open_test_04_end), + Case("OPEN_test_05_start", cfstore_utest_default_start), + Case("OPEN_test_05_end", cfstore_open_test_05_end), + Case("OPEN_test_06_start", cfstore_utest_default_start), + Case("OPEN_test_06_end", cfstore_open_test_06_end), + Case("OPEN_test_07_start", cfstore_utest_default_start), + Case("OPEN_test_07_end", cfstore_open_test_07_end), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/read/read.cpp b/TESTS/cfstore/read/read.cpp new file mode 100644 index 0000000000..ff3b4c59a2 --- /dev/null +++ b/TESTS/cfstore/read/read.cpp @@ -0,0 +1,265 @@ +/** @file read.cpp + * + * 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. + * + * Test cases to read KVs in the CFSTORE using the drv->Read() API call. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_read_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("READ_test_00", cfstore_read_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + + +#else + + +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +//#include +#include +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static char cfstore_read_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.read.box1"); +UVISOR_BOX_CONFIG(cfstore_read_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + +/* KV data for test_01 */ +static cfstore_kv_data_t cfstore_read_test_01_kv_data[] = { + CFSTORE_INIT_1_TABLE_MID_NODE, + { NULL, NULL}, +}; + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_read_test_00(const size_t call_count) +{ + (void) call_count; + CFSTORE_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", cfstore_driver.GetCapabilities().asynchronous_ops); + return CaseNext; +} + + +/* @brief check char at offset is as expected */ +static int32_t cfstore_read_seek_test(ARM_CFSTORE_HANDLE hkey, uint32_t offset, const char expected) +{ + ARM_CFSTORE_SIZE len = 1; + char read_buf[1]; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + ret = drv->Rseek(hkey, offset); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:failed to Rseek() to desired offset (offset=%d) (ret=%" PRId32 ").\n", __func__, (int) offset, ret); + goto out0; + } + ret = drv->Read(hkey, read_buf, &len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:failed to Read() (offset=%d)(ret=%" PRId32 ").\n", __func__, (int) offset, ret); + goto out0; + } + if(read_buf[0] != expected){ + ret = ARM_DRIVER_ERROR; + goto out0; + } +out0: + return ret; +} + +/** @brief + * + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_read_test_01_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(hkey); + cfstore_test_rw_data_entry_t *node; + ARM_CFSTORE_FMODE flags; + + CFSTORE_DBGLOG("%s:entered\n", __func__); + (void) call_count; + memset(&kdesc, 0, sizeof(kdesc)); + memset(&flags, 0, sizeof(flags)); + + /* create a key for reading */ + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(cfstore_read_test_01_kv_data[0].value); + ret = cfstore_test_create(cfstore_read_test_01_kv_data[0].key_name, (char*) cfstore_read_test_01_kv_data[0].value, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_read_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create KV in store (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_read_utest_msg_g); + + /* now open the newly created key */ + ret = drv->Open(cfstore_read_test_01_kv_data[0].key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_read_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open node (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, cfstore_read_test_01_kv_data[0].key_name, cfstore_read_test_01_kv_data[0].value, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_read_utest_msg_g); + + /* seek back and forth in key and check the characters are as expected */ + node = cfstore_test_rw_data_table; + while(node->offset != CFSTORE_TEST_RW_TABLE_SENTINEL) + { + ret = cfstore_read_seek_test(hkey, node->offset, node->rw_char); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_read_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Rseek() to offset (%d) didn't read expected char (\'%c\') (ret=%" PRId32 ")\n", __func__, (int) node->offset, node->rw_char, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_read_utest_msg_g); + node++; + } + CFSTORE_TEST_UTEST_MESSAGE(cfstore_read_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Close() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Close(hkey) >= ARM_DRIVER_OK, cfstore_read_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_read_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_read_utest_msg_g); + return CaseNext; +} + + +/** @brief + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_read_test_02_end(const size_t call_count) +{ + (void) call_count; + /*todo: implement test */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_read_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Warn: Not implemented\n", __func__); + CFSTORE_LOG("%s: WARN: requires implementation\n", __func__); + TEST_ASSERT_MESSAGE(true, cfstore_read_utest_msg_g); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(200, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("READ_test_00", cfstore_read_test_00), + Case("READ_test_01_start", cfstore_utest_default_start), + Case("READ_test_01_end", cfstore_read_test_01_end), + Case("READ_test_02_start", cfstore_utest_default_start), + Case("READ_test_02_end", cfstore_read_test_02_end), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/TESTS/cfstore/write/write.cpp b/TESTS/cfstore/write/write.cpp new file mode 100644 index 0000000000..667d24385c --- /dev/null +++ b/TESTS/cfstore/write/write.cpp @@ -0,0 +1,272 @@ +/** @file write.cpp + * + * 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. + * + * Test cases to write KVs in the CFSTORE using the drv->Write() API call. + */ +#if defined __MBED__ && ! defined TOOLCHAIN_GCC_ARM + + +#include +#include +#include +#include + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#include "cfstore_uvisor.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static control_t cfstore_write_test_00(const size_t call_count) +{ + (void) call_count; + printf("Not implemented for ARM toolchain\n"); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(100, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("WRITE_test_00", cfstore_write_test_00), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + + + +#else + + +#include +#include +#include + + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#include +#endif + +#include "cfstore_config.h" +#include "cfstore_test.h" +#include "cfstore_debug.h" +//#include +#include +#include +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "cfstore_utest.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +using namespace utest::v1; + +static char cfstore_write_utest_msg_g[CFSTORE_UTEST_MSG_BUF_SIZE]; + +/* Configure secure box. */ +#ifdef YOTTA_CFG_CFSTORE_UVISOR +UVISOR_BOX_NAMESPACE("com.arm.mbed.cfstore.test.write.box1"); +UVISOR_BOX_CONFIG(cfstore_write_box1, UVISOR_BOX_STACK_SIZE); +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + + +/* + * write tests + * cfstore_handle_t cfstore_write() + */ + +/* KV data for test_01 */ +static cfstore_kv_data_t cfstore_write_test_01_kv_data[] = { + CFSTORE_INIT_1_TABLE_MID_NODE, + { NULL, NULL}, +}; + + +/* report whether built/configured for flash sync or async mode */ +static control_t cfstore_write_test_00(const size_t call_count) +{ + (void) call_count; + CFSTORE_LOG("INITIALIZING: caps.asynchronous_ops=%lu\n", cfstore_driver.GetCapabilities().asynchronous_ops); + return CaseNext; +} + +/** @brief test to write many times to an open KV to test data is appended + * sequentially to the end of the value blob + * + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_write_test_01_end(const size_t call_count) +{ + char read_buf[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + uint32_t i = 0; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_DBGLOG("%s:entered\n", __func__); + (void) call_count; + memset(read_buf, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + memset(&kdesc, 0, sizeof(kdesc)); + memset(&flags, 0, sizeof(flags)); + + /* create an empty KV of the required length */ + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(cfstore_write_test_01_kv_data[0].value); + ret = cfstore_test_create(cfstore_write_test_01_kv_data[0].key_name, "one", &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create new KV (key_name=%s, ret=%" PRId32 ").\n", __func__, cfstore_write_test_01_kv_data[0].key_name, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_write_utest_msg_g); + + /* now open the newly created key and write repeated to created the string */ + flags.write = true; + ret = drv->Open(cfstore_write_test_01_kv_data[0].key_name, flags, hkey); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open node (key_name=\"%s\", value=\"%s\")(ret=%" PRId32 ")\n", __func__, cfstore_write_test_01_kv_data[0].key_name, cfstore_write_test_01_kv_data[0].value, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_write_utest_msg_g); + + for(i = 0; i < strlen(cfstore_write_test_01_kv_data[0].value); i++) + { + len = 1; + ret = drv->Write(hkey, &cfstore_write_test_01_kv_data[0].value[i], &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Write failed for char (\'%c\') (ret=%" PRId32 ")\n", __func__, cfstore_write_test_01_kv_data[0].value[i], ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_write_utest_msg_g); + } + /* check that the value created in the key is as expected*/ + len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + ret = drv->Read(hkey, read_buf, &len); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Read failed (ret=%" PRId32 ")\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_write_utest_msg_g); + + /* check node key_names are identical */ + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: KV value (%s) is not as expected (%s).\n", __func__, read_buf, cfstore_write_test_01_kv_data[0].value); + TEST_ASSERT_MESSAGE(strncmp(read_buf, cfstore_write_test_01_kv_data[0].value, strlen(cfstore_write_test_01_kv_data[0].value)) == 0, cfstore_write_utest_msg_g); + + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Close() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(drv->Close(hkey) >= ARM_DRIVER_OK, cfstore_write_utest_msg_g); + + cfstore_test_delete_all(); + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_write_utest_msg_g); + return CaseNext; +} + +/** @brief test to write with a NULL buffer, which should fail gracefully + * + * + * @return status code + * ARM_DRIVER_OK, the test passed successfully + * ret < ARM_DRIVER_OK, the test failed with the return code + * supplying more details + */ +static control_t cfstore_write_test_02_end(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_DBGLOG("%s:entered\n", __func__); + (void) call_count; + memset(&kdesc, 0, sizeof(kdesc)); + + /* create an empty KV of the required length */ + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + len = strlen(cfstore_write_test_01_kv_data[0].value); + ret = cfstore_test_create(cfstore_write_test_01_kv_data[0].key_name, NULL, &len, &kdesc); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error KV creation should have failed due to null write buffer but something else happended (ret=%" PRId32 ").\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret == ARM_CFSTORE_DRIVER_ERROR_INVALID_WRITE_BUFFER, cfstore_write_utest_msg_g); + + ret = drv->Uninitialize(); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_write_utest_msg_g, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: Uninitialize() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_write_utest_msg_g); + return CaseNext; +} + + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(200, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("WRITE_test_00", cfstore_write_test_00), + Case("WRITE_test_01_start", cfstore_utest_default_start), + Case("WRITE_test_01_end", cfstore_write_test_01_end), + Case("WRITE_test_02_start", cfstore_utest_default_start), + Case("WRITE_test_02_end", cfstore_write_test_02_end), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +/* mbedosV3*/ +void app_start(int argc __unused, char** argv __unused) +{ + /* Run the test specification */ + Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 4 +/* mbedosV3++*/ +int main() +{ + return !Harness::run(specification); +} +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 4 */ + + +#endif // __MBED__ && ! defined TOOLCHAIN_GCC_ARM diff --git a/storage/cfstore/LICENSE b/storage/cfstore/LICENSE new file mode 100644 index 0000000000..3ebeebb336 --- /dev/null +++ b/storage/cfstore/LICENSE @@ -0,0 +1,8 @@ +Unless specifically indicated otherwise in a file, files are licensed +2 under the Apache 2.0 license, as can be found in: apache-2.0.txt + +Source code in cfstore_fnmatch.c is licensed under the agreements in the +following files: +- berkeley.txt +- chernov.txt +- tatmanjants.txt \ No newline at end of file diff --git a/storage/cfstore/Makefile.scripts b/storage/cfstore/Makefile.scripts new file mode 100644 index 0000000000..b9067d61a7 --- /dev/null +++ b/storage/cfstore/Makefile.scripts @@ -0,0 +1,65 @@ +########################################################################### +# +# Copyright (c) 2013-2015, 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. +# +########################################################################### + +# +# inline debugging/flashing scripts +# +define __SCRIPT_GDB + target remote $(DEBUG_HOST) + monitor endian little + monitor reset + monitor halt + monitor semihosting enable + monitor speed 1000 + monitor loadbin $(TARGET_BIN) 0 + monitor flash device = $(CPU) + load $(TARGET) + file $(TARGET) + $(GDB_DEBUG_UVISOR) + b vmpu_init_post + b app_start +endef +export __SCRIPT_GDB + +define __SCRIPT_FLASH + r + loadbin $(TARGET_BIN) 0 + r + g + q +endef +export __SCRIPT_FLASH + +define __SCRIPT_ERASE + h + Sleep 100 + unlock kinetis + Sleep 100 + erase + q +endef +export __SCRIPT_ERASE + +define __SCRIPT_RESET + h + r + g + q +endef +export __SCRIPT_RESET diff --git a/storage/cfstore/README.md b/storage/cfstore/README.md new file mode 100644 index 0000000000..64b9d01251 --- /dev/null +++ b/storage/cfstore/README.md @@ -0,0 +1,112 @@ +# Secure Key-Value Storage # + + +## Executive Summary + +The Configuration Store (CFSTORE) is a secure, +associative key-value (KV) store C-Language Hardware Abstraction Layer. +CFSTORE provides the secure and persistent storage for: +- Storing encryption keys data. +- Storing configuration data. +- Storing firmware, firmware updates and incremental firmware blocks for assembling into a firmware update. + +These services are presented to clients with: +- A conceptually simple, file-like interface for storing and managing data using (key, value) pairs in + persistent storage media. +- A simple, hardware-independent API to promote portability across multiple platforms and a low attack surface. +- A very small code/memory footprint so CFSTORE is capable of running on highly-constrained memory systems (~10kB free memory) + where typically available SRAM << NV storage. +- A simple (low complexity) storage capability at the expense of features. For example, CFSTORE only supports the storage of + binary blobs rather than a rich set of data types. + +Current support includes: +- NV-backed support. Integration with Flash Abstraction (Flash Journal Strategy Sequential) for persistent storage on the Freescale FRDM K64F target. +- SRAM backed support. +- More than 60 test cases with >80% test coverage. +- Comprehensive documentation. + + +# Configuration-Store Software Architecture + +```C + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Configuration Store Client | + | e.g. FOTA | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Configuration Store | | uvisor | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + | Flash Abstraction Layer | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + | Flash Driver Layer | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + SW + ----------------------------------------------------------------------- + HW + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NV Storage Media e.g. Flash | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Configuration Store Software Architecture + +``` + +The above figure shows the following entities (from top to bottom): +- A Configuration Store client e.g. FOTA. +- Configuration Store, the associative KV pair store. +- Flash Abstraction Layer, portable across the driver layer. +- Flash Driver layer e.g. CMSIS-Driver. +- NV Storage Media. These are the physical storage media. + + +# Providing Feedback + +If you would like to make a contribution to CFSTORE, please provide feedback/designs/comments/code in one of the following ways: +- By logging an issue in the CFSTORE repo. +- By submitting a Pull Request to the CFSTORE repo. +- By sending an email to: +-- simon.hughes@arm.com +-- milosch.meriac@arm.com + + +# Further Reading + +* The [CFSTORE Getting Started Guide.][CFSTORE_GETSTART] +* The [CFSTORE Client Example 3 for flash-journal synchronous mode only (simpler code).][CFSTORE_EX3] +* The [CFSTORE Client Example 1 for both flash-journal modes (asynchronous and synchronous)(more complicated but versatile code).][CFSTORE_EX1] +* The CFSTORE Product Requirements were not written. +* The [CFSTORE Engineering Requirements.][CFSTORE_ENGREQ] +* The [CFSTORE High Level Design Document.][CFSTORE_HLD] +* The [CFSTORE Low Level Design Document.][CFSTORE_LLD] +* The [CFSTORE Project Test Plan describing the test methodologies and test cases.][CFSTORE_TESTPLAN] +* The [CFSTORE Release Notes.][CFSTORE_RELEASES] +* The [CFSTORE Jenkins Build and Test Results.][CFSTORE_JENKINS_BT] +* The [CFSTORE Jenkins Code Coverage Results.][CFSTORE_JENKINS_COV] +* The [CFSTORE Project Plan describing milestones and roadmap.][CFSTORE_PROJPLAN] +* The [CFSTORE Project Plan Excel Spreadsheet with estimates and milestones][CFSTORE_PROJXLS] +* The [Flash Abstraction Layer.][FAL] + + +[CFSTORE_ENGREQ]: https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_requirements.md +[CFSTORE_EX1]: https://github.com/ARMmbed/configuration-store-example1 +[CFSTORE_EX3]: https://github.com/ARMmbed/configuration-store-example3 +[CFSTORE_GETSTART]: https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_getting_started.md +[CFSTORE_HLD]: https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_hld.md +[CFSTORE_JENKINS_BT]: http://e108747.cambridge.arm.com:8080/job/configuration-store-nightly-build-and-test/ +[CFSTORE_JENKINS_COV]: http://e108747.cambridge.arm.com:8080/job/configuration-store-test-coverage/ +[CFSTORE_LLD]: https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_lld.md +[CFSTORE_TESTPLAN]: https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_test_plan.md +[CFSTORE_PRODREQ]: https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_product_requirements.md +[CFSTORE_PROJPLAN]:https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_project.md +[CFSTORE_PROJXLS]:https://github.com/ARMmbed/configuration-store/blob/master/doc/project/ARM_MBED_TN_0020_cfstore_project_plan.xlsx +[CFSTORE_RELEASES]:https://github.com/ARMmbed/configuration-store/blob/master/doc/project/configuration_store_releases.md +[CFSTORE_TERM]: https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_terminology.md +[FAL]: https://github.com/ARMmbed/flash-abstraction diff --git a/storage/cfstore/VERSION b/storage/cfstore/VERSION new file mode 100644 index 0000000000..a3c7a02f9b --- /dev/null +++ b/storage/cfstore/VERSION @@ -0,0 +1,3 @@ +{ + "version": "0.3.3", +} diff --git a/storage/cfstore/apache-2.0.txt b/storage/cfstore/apache-2.0.txt new file mode 100644 index 0000000000..35750c5de1 --- /dev/null +++ b/storage/cfstore/apache-2.0.txt @@ -0,0 +1,13 @@ +Copyright (c) 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. diff --git a/storage/cfstore/berkeley.txt b/storage/cfstore/berkeley.txt new file mode 100644 index 0000000000..85d6eacfe0 --- /dev/null +++ b/storage/cfstore/berkeley.txt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ \ No newline at end of file diff --git a/storage/cfstore/chernov.txt b/storage/cfstore/chernov.txt new file mode 100644 index 0000000000..a81f43cd23 --- /dev/null +++ b/storage/cfstore/chernov.txt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 1996 by Andrey A. Chernov, Moscow, Russia. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ diff --git a/storage/cfstore/configuration-store/configuration_store.h b/storage/cfstore/configuration-store/configuration_store.h new file mode 100644 index 0000000000..560b29753d --- /dev/null +++ b/storage/cfstore/configuration-store/configuration_store.h @@ -0,0 +1,949 @@ +/** @file configure-store.h + * + * 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. + * + * This is the interface file to configuration store service. + */ +#ifndef __CONFIGURATION_STORE_H +#define __CONFIGURATION_STORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include /* requierd for memset() in ARM_CFSTORE_HANDLE_INIT() */ + +#define DEVICE_STORAGE 1 /* enable storage */ +#include +#include + +#define ARM_CFSTORE_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(1,0) /* API version */ +#define ARM_CFSTORE_DRV_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(0,1) /* DRV version */ + +/* CFSTORE Specific error codes*/ +#define ARM_CFSTORE_DRIVER_ERROR_UNINITIALISED -1000 /* Start of driver specific errors */ +#define ARM_CFSTORE_DRIVER_ERROR_PREEXISTING_KEY -1001 +#define ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND -1002 +#define ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE -1003 +#define ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY -1004 +#define ARM_CFSTORE_DRIVER_ERROR_PREEXISTING_KEY_DELETING -1005 +#define ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE_BUF -1006 +#define ARM_CFSTORE_DRIVER_ERROR_INTERNAL -1007 +#define ARM_CFSTORE_DRIVER_ERROR_INVALID_KEY_NAME -1008 +#define ARM_CFSTORE_DRIVER_ERROR_VALUE_SIZE_TOO_LARGE -1009 +#define ARM_CFSTORE_DRIVER_ERROR_KEY_READ_ONLY -1010 +#define ARM_CFSTORE_DRIVER_ERROR_INVALID_SEEK -1011 +#define ARM_CFSTORE_DRIVER_ERROR_KEY_UNREADABLE -1012 +#define ARM_CFSTORE_DRIVER_ERROR_INVALID_WRITE_BUFFER -1013 +#define ARM_CFSTORE_DRIVER_ERROR_INVALID_KEY_LEN -1014 +#define ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED -1015 +#define ARM_CFSTORE_DRIVER_ERROR_INVALID_READ_BUFFER -1016 +#define ARM_CFSTORE_DRIVER_ERROR_INVALID_KEY_DESCRIPTOR -1017 +#define ARM_CFSTORE_DRIVER_ERROR_PERM_NO_READ_ACCESS -1018 +#define ARM_CFSTORE_DRIVER_ERROR_PERM_NO_WRITE_ACCESS -1019 +#define ARM_CFSTORE_DRIVER_ERROR_PERM_NO_EXECUTE_ACCESS -1020 +#define ARM_CFSTORE_DRIVER_ERROR_NO_PERMISSIONS -1021 +#define ARM_CFSTORE_DRIVER_ERROR_HANDLE_COUNT_MAX -1022 +#define ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_ERROR -1023 +#define ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_BUSY -1024 +#define ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_TIMEOUT -1025 +#define ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_UNSUPPORTED -1026 +#define ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_PARAMETER -1027 +#define ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_BOUNDED_CAPACITY -1028 +#define ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_STORAGE_API_ERROR -1029 +#define ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_STORAGE_IO_ERROR -1030 +#define ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_NOT_INITIALIZED -1031 +#define ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_ATTEMPTING_COMMIT_WITHOUT_WRITE -1032 +#define ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_EMPTY -1033 +#define ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_SMALL_LOG_REQUEST -1034 +#define ARM_CFSTORE_DRIVER_ERROR_OPERATION_PENDING -1035 +#define ARM_CFSTORE_DRIVER_ERROR_UVISOR_BOX_ID -1036 +#define ARM_CFSTORE_DRIVER_ERROR_UVISOR_NAMESPACE -1037 + + +/* + * Type Definitions + * + * ARM_CFSTORE_HANDLE + * opaque cfstore handle for manipulating cfstore data objects e.g. KV pairs. + */ +typedef void *ARM_CFSTORE_HANDLE; +typedef size_t ARM_CFSTORE_SIZE; +typedef size_t ARM_CFSTORE_OFFSET; + +typedef struct _ARM_CFSTORE_STATUS { + uint32_t in_progress : 1; + uint32_t error : 1; +} ARM_CFSTORE_STATUS; + + +/* Defines + * + * CFSTORE_KEY_NAME_MAX_LENGTH + * The maximum length of the null terminated character string used as a + * key name string. + * + * CFSTORE_VALUE_SIZE_MAX + * Max size of the KV value blob (currently 64MB) + * + * CFSTORE_HANDLE_BUFSIZE + * size of the buffer owned and supplied by client to CFSTORE to hold internal + * data structures, referenced by the key handle. Note this can change on + * different platforms depending on the sizeof types. + * + */ +#define CFSTORE_KEY_NAME_MAX_LENGTH 220 +#define CFSTORE_VALUE_SIZE_MAX (1<<26) + +#ifdef TARGET_LIKE_FRDM_K64F_GCC +#define CFSTORE_HANDLE_BUFSIZE 24 +#endif + +#ifdef TARGET_LIKE_X86_LINUX_NATIVE +#define CFSTORE_HANDLE_BUFSIZE 40 +#endif + +/* @brief Helper macro to declare handle and client owned buffer supplied + * to CFSTORE for storing opaque handle state */ +#define ARM_CFSTORE_HANDLE_INIT(__name) \ + uint8_t (__name##_buf_cFsToRe)[CFSTORE_HANDLE_BUFSIZE]; \ + ARM_CFSTORE_HANDLE (__name) = (ARM_CFSTORE_HANDLE) (__name##_buf_cFsToRe); \ + memset((__name##_buf_cFsToRe), 0, CFSTORE_HANDLE_BUFSIZE) + +/* @brief Helper macro to swap 2 handles, which is useful for the Find() + * idiom (see CFSTORE_NOTE1 later). + */ +#if defined __MBED__ && defined TOOLCHAIN_GCC_ARM +#define CFSTORE_HANDLE_SWAP(__a_HaNdLe, __b_HaNdLe) \ + do{ ARM_CFSTORE_HANDLE __temp_HaNdLe = (__a_HaNdLe); \ + __asm volatile("" ::: "memory"); \ + (__a_HaNdLe) = (__b_HaNdLe); \ + __asm volatile("" ::: "memory"); \ + (__b_HaNdLe) = (__temp_HaNdLe); \ + __asm volatile("" ::: "memory"); \ + }while(0) +#endif + +#if defined __MBED__ && defined TOOLCHAIN_ARM +/* todo: implment this macro */ +#define CFSTORE_HANDLE_SWAP(__a_HaNdLe, __b_HaNdLe) \ + do{ ARM_CFSTORE_HANDLE __temp_HaNdLe = (__a_HaNdLe); \ + __dmb(0xf); \ + (__a_HaNdLe) = (__b_HaNdLe); \ + __dmb(0xf); \ + (__b_HaNdLe) = (__temp_HaNdLe); \ + __dmb(0xf); \ + }while(0) +#endif + +/* @brief The access control permissions for the key-value. + * @note A client requires the perm_xxx_write set to be able to + * delete the KV. + * @param perm_owner_read + * if set => this KV is owner readable + * @param perm_owner_write + * if set => this KV is owner writable + * @param perm_owner_execute (currently not supported) + * if set => this KV is owner executable + * @param perm_other_read + * if set => this KV is world readable + * @param perm_other_write + * if set => this KV is world writable + * @param perm_other_execute (currently not supported) + * if set => this KV is world executable + * @param reserved + * reserved for future use. + */ +typedef struct ARM_CFSTORE_ACCESS_CONTROL_LIST +{ + uint32_t perm_owner_read : 1; + uint32_t perm_owner_write : 1; + uint32_t perm_owner_execute : 1; + uint32_t perm_other_read : 1; + uint32_t perm_other_write : 1; + uint32_t perm_other_execute : 1; + uint32_t reserved : 26; +} ARM_CFSTORE_ACCESS_CONTROL_LIST; + + +/* @brief file mode bitfield structure for specifying how flags for the + * following operations: + * - ARM_CFSTORE_DRIVER::(*Create)(), when creating a KV. + * - ARM_CFSTORE_DRIVER::(*Open)(), when opening a pre-existing KV. + * + * continuous + * if set, the key value should be stored in a continuous sequence of hardware + * addresses + * + * lazy_flush + * if set then configuration store will defer flushing the KV + * changes until an optimal time. e.g. to save energy rather than performing + * the operation immediately. + * + * flush_on_close + * if set then the key-value should be flushed to the backing store when + * the key is closed. + * + * read + * if set then the KV can be read + * + * write + * if set then the KV can be written + * + * storage_detect + * if set then the call to ARM_CFSTORE_DRIVER::(*Create)() returns whether + * a key could be created with the required storage characteristics. The + * key is not created. + */ + +typedef struct ARM_CFSTORE_FMODE +{ + uint32_t continuous : 1; + uint32_t lazy_flush : 1; + uint32_t flush_on_close : 1; + uint32_t read : 1; + uint32_t write : 1; + uint32_t execute : 1; + uint32_t storage_detect : 1; + uint32_t reserved : 25; +} ARM_CFSTORE_FMODE; + + +/** + * @brief descriptor used to create keys + * + * @param acl + * Access Control List specifying the access permissions of the KV. + * @param drl + * data retention level for the KV required by the client. CFSTORE will + * store the KV in the specified type store/media. + * @param security + * flash security properties for the KV required by the client. CFSTORE + * will store the KV in a storage media supporting the specified + * security attributes. + * @param flags + * A bitfield containing the access mode setting for the key. + * + */ +typedef struct ARM_CFSTORE_KEYDESC +{ + /*key descriptor attributes */ + ARM_CFSTORE_ACCESS_CONTROL_LIST acl; + uint8_t drl; + ARM_STORAGE_SECURITY_FEATURES security; + ARM_CFSTORE_FMODE flags; +} ARM_CFSTORE_KEYDESC; + + +/* ARM_CFSTORE_OPCODE, ARM_CFSTORE_CALLBACK and the Asynchronous Completion + * of Driver Dispatch methods [REFERENCE_1] + * + * Configuration Store driver dispatch methods can operate in 2 modes: + + * - Synchronous i.e. when: + * ARM_CFSTORE_CAPABILITIES::asynchronous_ops == 0 + * then the ARM_CFSTORE_DRIVER::Dispatch_Method_Xxx() will return: + * - >= 0 => CFSTORE Dispatch_Method_Xxx() completed successfully + * - otherwise CFSTORE Dispatch_Method_Xxx() did not complete successfully + * (return code supplies further details). + * In synchronous mode there it is optional as to whether the client registers + * a client callback with the Initialize() method. If one is registered, it + * will be called to indicate the synchronous completion status. + * + * - Asynchronous i.e. when: + * ARM_CFSTORE_CAPABILITIES::asynchronous_ops == 1 + * then the ARM_CFSTORE_DRIVER::Dispatch_Method_Xxx() will return: + * - return_value = ARM_DRIVER_OK (i.e. ==0) implies CFSTORE + * Dispatch_Method_Xxx() transaction pending. Dispatch_Method_Xxx + * completion status will be indicated via + * an asynchronous call to ARM_CFSTORE_CALLBACK registered with the + * ARM_CFSTORE_DRIVER::(*Initialize)(), where the status supplied to + * the callback will be the final status of the call. + * - return_value > ARM_DRIVER_OK (i.e. > 0) implies CFSTORE Dispatch_Method_Xxx() completely + * synchronously and successfully. The return_value has specific + * meaning for the Dispatch_Method_Xxx() e.g. for the Read() method + * return_value is the number of bytes read. + * - otherwise return_value < 0 => CFSTORE Dispatch_Method_Xxx() + * completed unsuccessfully (return code supplies further details). + * + * The client registered asynchronous callback method ARM_CFSTORE_CALLBACK is + * registered by the client using: + * ARM_CFSTORE_DRIVER::(*Initialize)(ARM_CFSTORE_CALLBACK callback, + * void* client_context) + * See the (*Initialize) documentation for more details. + * + * The registered callback has the following prototype: + * + * typedef void (*ARM_CFSTORE_CALLBACK)( + * int32_t status, + * ARM_CFSTORE_OPCODE cmd_code, + * void *client_context, + * ARM_CFSTORE_HANDLE handle); + * + * Before an asynchronous notification is received, a client can check on the + * status of the call by calling ARM_CFSTORE_DRIVER::(*GetStatus)(). + * + */ +typedef enum ARM_CFSTORE_OPCODE { + CFSTORE_OPCODE_CLOSE = 1, + CFSTORE_OPCODE_CREATE, + CFSTORE_OPCODE_DELETE, + CFSTORE_OPCODE_FIND, + CFSTORE_OPCODE_FLUSH, + CFSTORE_OPCODE_GET_KEY_NAME, + CFSTORE_OPCODE_GET_STATUS, + CFSTORE_OPCODE_GET_VALUE_LEN, + CFSTORE_OPCODE_INITIALIZE, + CFSTORE_OPCODE_OPEN, + CFSTORE_OPCODE_POWER_CONTROL, + CFSTORE_OPCODE_READ, + CFSTORE_OPCODE_RSEEK, + CFSTORE_OPCODE_UNINITIALIZE, + CFSTORE_OPCODE_WRITE, + CFSTORE_OPCODE_MAX +} ARM_CFSTORE_OPCODE; + + +/* [REFERENCE_2] + * + * @brief client registered callback type for receiving asynchronous event + * notifications. + * + * @param status + * In the case that cmd_code is one of ARM_CFSTORE_OPCODE then this + * is the asynchronous completion status of the associated + * ARM_CFSTORE_DRIVER::Dispatch_Method_Xxx() + * @param cmd_code + * ARM_CFSTORE_OPCODE for the + * ARM_CFSTORE_DRIVER::Dispatch_Method_Xxx() for which this + * invocation of the callback is an asynchronous completion event. + * @param client_context + * The client ARM_CFSTORE_CALLBACK context registered with the + * ARM_CFSTORE_DRIVER::(*Initialize)() method. + * @param handle + * For certain ARM_CFSTORE_DRIVER::Dispatch_Method_Xxx() e.g. + * ARM_CFSTORE_DRIVER::(*Create)() + * ARM_CFSTORE_DRIVER::(*Open)() + * ARM_CFSTORE_DRIVER::(*Find)() + * then an open key handle is supplied upon asynchronous completion. + * See the documentation of the ARM_CFSTORE_OpCode_e and the + * ARM_CFSTORE_DRIVER::Dispatch_Method_Xxx() for further details. + * + */ +typedef void (*ARM_CFSTORE_CALLBACK)(int32_t status, ARM_CFSTORE_OPCODE cmd_code, void *client_context, ARM_CFSTORE_HANDLE handle); + + +/* @brief Find the capabilities of the configuration store. + * + * @param asynchronous_ops + * If set => the configuration store dispatch interface is operating in + * non-blocking (asynchronous) mode. + * If clr => the configuration store dispatch interface is operating in + * blocking (synchronous) mode. + * @param uvisor_support_enabled + * The configuration store is using uvisor security contexts. + */ + +typedef struct ARM_CFSTORE_CAPABILITIES +{ + uint32_t asynchronous_ops : 1; + uint32_t uvisor_support_enabled : 1; +} ARM_CFSTORE_CAPABILITIES; + + +/** + * This is the set of operations constituting the Configuration Store driver. + * + * The functions dispatch methods can operate synchronously or asynchronously + * depending on the underlying implementation. The client of this interface + * should not assume one or other mode of operation but use the + * (*GetCapabilities) method to determine the operational mode and then + * behave accordingly. + */ +typedef struct _ARM_DRIVER_CFSTORE +{ + /** + * @brief Get driver version. + * + * The synchronous function GetVersion() returns version information of the + * driver implementation in ARM_DRIVER_VERSION. + * - ARM_DRIVER_VERSION::api + * the version of the CMSIS-Driver specification used to implement + * this driver. + * - ARM_DRIVER_VERSION::drv + * the source code version of the actual configuration store driver + * implementation. + * + * @return \ref ARM_DRIVER_VERSION, the configuration store driver + * version information + */ + ARM_DRIVER_VERSION (*GetVersion)(void); + + + /** + * @brief Close the hkey context previously recovered from CFSTORE. + * + * @param hkey + * IN: a previously returned handle from (*Create((), (*Open)() + * or (*Find)() to close. + * + * @return return_value + * + * See REFERENCE_1 and REFERENCE_2. + * ARM_CFSTORE_DRIVER::(*Close)() asynchronous completion command code + * (*ARM_CFSTORE_CALLBACK) function argument values on return: + * @param status + * ARM_DRIVER_OK => success, KV deleted, else failure. + * @param cmd_code == CFSTORE_OPCODE_CLOSE + * @param client context + * as registered with ARM_CFSTORE_DRIVER::(*Initialize)() + * @param hkey == NULL + */ + int32_t (*Close)(ARM_CFSTORE_HANDLE hkey); + + + /** + * @brief Create a key-value int he configuration strore. + * + * Once created, the following attributes of a KV cannot be changed: + * - the key_name + * - the permissions or attributes specifies by the key descriptor kdesc. + * To change these properties, the key must be deleted and then created + * again with the new properties. + * + * @param key_name + * IN: zero terminated string specifying the key name. + * @param value_len + * the client specifies the length of the value data item in the + * KV being created. + * @param kdesc + * IN: key descriptor, specifying how the details of the key + * create operations. Note the following cases: + * 1) if key_name is not present in the CFSTORE and kdesc is NULL + * (i.e. a new KV pair is being created), + * then CFSTORE will chose the most appropriate defaults + * e.g. CFSTORE will store the KV in nv store if the value size + * is large, with no security guarantees just safety. + * 2) if key_name is present in the CFSTORE and kdesc is NULL + * (i.e. there is a pre-existing KV pair), + * then CFSTORE will grow/shrink the value data buffer to + * value_len octets. This idiom can be used to increase/decrease + * the size of the KV value storage. + * 3) If the kdesc->flags.storage_detect == 1 then the function + * does not create the key but reports whether the key + * with the specified size and storage media attributes + * could/could not be created by configuration storage. + * @param hkey + * IN: pointer to client owned buffer of CFSTORE_HANDLE_BUFSIZE + * bytes + * OUT: on success, hkey is a valid handle to a KV. + * + * @return return_value + * + * See REFERENCE_1 and REFERENCE_2. + * ARM_CFSTORE_DRIVER::(*Create)() asynchronous completion command code + * (*ARM_CFSTORE_CALLBACK) function argument values on return: + * @param status + * ARM_DRIVER_OK => success, KV deleted, else failure. + * @param cmd_code == CFSTORE_OPCODE_CREATE + * @param client context + * as registered with ARM_CFSTORE_DRIVER::(*Initialize)() + * @param hkey now contains returned handle to newly created key. + */ + int32_t (*Create)(const char* key_name, ARM_CFSTORE_SIZE value_len, const ARM_CFSTORE_KEYDESC* kdesc, ARM_CFSTORE_HANDLE hkey); + + + /** + * @brief delete key-value from configuration store + * + * This method is used in the following ways: + * - (*Open)() to get a handle to the key, (*Delete)() to delete the key, + * (*Close)() to close the handle to the key. + * - (*Find)() to get a handle to the key, (*Delete)() to delete the key, + * (*Close)() to close the handle to the key. + * + * @param hkey + * IN: the handle of the key to delete. + * + * @return return_value + * + * See REFERENCE_1 and REFERENCE_2. + * ARM_CFSTORE_DRIVER::(*Delete)() asynchronous completion command code + * (*ARM_CFSTORE_CALLBACK) function argument values on return: + * @param status + * ARM_DRIVER_OK => success, KV deleted, else failure. + * @param cmd_code == CFSTORE_OPCODE_DELETE + * @param client context + * as registered with ARM_CFSTORE_DRIVER::(*Initialize)() + * @param hkey, unused. + * + */ + int32_t (*Delete)(ARM_CFSTORE_HANDLE hkey); + + + /** + * @brief find stored keys that match a query string + * + * find a list of pre-existing keys according to a query pattern. + * The search pattern can have the following formats + * - 'com.arm.mbed.wifi.accesspoint.essid'. Find whether an object + * exists that has a key matching + * 'com.arm.mbed.wifi.accesspoint.essid' + * - 'com.arm.mbed.wifi.accesspoint*.essid'. Find all keys that + * start with the substring 'com.arm.mbed.wifi.accesspoint' and + * end with the substring '.essid' + * - 'yotta.your-yotta-registry-module-name.*'. Find all key_name + * strings that begin with the substring + * 'yotta.your-yotta-registry-module-name.' + * - 'yotta.hello-world.animal[dog][foot][*]'. Find all key_name + * strings beginning yotta.hello-world.animal[dog][foot] + * - 'yotta.hello-world.animal[dog][foot]*' + * - 'yotta.hello-world.animal[dog*3]' + * + * It is possible to iterate over the list of matching keys by + * using the idiom below (in the synchronous case)(CFSTORE_NOTE1) + * + * int32_t ret = ARM_DRIVER_ERROR; + * const char* key_name_query = "*"; + * char key_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + * uint8_t len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + * ARM_CFSTORE_HANDLE_INIT(next); + * ARM_CFSTORE_HANDLE_INIT(prev); + * + * while(drv->Find(key_name_query, prev, next) == ARM_DRIVER_OK) + * { + * len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + * drv->GetKeyName(next, key_name, &len); + * printf("Found matching key-value with key_name=%s\n", key_name); + * CFSTORE_HANDLE_SWAP(prev, next); + * } + * + * The iteration over the find results can be terminated before + * the end of the list is reached by closing the last open + * file handle and not making any further calls to + * find() e.g. + * + * int32_t ret = ARM_DRIVER_ERROR; + * const char* key_name_query = "*"; + * char key_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + * uint8_t len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + * ARM_CFSTORE_HANDLE_INIT(next); + * ARM_CFSTORE_HANDLE_INIT(prev); + * + * while(drv->Find(key_name_query, prev, next) == ARM_DRIVER_OK) + * { + * len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + * drv->GetKeyName(next, key_name, &len); + * if(strncmp(key_name, "my.key_name", CFSTORE_KEY_NAME_MAX_LENGTH)) + * { + * printf("Found matching key-value with key_name=%s\n", key_name; + * // terminating walk of find results + * drv->Close(next); + * break; + * } + * CFSTORE_HANDLE_SWAP(prev, next); + * } + * + * @param key_name_query + * IN: a search string to find. This can include the wildcard '*' + * character + * @param previous + * IN: On the first call to (*Find) then previous is a pointer + * (the handle) to a buffer initialised to 0. + * On the subsequent calls to (*Find), a previously returned + * key handle can be supplied as the previous argument. The next + * available search result will be returned. If no further search + * results are available then (*Find) will return + * ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND. Once the + * file handle has been supplied to the function as the prev + * argument, CFSTORE close the open file handle. Otherwise, the + * client must call (*Close)() on the open file handle. + * @param next + * IN: pointer to client owned buffer of CFSTORE_HANDLE_BUFSIZE + * bytes to hold the + * OUT: Success: In the case that a match has been found then hkey + * is a valid handle to an open KV. The key must be explicitly closed + * by the client. Note this is a read-only key. + * Async operation: The storage at hkey must be available until after + * the completion notification has been received by the client. + * @return return_value + * On success (finding a KV matching the query) ARM_DRIVER_OK is + * returned. If a KV is not found matching the description then + * ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND is returned. + * See REFERENCE_1 and REFERENCE_2. + * ARM_CFSTORE_DRIVER::(*Find)() asynchronous completion command code + * (*ARM_CFSTORE_CALLBACK) function argument values on return: + * @param status + * ARM_DRIVER_OK => success, hkey contains open key + * else failure. + * @param cmd_code == CFSTORE_OPCODE_FIND + * @param client context + * as registered with ARM_CFSTORE_DRIVER::(*Initialize)() + * @param hkey + * ARM_DRIVER_OK => contains returned handle to newly found key. + * else, indeterminate data. + */ + int32_t (*Find)(const char* key_name_query, const ARM_CFSTORE_HANDLE previous, ARM_CFSTORE_HANDLE next); + + + /** + * @brief flush (write) the configuration changes to the nv backing + * store. + * + * @note all open key handles must be closed before flushing the CFSTORE to nv + * backing store. + * + * @return return_value + * See REFERENCE_1 and REFERENCE_2. + * ARM_CFSTORE_DRIVER::(*Flush)() asynchronous completion command code + * (*ARM_CFSTORE_CALLBACK) function argument values on return: + * @param status + * ARM_DRIVER_OK => success, hkey contains open key + * else failure. + * @param cmd_code == CFSTORE_OPCODE_FLUSH + * @param client context, registered ARM_CFSTORE_DRIVER::(*Initialize)() + * @param hkey, unused + */ + int32_t (*Flush)(void); + + + /** + * @brief Get configuration store capabilities. + * + * This synchronous function returns a ARM_CFSTORE_CAPABILITIES + * information structure describing configuration store implementation + * attributes. + * + * @return \ref ARM_CFSTORE_CAPABILITIES + */ + ARM_CFSTORE_CAPABILITIES (*GetCapabilities)(void); + + + /** + * @brief get the name of an open key handle + * + * @param hkey + * open handle of key to get the key_name + * @param key_name. The key name string is guaranteed to be terminated + * with '\0'. + * @param key_len, the length of the buffer available at data to receive + * the key_name string + * @return return_value + * See REFERENCE_1 and REFERENCE_2. + * ARM_CFSTORE_DRIVER::(*GetKeyName)() asynchronous completion command code + * (*ARM_CFSTORE_CALLBACK) function argument values on return: + * @param status + * ARM_DRIVER_OK => success, key_name contains key_name string, len + * contains length of string. + * else failure. + * @param cmd_code == CFSTORE_OPCODE_GET_KEY_NAME + * @param client context, registered ARM_CFSTORE_DRIVER::(*Initialize)() + * @param hkey supplied to the GetKeyName() call at hkey. + * @param key_name + * on success, the buffer holds the name of the key + * @param key_len + * on success, the supplied integer is set to strlen(key_name)+1, where + * the additional character corresponds to the terminating null. + * + */ + int32_t (*GetKeyName)(ARM_CFSTORE_HANDLE hkey, char* key_name, uint8_t *key_len); + + + /** + * @brief Get the status of the configuration store. + * + * @return return_value + * See REFERENCE_1 and REFERENCE_2 + * ARM_DRIVER_OK if the command has been accepted and + * enqueued by the underlying controller, else an appropriate + * error code. Getting a return value of ARM_DRIVER_OK + * implies that basic argument validation was successful, and the + * caller can expect a command completion callback to be invoked + * at a later time. + * + * ARM_CFSTORE_DRIVER::(*GetStatus)() asynchronous completion command code + * (*ARM_CFSTORE_CALLBACK) function argument values on return: + * @param status + * status of next command pending completion + * @param cmd_code + * next command pending completion. + * @param client context registered ARM_CFSTORE_DRIVER::(*Initialize)() + * @param hkey + * unused. + */ + ARM_CFSTORE_STATUS (*GetStatus)(void); + + + /** + * @brief get the value length from an open key handle + * + * @param hkey + * open handle of key for which to get value length + * @param value_len, the location in which to store the value length. + * @return return_value + * See REFERENCE_1 and REFERENCE_2. + * ARM_CFSTORE_DRIVER::(*GetValueLen)() asynchronous completion command code + * (*ARM_CFSTORE_CALLBACK) function argument values on return: + * @param status + * ARM_DRIVER_OK => success, value_len contains length value. + * else failure. + * @param cmd_code == CFSTORE_OPCODE_GET_VALUE_LEN + * @param client context, registered ARM_CFSTORE_DRIVER::(*Initialize)() + * @param hkey supplied to the GetValueLen() call at hkey. + * + */ + int32_t (*GetValueLen)(ARM_CFSTORE_HANDLE hkey, ARM_CFSTORE_SIZE *value_len); + + + /** + * @brief Initialize the Configuration Store + * + * This function: + * - initialised the configuration store service + * - allows the client to subscribe to event notifications, in particular + * the asynchronous completion events for the driver dispatch interface + * (see ARM_CFSTORE_OPCODE). + * + * The configuration store should be initialised as follows: + * drv->initialise(client_callback, client_callback_context); + * drv->PowerControl(ARM_POWER_FULL) + * where: + * - client_callback is a client implemented callback function. + * - client_callback_context is a client registered context that + * will be supplied as an argument to the client_callback + * when called. + * - PowerControl indicates that any underlying system services + * that are in the low power state should be powered up. + * + * The configuration store should be de-initialised as follows: + * drv->PowerControl(ARM_POWER_OFF) + * drv->Deinitialise(); + * + * @param client_callback + * this is a client implemented callback function for + * receiving asynchronous event notifications (see + * ARM_CFSTORE_CALLBACK). NULL indicates client does + * not subscribed to event notifications. + * + * @param client_callback_context + * this is the client registered context that + * will be supplied as an argument to the client_callback + * when called. This argument can be NULL. + * + * @return return_value + * See REFERENCE_1 and REFERENCE_2 + * ARM_CFSTORE_DRIVER::(*Initialize)() asynchronous completion command code + * (*ARM_CFSTORE_CALLBACK) function argument values on return: + * @param status + * ARM_DRIVER_OK => success, configuration store initialised, + * else failure + * @param CFSTORE_OPCODE_INITIALIZE + * @param client context registered ARM_CFSTORE_DRIVER::(*Initialize)() + * @param hkey, unused. + * + */ + int32_t (*Initialize)(ARM_CFSTORE_CALLBACK callback, void* client_context); + + + /** + * @brief Function to set the target configuration store power state. + * + * @param state + * \ref ARM_POWER_STATE. The target power-state for the + * configuration store. The parameter state can have the + * following values: + * - ARM_POWER_FULL => set the mode to full power state + * - ARM_POWER_LOW => set the mode to low power state + * - ARM_POWER_OFF => set the mode to off power state + * + * @return return_value + * See REFERENCE_1 and REFERENCE_2. + * CFSTORE_OPCODE_POWER_CONTROL + * ARM_CFSTORE_DRIVER::(*PowerControl)() asynchronous completion command code + * (*ARM_CFSTORE_CALLBACK) function argument values on return: + * @param status + * ARM_DRIVER_OK => success, power control set, else failure. + * @param cmd_code == CFSTORE_OPCODE_POWER_CONTROL + * @param client context, registered ARM_CFSTORE_DRIVER::(*Initialize)() + * @param hkey, unused. + */ + int32_t (*PowerControl)(ARM_POWER_STATE state); + + + /** + * @brief read the value data associated with a specified key. + * + * @param hkey + * IN: the handle returned from a previous call to (*Open)() to + * get a handle to the key + * @param data + * IN: a pointer to a data buffer supplied by the caller for CFSTORE to + * fill with value data + * OUT: on ARM_DRIVER_OK the data is (or will be when asynchronously + * completed) available in the buffer. The data will be read from + * the current form the current location (see (*Rseek)(). + * @param len + * IN: the client specifies the length of the buffer available at data + * OUT: the CFSTORE specifies how many octets have been stored in the + * supplied buffer. Note fewer octets may be stored than the input len + * depending on the CFSTORE internal representation of the value. + * + * @return return_value + * See REFERENCE_1 and REFERENCE_2. + * return_value == ARM_DRIVER_OK (==0) => asynchronous + * transaction pending. + * return_value > 0 => synchronous completion of read with the + * number of bytes read == return_value + * return_value < 0, error condition. + * + * ARM_CFSTORE_DRIVER::(*Read)() asynchronous completion command code + * (*ARM_CFSTORE_CALLBACK) function argument values on return: + * @param status + * >= 0 => success with number of bytes read as indicated by + * the value of status + * < 0 => error, data in the data buffer is undefined, len is + * undefined. + * @param cmd_code == CFSTORE_OPCODE_READ + * @param client context, registered ARM_CFSTORE_DRIVER::(*Initialize)() + * @param hkey, unused. + */ + int32_t (*Read)(ARM_CFSTORE_HANDLE hkey, void* data, ARM_CFSTORE_SIZE* len); + + + /** + * @brief Open a key-value object for future operations. + * + * @param key_name + * IN: zero terminated string specifying the key name. + * @param flags + * can open a RW key in read only mode. + * @param hkey + * IN: pointer to client owned buffer of CFSTORE_HANDLE_BUFSIZE + * bytes + * OUT: on success, hkey is a valid handle to an open KV. + * + * @return return_value + * See REFERENCE_1 and REFERENCE_2. + * ARM_CFSTORE_DRIVER::(*Open)() asynchronous completion command code + * (*ARM_CFSTORE_CALLBACK) function argument values on return: + * @param status + * ARM_DRIVER_OK => success, KV opened, else failure. + * @param cmd_code == CFSTORE_OPCODE_OPEN + * @param client context, registered ARM_CFSTORE_DRIVER::(*Initialize)() + * @param hkey now contains returned handle to newly opened key. + */ + int32_t (*Open)(const char* key_name, ARM_CFSTORE_FMODE flags, ARM_CFSTORE_HANDLE hkey); + + + /** + * @brief move the position of the read pointer within a value + * + * @param hkey + * IN: the key referencing the value data for which the read + * location should be updated. Note that this function can + * only be called on pre-existing keys opened with read-only + * flag. Performing a seek operation on a writable key will fail. + * @param offset + * IN: the new offset position from the start of the value data + * + * @return return_value + * See REFERENCE_1 and REFERENCE_2. + * ARM_CFSTORE_DRIVER::(*Rseek)() asynchronous completion command code + * (*ARM_CFSTORE_CALLBACK) function argument values on return: + * @param status + * >=0 => success, read location set, else failure. + * upon success, the function returns the current read + * location measured from the beginning of the data value. + * <0 => error + * @param cmd_code == CFSTORE_OPCODE_RSEEK + * @param client context, registered ARM_CFSTORE_DRIVER::(*Initialize)() + * @param hkey, unused. + */ + int32_t (*Rseek)(ARM_CFSTORE_HANDLE hkey, ARM_CFSTORE_OFFSET offset); + + + /** + * @brief Function to de-initialise the Configuration Store + * + * @return return_value + * See REFERENCE_1 and REFERENCE_2 + * CFSTORE_OPCODE_UNINITIALIZE + * ARM_CFSTORE_DRIVER::(*Uninitialize)() asynchronous completion command code + * (*ARM_CFSTORE_CALLBACK) function argument values on return: + * @param status + * ARM_DRIVER_OK => success, read location set, else failure. + * @param cmd_code == CFSTORE_OPCODE_UNINITIALIZE + * @param client context, registered ARM_CFSTORE_DRIVER::(*Initialize)() + * @param hkey, unused. + */ + int32_t (*Uninitialize)(void); + + + /** + * @brief write the value data associated with a specified key + * + * @note Note that Write() only supports sequential-access. + * + * @param hkey + * IN: the key for which value data will be written + * @param data + * IN: a pointer to a data buffer supplied by the caller for CFSTORE to + * write as the binary blob value data. + * Async operation: the buffer must be available until after + * completion notification is received by the client. + * @param len + * IN: the client specifies the length of the data buffer available. + * len must not exceed the value_len field specified when the + * KV pair was created. + * OUT: the CFSTORE specifies how many octets have been stored. + * Note that fewer octets may be stored than the input len + * depending on the CFSTORE internal representation of the value. + * Async operation: the len storage location must be available + * until after completion notification is received by the client. + * + * @return return_value + * See REFERENCE_1 and REFERENCE_2. + * return_value == ARM_DRIVER_OK (==0) => asynchronous + * transaction pending. + * return_value > 0 => synchronous completion of write with the + * number of bytes written == return_value + * return_value < 0, error condition. + * + * ARM_CFSTORE_DRIVER::(*Write)() asynchronous completion + * (*ARM_CFSTORE_CALLBACK) function argument values on return: + * @param status + * >= 0 => success with the number bytes written equal to the value + * of the return status + * ARM_CFSTORE_CALLBACK status argument < 0 => error + * @param cmd_code == CFSTORE_OPCODE_WRITE + * @param client context, registered ARM_CFSTORE_DRIVER::(*Initialize)() + * @param hkey, unused. + */ + int32_t (*Write)(ARM_CFSTORE_HANDLE hkey, const char* data, ARM_CFSTORE_SIZE* len); + + +} const ARM_CFSTORE_DRIVER; + + + +extern ARM_CFSTORE_DRIVER cfstore_driver; + +#ifdef __cplusplus +} +#endif + +#endif /* __CONFIGURATION_STORE_H */ diff --git a/storage/cfstore/doc/design/configuration_store_getting_started.md b/storage/cfstore/doc/design/configuration_store_getting_started.md new file mode 100644 index 0000000000..3e561b264b --- /dev/null +++ b/storage/cfstore/doc/design/configuration_store_getting_started.md @@ -0,0 +1,219 @@ +# Configuration Store Getting Started +Author: Simon Hughes + +# Overview + +## Executive Summary + +This document describes how to get started with the configuration store module. + +## Terminology + +The terminology used throughout this document is defined on the [CFSTORE Terminology](https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_terminology.md) page. + + +# CFSTORE : Getting started + +This is the link to the configuration store github repo, with links to further documentation from the top level readme.md is [here.](https://github.com/ARMmbed/configuration-store/README.md) + +The documentation explains how the API works. There are also a number of test cases in the repo which can be used as examples of how to use the API. + +This is the project plan is [here.](https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_project.md) + +* Milestone 1 (SRAM version) has been released and tagged MBED_V_0_1_0. +* Milestone 2 (CFSTORE Flash Journal Integration) has been released and tagged MBED_V_0_2_0. + + +# CFSTORE : Building the Module + +First take a clone of the configuration store github repository from the tag of the last release. For example: + +``` + simhug01@E107851:/d/sdh_dev$ git clone git@github.com:ARMmbed/configuration-store.git +``` + +Then cd into the configuration-store sub-dir and get a list of the tags + +``` + simhug01@E107851:/d/sdh_dev$ cd configuration-store + simhug01@E107851:/d/sdh_dev/configuration-store $ git tag -l + MBED_V_0_1_0 + MBED_V_0_2_0 +``` + +The current release is MBED_V_0_2_0 (CFSTORE-flash journal integrated version). Checkout this version for evaluation: + +``` + simhug01@E107851:/d/sdh_dev/configuration-store $ git checkout tags/MBED_V_0_2_0 +``` + +configuration-store depends on modules published in the private yotta registry and therefore you need access to this registry. To login to the yotta private use the following command: + +``` + simhug01@E107851:/d/sdh_dev/configuration-store $ yotta login --registry https://yotta-private.herokuapp.com --apikey=873_b8isfdns3684_327t2evut3r +``` + + +Set the yotta target: + +``` + simhug01@E107851:/d/sdh_dev/configuration-store $ yt target frdm-k64f-gcc +``` + +Now install the yotta dependencies (which will require access to the yotta private registry to succeed): + +``` + simhug01@E107851:/d/sdh_dev/configuration-store $ yt install +``` + +You should see something like the following for the output to yotta list, which shows the CFSTORE module dependency graph: + +``` + simhug01@E107851:/d/datastore/public/jobs/yr2016/2247/sdh_dev_8/configuration-store$ yt list + configuration-store 0.2.0 + |_ mbed-drivers 1.5.0 + | |_ mbed-hal 1.3.0 yotta_modules\mbed-hal + | | \_ mbed-hal-freescale 1.1.0 yotta_modules\mbed-hal-freescale + | | \_ mbed-hal-ksdk-mcu 1.3.0 yotta_modules\mbed-hal-ksdk-mcu + | | |_ uvisor-lib 2.1.2 yotta_modules\uvisor-lib + | | \_ mbed-hal-k64f 1.3.0 yotta_modules\mbed-hal-k64f + | | \_ mbed-hal-frdm-k64f 1.1.1 yotta_modules\mbed-hal-frdm-k64f + | |_ cmsis-core 1.2.0 yotta_modules\cmsis-core + | | \_ cmsis-core-freescale 1.1.0 yotta_modules\cmsis-core-freescale + | | \_ cmsis-core-k64f 1.1.0 yotta_modules\cmsis-core-k64f + | |_ ualloc 1.3.0 yotta_modules\ualloc + | | \_ dlmalloc 1.1.0 yotta_modules\dlmalloc + | |_ minar 1.3.0 yotta_modules\minar + | | \_ minar-platform 1.1.0 yotta_modules\minar-platform + | | \_ minar-platform-mbed 1.3.0 yotta_modules\minar-platform-mbed + | |_ core-util 1.8.0 yotta_modules\core-util + | \_ compiler-polyfill 1.3.0 yotta_modules\compiler-polyfill + |_ flash-journal-strategy-sequential 0.3.0 + | \_ flash-journal 0.3.0 yotta_modules\flash-journal + | \_ flash-abstraction 0.3.0 yotta_modules\flash-abstraction + | \_ mtd-k64f 0.3.0 yotta_modules\mtd-k64f + |_ unity 2.2.0 (test dependency) + | \_ utest 1.12.2 yotta_modules\utest + \_ greentea-client 0.1.8 (test dependency) + +``` + +Now build configuration-store: + +``` + simhug01@E107851:/d/sdh_dev/configuration-store $ yt build +``` + +The above command builds CFSTORE with support for asynchronous mode flash-journal. If you would like synchronous mode support then use +the following build command: + +``` + simhug01@E107851:/d/sdh_dev/configuration-store $ yotta --config='{"config":{"hardware":{"mtd":{"async":{"ops":0}}}}}' build +``` + +Having built successfully, you can now test with the following mbed-greentea command: + +``` + simhug01@E107851:/d/sdh_dev/configuration-store $ mbedgt -V +``` + +The test results should be similar to the test results form the [Jenkins CFSTORE Build and Test Job](http://e108747.cambridge.arm.com:8080/job/configuration-store-nightly-build-and-test/) the summary of which is provided below for reference: + +``` +mbedgt: test suite report: ++---------------+---------------+----------------------------------+--------+--------------------+-------------+ +| target | platform_name | test suite | result | elapsed_time (sec) | copy_method | ++---------------+---------------+----------------------------------+--------+--------------------+-------------+ +| frdm-k64f-gcc | K64F | configuration-store-test-acl | OK | 10.26 | shell | +| frdm-k64f-gcc | K64F | configuration-store-test-add_del | OK | 20.78 | shell | +| frdm-k64f-gcc | K64F | configuration-store-test-close | OK | 13.02 | shell | +| frdm-k64f-gcc | K64F | configuration-store-test-create | OK | 12.8 | shell | +| frdm-k64f-gcc | K64F | configuration-store-test-find | OK | 24.31 | shell | +| frdm-k64f-gcc | K64F | configuration-store-test-flash | OK | 11.27 | shell | +| frdm-k64f-gcc | K64F | configuration-store-test-flush | OK | 12.51 | shell | +| frdm-k64f-gcc | K64F | configuration-store-test-flush2 | OK | 12.53 | shell | +| frdm-k64f-gcc | K64F | configuration-store-test-misc | OK | 14.64 | shell | +| frdm-k64f-gcc | K64F | configuration-store-test-open | OK | 14.5 | shell | +| frdm-k64f-gcc | K64F | configuration-store-test-read | OK | 12.78 | shell | +| frdm-k64f-gcc | K64F | configuration-store-test-write | OK | 12.88 | shell | ++---------------+---------------+----------------------------------+--------+--------------------+-------------+ +mbedgt: test suite results: 12 OK +mbedgt: test case report: ++---------------+---------------+----------------------------------+------------------------------+--------+--------+--------+--------------------+ +| target | platform_name | test suite | test case | passed | failed | result | elapsed_time (sec) | ++---------------+---------------+----------------------------------+------------------------------+--------+--------+--------+--------------------+ +| frdm-k64f-gcc | K64F | configuration-store-test-acl | CFSTORE_ACL_test_01 | 1 | 0 | OK | 0.1 | +| frdm-k64f-gcc | K64F | configuration-store-test-add_del | ADD_DEL_test_01 | 1 | 0 | OK | 0.1 | +| frdm-k64f-gcc | K64F | configuration-store-test-add_del | ADD_DEL_test_02 | 1 | 0 | OK | 0.1 | +| frdm-k64f-gcc | K64F | configuration-store-test-add_del | ADD_DEL_test_03 | 1 | 0 | OK | 0.1 | +| frdm-k64f-gcc | K64F | configuration-store-test-add_del | ADD_DEL_test_04 | 1 | 0 | OK | 0.1 | +| frdm-k64f-gcc | K64F | configuration-store-test-add_del | ADD_DEL_test_05 | 1 | 0 | OK | 0.11 | +| frdm-k64f-gcc | K64F | configuration-store-test-add_del | ADD_DEL_test_06 | 1 | 0 | OK | 0.1 | +| frdm-k64f-gcc | K64F | configuration-store-test-add_del | ADD_DEL_test_07_end | 1 | 0 | OK | 0.25 | +| frdm-k64f-gcc | K64F | configuration-store-test-add_del | ADD_DEL_test_07_start | 1 | 0 | OK | 0.05 | +| frdm-k64f-gcc | K64F | configuration-store-test-add_del | ADD_DEL_test_08_end | 1 | 0 | OK | 0.54 | +| frdm-k64f-gcc | K64F | configuration-store-test-add_del | ADD_DEL_test_08_start | 1 | 0 | OK | 0.05 | +| frdm-k64f-gcc | K64F | configuration-store-test-add_del | ADD_DEL_test_09_end | 1 | 0 | OK | 4.94 | +| frdm-k64f-gcc | K64F | configuration-store-test-add_del | ADD_DEL_test_09_start | 1 | 0 | OK | 0.05 | +| frdm-k64f-gcc | K64F | configuration-store-test-add_del | ADD_DEL_test_10 | 1 | 0 | OK | 0.11 | +| frdm-k64f-gcc | K64F | configuration-store-test-close | CLOSE_test_01_end | 1 | 0 | OK | 0.51 | +| frdm-k64f-gcc | K64F | configuration-store-test-close | CLOSE_test_01_start | 1 | 0 | OK | 0.04 | +| frdm-k64f-gcc | K64F | configuration-store-test-create | CREATE_test_01_end | 1 | 0 | OK | 0.19 | +| frdm-k64f-gcc | K64F | configuration-store-test-create | CREATE_test_01_start | 1 | 0 | OK | 0.05 | +| frdm-k64f-gcc | K64F | configuration-store-test-find | FIND_test_01 | 1 | 0 | OK | 0.1 | +| frdm-k64f-gcc | K64F | configuration-store-test-find | FIND_test_02 | 1 | 0 | OK | 0.1 | +| frdm-k64f-gcc | K64F | configuration-store-test-find | FIND_test_03_end | 1 | 0 | OK | 10.53 | +| frdm-k64f-gcc | K64F | configuration-store-test-find | FIND_test_03_start | 1 | 0 | OK | 0.05 | +| frdm-k64f-gcc | K64F | configuration-store-test-find | FIND_test_04 | 1 | 0 | OK | 0.09 | +| frdm-k64f-gcc | K64F | configuration-store-test-find | FIND_test_05_end | 1 | 0 | OK | 0.05 | +| frdm-k64f-gcc | K64F | configuration-store-test-find | FIND_test_05_start | 1 | 0 | OK | 0.05 | +| frdm-k64f-gcc | K64F | configuration-store-test-flash | flash_journal_async_test_01 | 1 | 0 | OK | 0.22 | +| frdm-k64f-gcc | K64F | configuration-store-test-flush | initialize | 1 | 0 | OK | 0.03 | +| frdm-k64f-gcc | K64F | configuration-store-test-flush2 | CFSTORE_FLUSH2_flush | 1 | 0 | OK | 0.17 | +| frdm-k64f-gcc | K64F | configuration-store-test-flush2 | CFSTORE_FLUSH2_init_on_entry | 1 | 0 | OK | 0.06 | +| frdm-k64f-gcc | K64F | configuration-store-test-misc | MISC_test_00_end | 1 | 0 | OK | 0.04 | +| frdm-k64f-gcc | K64F | configuration-store-test-misc | MISC_test_00_start | 1 | 0 | OK | 0.05 | +| frdm-k64f-gcc | K64F | configuration-store-test-misc | MISC_test_01 | 1 | 0 | OK | 0.04 | +| frdm-k64f-gcc | K64F | configuration-store-test-misc | MISC_test_02 | 1 | 0 | OK | 0.04 | +| frdm-k64f-gcc | K64F | configuration-store-test-misc | MISC_test_03_end | 1 | 0 | OK | 0.4 | +| frdm-k64f-gcc | K64F | configuration-store-test-misc | MISC_test_03_start | 1 | 0 | OK | 0.05 | +| frdm-k64f-gcc | K64F | configuration-store-test-misc | MISC_test_04_end | 1 | 0 | OK | 0.24 | +| frdm-k64f-gcc | K64F | configuration-store-test-misc | MISC_test_04_start | 1 | 0 | OK | 0.05 | +| frdm-k64f-gcc | K64F | configuration-store-test-misc | MISC_test_05_end | 1 | 0 | OK | 0.05 | +| frdm-k64f-gcc | K64F | configuration-store-test-misc | MISC_test_05_start | 1 | 0 | OK | 0.05 | +| frdm-k64f-gcc | K64F | configuration-store-test-open | OPEN_test_01_end | 1 | 0 | OK | 0.26 | +| frdm-k64f-gcc | K64F | configuration-store-test-open | OPEN_test_01_start | 1 | 0 | OK | 0.04 | +| frdm-k64f-gcc | K64F | configuration-store-test-open | OPEN_test_02_end | 1 | 0 | OK | 0.05 | +| frdm-k64f-gcc | K64F | configuration-store-test-open | OPEN_test_02_start | 1 | 0 | OK | 0.05 | +| frdm-k64f-gcc | K64F | configuration-store-test-open | OPEN_test_100 | 1 | 0 | OK | 0.1 | +| frdm-k64f-gcc | K64F | configuration-store-test-open | OPEN_test_101 | 1 | 0 | OK | 0.1 | +| frdm-k64f-gcc | K64F | configuration-store-test-open | OPEN_test_102 | 1 | 0 | OK | 0.09 | +| frdm-k64f-gcc | K64F | configuration-store-test-open | OPEN_test_103 | 1 | 0 | OK | 0.1 | +| frdm-k64f-gcc | K64F | configuration-store-test-open | OPEN_test_104 | 1 | 0 | OK | 0.1 | +| frdm-k64f-gcc | K64F | configuration-store-test-open | OPEN_test_105 | 1 | 0 | OK | 0.1 | +| frdm-k64f-gcc | K64F | configuration-store-test-read | READ_test_01_end | 1 | 0 | OK | 0.05 | +| frdm-k64f-gcc | K64F | configuration-store-test-read | READ_test_01_start | 1 | 0 | OK | 0.04 | +| frdm-k64f-gcc | K64F | configuration-store-test-read | READ_test_02 | 1 | 0 | OK | 0.1 | +| frdm-k64f-gcc | K64F | configuration-store-test-write | WRITE_test_01_end | 1 | 0 | OK | 0.04 | +| frdm-k64f-gcc | K64F | configuration-store-test-write | WRITE_test_01_start | 1 | 0 | OK | 0.04 | +| frdm-k64f-gcc | K64F | configuration-store-test-write | WRITE_test_02_end | 1 | 0 | OK | 0.04 | +| frdm-k64f-gcc | K64F | configuration-store-test-write | WRITE_test_02_start | 1 | 0 | OK | 0.05 | ++---------------+---------------+----------------------------------+------------------------------+--------+--------+--------+--------------------+ +mbedgt: test case results: 56 OK +mbedgt: completed in 177.39 sec +simhug01@E107851:/d/datastore/public/jobs/yr2016/2247/sdh_dev_8/configuration-store$``` + + +# FAQ + +## Q: What version has been published in the yotta registry? + +A: Version 0.1.0 is the latest version published in the yotta registry. + +## Q: Does Configuration-Store work with mbed OS 16.3? +A: Yes + +## Q: Does Configuration-Store work with mbed OS 15.11? +A: Yes + + diff --git a/storage/cfstore/doc/design/configuration_store_hld.md b/storage/cfstore/doc/design/configuration_store_hld.md new file mode 100644 index 0000000000..58625b6ba7 --- /dev/null +++ b/storage/cfstore/doc/design/configuration_store_hld.md @@ -0,0 +1,1096 @@ +# Configuration Store High Level Design +Author: Simon Hughes + +# Overview + +## Executive Summary + +This document is the High Level Design for the first version of the +Configuration Store (CFSTORE). The configuration store is a secure, +associative key-value (KV) store C-Language Hardware Abstraction Layer. +CFSTORE's main function is storing and managing (key, value) pairs in +persistent storage media. It implements a subset of the requirements as listed +in the supported requirements section. + +CFSTORE provides the secure and persistent storage for: + +- Storing encryption keys data. +- Storing configuration data. +- Storing firmware, firmware updates and incremental firmware blocks for assembling into a firmware update. + +i.e. CFSTORE is a general purpose registry for storing code and data objects. + +These services are presented to clients with: + +- A conceptually simple, file-like interface for storing and managing data using (key, value) pairs in + persistent storage media. +- A simple, hardware-independent API to promote portability across multiple platforms and a low attack surface. +- A very small code/memory footprint so CFSTORE is capable of running on highly-constrained memory systems (~10kB free memory) + where typically available SRAM << NV storage. +- A simple (low complexity) storage capability such CFSTORE only supports the storage of binary blobs. + + +## Terminology + +The terminology used throughout this document is defined [here] [CFSTORE_TERM]. + + +## Document Scope + +The scope of this document is the High Level Design (HLD) of the +Configuration Store (CFSTORE) component. + +The scope of this document is describe the CFSTORE high level design for the +following features: + +- The creation of new key-value (KV) pairs in the store for writing. + The values are binary blobs with no type structure. +- The opening of pre-existing KV pairs for reading/writing/deletion. +- The searching of KVs with keys matching a search string with wildcards. +- The synchronous and asynchronous completion of the above functions. +- The committing of KV pair changes to non-volatile backing stores. +- The secure storage and management of KV pairs according to access permissions and uvisor supported security features. +- CFSTORE alignment with the CMSIS-Driver Model. + + +The [overview](#Overview) section provides an introduction to this document including a CFSTORE executive summary. + +The [use cases](#Use-Cases) section describes the important CFSTORE client use cases, in particular the +FOTA use case. + +The [software architecture](#Configuration-Store-Software-Architecture) section describes the +entities in the CFSTORE software stack including the CFSTORE clients (e.g. FOTA), CFSTORE, the +[flash-abstraction layer][FAL], the CMSIS-Driver Flash driver layer, uvisor and the hardware/software +interface. + +The [CFSTORE API](#Configuration-Store-Application-Programming-Interface-(CFSTORE-API)) section describes the +application programming interface to the CFSTORE component. This includes sequence diagrams +describing the client-CFSTORE API call sequences to use the interface to accomplish common +operations e.g. CFSTORE initialisation/de-initialisation, creating a key and finding KVs +that match a given search specification. + + +## Outstanding Issues With This Document + +The following is a list of outstanding issues with this document: + +- The list of supported requirements needs updating. +- Security considerations need defining. +- The usage of keys needs defining. + + +# CFSTORE Motivation, Design Considerations and Key Concepts + +## Rationale/Motivation + +CFSTORE provides the secure and persistent storage for: + +- Storing encryption keys data. +- Storing configuration data. +- Storing firmware, firmware updates and incremental firmware blocks for assembling into a firmware update. + +i.e. CFSTORE is a general purpose registry for storing code and data objects. + +These services are presented to clients in the following way: + +- A simple, hardware-independent API to promote portability across multiple platforms and a low attack surface. +- A very small code/memory footprint so CFSTORE is capable of scaling from highly-constrained memory systems (~10kB free memory) + where typically available SRAM << NV storage, to less constrained systems with more memory. +- A simple (low complexity) storage capability such CFSTORE only supports the storage of binary blobs. + + +## CFSTORE Security Considerations + +For security reasons the uVisor security model allows applications (general purpose code) +only restricted access to NV (flash) storage. This ensures modifications to OS or application +code are tightly controlled so ensure: + +- Malware cannot become resident in the system. +- Security measures like Access Control Lists (ACLs) cannot be modified or circumvented by malicious code. + + +## Low Complexity of CFSTORE and the Implied Security Benefits + +The design concept behind the secure key-value storage one of simplicity to ensure a +low attack surface. The design reflects the smallest possible common denominator +for storing data and code blocks in a mutually-distrustful operating system. + +Complex access restrictions are implemented in additional uvisor boxes as +CFSTORE clients i.e. "on top of" the secure CFSTORE key-value storage. An example of a complex +access restriction is given as follows: + +> The following key can be only updated from Monday to Thursday by a remote +> server matching the following valid public key + +The additional secure uvisor box is used to wrap values with box-specific +ACLs and marks these keys as accessible only to the owner box. +Restricting access in this way guarantees domain-specific +key-access restrictions can be reliably enforced by each security context. + + +## CFSTORE Implied Shifting of Complexity to Other (Higher Layer) Components + +The same is true for supporting custom or complex data types beyond the simple octet blob supported by CFSTORE. +For sophisticated configuration storage +systems arbitrary types can be supported by wrapping values with a type identifier. +This ensures that every programming language can be adequately and safely supported by +the key-value storage. + + +## Key Value Storage + +The CFSTORE KV storage has the following characteristics: + +- The only supported key-value payload type is a binary blob. +- All values are referenced by a globally name-spaced key string. +- The key_name is required to be unique on a device. +- Although by design the key_name format does not provide the notion of hierarchic key-trees, + key-hierarchies can be reflected in key path strings. +- Allowable characters in a key path directory entry are only ASCII letters, digits, and the '-' character. +- Path directory entries are separated by the path delimiter ('.'). +- Path directory entries are indicated to be part 'lists' by adding one or more list indexes enclosed by square brackets ('\[ ]'). + Index names are composed with allowable characters (see previous point). +- Key name sizes are limited to 220 bytes (excluding zero termination). + +The following illustrates valid name examples: + +``` + + 'com.arm.mbed.wifi.accesspoint[5].essid' = 'AccessNG' + + 'com.arm.mbed.wifi.accesspoint[home].essid' = 'HomeSweetHome' + + 'yotta.your-yotta-registry-module-name.your-value' = 'XYZ' + + 'yotta.hello-world.animal[dog][foot][3]' = 'dirty' +``` + +Note that the key-path-prefix 'yotta' is reserved for modules in the [yotta registry](http://yotta.mbed.com/). + + +## Key Ownership + +Key ownership is tied to the name of the key. + +- If a client security context (uvisor box) needs to create CFSTORE KV entries, it must + have a (mandatory) key name prefix. This is called the security_prefix_name. +- When a client security context create a KV pair, the security_prefix_name is "enforced" + for created values i.e. the security_prefix_name must be the prefix of the key_name. +- Therefore, the CFSTORE key_name namespace owned by a client security context is defined by the + clients security_prefix_name. + +Consider the following example: + +- The client security context (uvisor box) has the security_prefix_name "com.arm.mbed.tls". + This security_prefix_name is registered with uvisor as part of box creation. +- The client security context creates a CFSTORE KV with client_key_name='com.arm.mbed.tls.cert[5].key'. +- CFSTORE queries uvisor for the client security_prefix_name and computes the client_key_name_prefix by + post-pending '.' to give "com.arm.mbed.tls.". +- CFSTORE only creates the KV if the client security_prefix_name (i.e. "com.arm.mbed.tls.") + matches the leading characters of the client_key_name (i.e. "com.arm.mbed.tls.cert[5].key"). + In this case there is a match so CFSTORE creates the KV. +- The client "com.arm.mbed.tls" uvisor box is regarded as the owner of the newly created key. + +Uvisor box security_prefix_name's are not allowed to overlap. + + +## Access Control Security ### + +Access control lists (ACL) enforce access for the following security groups + +- Owner. +- Other. + +The permissions for these two groups are: + +- Reading. +- Writing. +- Execution. + +The resulting matrix can be represented by a 6-bit binary field: + +- Owner (i.e. the caller during key creation: + - Read permission bit. + - Write permission bit. + - Execute permission bit. +- Other (i.e. everybody else): + - Read permission bit. + - Write permission bit. + - Execute permission bit. + +Note the following: + +- A writable field is not allowed to be executable. +- The executable bit is not supported till further notice and is reserved for performing modular firmware updates at a later point. +- The high level API provides a function for listing accessible values, but ensures that only values with + read or write access will be listed to the caller. +- The caller is able to choose between listing just the values he owns, or values of others he has read or write access to. + + +## Finding Keys + +Whenever a key is read, the CFSTORE is scanned for active keys with suitable access permissions key-by-key. +Wild card searches are explicitly supported. The reserved character asterisk ('*') is used to indicate a wild +card search to the API. Wild card operations are only supported for finding keys, not for accessing keys. +The wild card operator can occur once at any point of the search string: + +The following shows examples of valid key_name query strings to the Find() method: + +``` + + 'com.arm.mbed.wifi.accesspoint*.essid' + + 'yotta.your-yotta-registry-module-name.*' + + 'yotta.hello-world.animal[dog][foot][*]' + + 'yotta.hello-world.animal[dog][foot]*' + + 'yotta.hello-world.animal[dog*3]' + +``` + +Note that whenever a search returns a key candidate, the search can be resumed to return further matches. + + +## Creating & Opening Keys for Writing + +Keys must be explicitly created using Create() with the following parameters: + +- Security ACLs (owner & others). +- The intended retention levels. +- The expected Device Data Security Protection Features +- The value size +- Mode flags + +Note the following: + +- Pre-existing keys are opened using the Open(). +- The returned handle allows write access to the value by default. +- Wild cards are not allowed for creating or opening keys. +- Failing to meet expected retention levels or security levels result in the API call failing. +- An executable key is always treated as 'continuous' by the API even if the 'continuous' flag is not set by the caller. +- Non-executable values can be optionally 'continuous'. + + +## Error Handling + +### Overview of Error Handling + +The following provides general notes on the handling of errors: + +- CFSTORE is intended to "Fail-Safe" i.e. if errors occur then the system should be recoverable from such errors. +- "Fail-Safe" requires CFSTORE storage backends must support the flushing of data in an atomic operation. This is so + that the loss of power during a flush operation to the CFSTORE backend does not result in the stored data being in + an indeterminate state. +- Supported backends may include non-volatile storage (flash) and SRAM for example. +- For exmaple. tf a non-volatile storage (flash) backend is supported, then the flushing of data to the NV store must be atomic. + This may require that flushing of data to falsh is commited using an atomic write operation e.g. of a CRC32 value for the + commit data. If power loss occured during the atomic write then the CRC32 would be invalid, the previously written data would + not be value, and the system would have to revert to a previous version of the data that has successfully commit the correct CRC32 + value. + + +### Synchronous/Asynchronous API Calls and Error Handling + +The CFSTORE has 2 modes of operations: + +- Synchronous (SYNC) mode. +- Asynchronous (ASYNC) mode. + +The mode is determined by inspecting the results of the GetCapabilites() API call. + +All CFSTORE API calls (apart of specific exclusions listed below) return an int32_t return code designated ```RETURN_CODE```. + +- A ```RETURN_CODE``` < 0 always indicates an error. +- A ```RETURN_CODE``` >= 0 always indicates success. + + - In SYNC mode the operation has completed successfully. + - In ASYNC mode the transaction has been queued successfully, pending completion sometime in the future. + The transaction status of the completed tranaction ```RETURN_CODE_ASYNC``` is supplied to the client + registered callback handler (if such a callback handler has been registered). +- Specific API calls may assign meaning to ```RETURN_CODE``` or ```RETURN_CODE_ASYNC``` when >=0. For example ```RETURN_CODE``` or ```RETURN_CODE_ASYNC``` + for a successful Read() call may be interpretted as the number of octets read. Consult the documentation for specific API calls + for further details. + +In ASYNC mode: + +- The client may register a callback handler for asynchronous completion notifications i.e. to receive the final + return status code ```RETURN_CODE_ASYNC```. +- API calls may return synchronously. A client may be able to determine whether an operation has completed + synchronously through knowledge of the assigned meaning of the ```RETURN_CODE```. For example, if ```RETURN_CODE```=100 + for a successful Read() call with a supplied buffer length of 100 bytes, then the client infers the call + completed synchronously. +- If a callback handler is registered then it will receive asynchronous notififications for all API calls irrespective + of whether they completed synchronously or asynchronously. + +CFSTORE API calls that do not return int32_t return values (i.e. exclusions to the foregoing) are as follows: + +- GetCapabilities(). +- GetStatus(). +- GetVersion(). + + +### Recovering for Errors + +CFSTORE clients must check all ```RETURN_CODE``` values for errors and act accordingly if an error is detected. + +Some API calls may return values < 0 as a part of the their normal operations. For example, when iterating over +a list of Find() results matching a wildcard, Find() may return ```RETURN_CODE``` < 0 to indicate no more matches are found + +If an ```RETURN_CODE``` error indicates a system failure the the CFSTORE client should implement the following +recovery procedure: + +- Call Uninitialise() which returns ```RETURN_CODE_UNINIT```. If ```RETURN_CODE_UNINIT``` < 0, abort any further action. + All client maintained state variables (e.g. hkeys) are the invalid. +- Call Initialise() which returns ```RETURN_CODE_REINIT```. If ```RETURN_CODE_REINIT``` < 0, abort any further action. +- Proceed to use CFSTORE. + + +## Known Limitations + +### CFSTORE Is Not An OS System Component. + +Note the following: + +- The notion of CFSTORE being and OS System component supporting +- CFSTORE is not re-entrant. +- CFSTORE does not support concurrent access from multiple clients. +- If the above are required then a abstraction layer above + CFSTORE_OS providing sequential access to the CFSTORE_API (e.g. implementing a queue with a mutex) should be implemented. + This is currently not supported. +- The current implmentation envisages only 1 instance of the CFSTORE per System. + +### Max Storage Data Size Limited by Available SRAM + +Note the following: + +- CFSTORE currently loads all KVs stored in a backend into SRAM. Hence, the backend storage size is limited to the maximum available SRAM for storing KVs. + +# Use Cases + +The design addresses the requirements of the following use cases: + +- CFSTORE Initialisation and Factory Initialisation +- FOTA + +## FOTA Use Case + +- FOTA received new firmware image incrementally in data blocks. +- Firmware image sizes are in the range 32-512kB. +- FOTA may choose to manage an image in blocks e.g. size 16kB chunks + so a 512kB image would be made up of 32 data blocks i.e. 32 x 16kB=512kB +- FOTA is responsible for receiving the 16kB blocks. +- FOTA may have 32 keys in the registry each storing 16kBs. +- A number of CFSTORE keys may be open simultaneously. +- FOTA may be writing incrementally to the key values, as data is received. +- FOTA block data may be stored in memory initially + + +# Configuration-Store Software Architecture + +```C + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Configuration Store Client | + | e.g. FOTA | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Configuration Store | | uvisor | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + | Flash Abstraction Layer | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + | Flash Driver Layer | | | + | e.g. CMSIS-Driver | | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + SW + ----------------------------------------------------------------------- + HW + + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NV Storage Media e.g. Flash | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Configuration Store Software Architecture + +``` + +The above figure shows the following entities: + +- NV Storage Media. These are the physical storage media. +- Flash Driver layer e.g. CMSIS-Driver. +- Flash Abstraction Layer, portable across the driver layer. +- Configuration Store, the associative KV pair store. +- A Configuration Store client e.g. FOTA. + + +# Configuration-Store Application Programming Interface (CFSTORE-API) + +The main API methods for creating, reading and writing CFSTORE KV are as follows: + +- (*Create)() creates a new KV in the store. +- (*Open)() opens a pre-existing KV in the store. KVs can be opened read-only or read-write. +- (*Read)() permits the random-access reading of the value data from the 'file-like' read location. +- (*Rseek)() permits the setting of the 'file' read location, the offset from the start of the value data where the next Read() will read from. +- (*Write)() permits the sequential-access writing of value data with the start of the write always starting from the beginning of the data value storage area. +- (*Flush)() permits the writing of CFSTORE changes to the backing store. + +Note that the above methods show similarities with a file system interface, but CFSTORE is not intended to be a +file system e.g. CFSTORE does not implement volume management or directory structures required for a file system. + +Additionally, the API supports also includes the following support methods: + +- (*GetCapabilities)() to get the capabilities of the CFSTORE implementation (e.g. whether CFSTORE is synchronous or asynchronous). +- (*GetKeyName)() to get the name of a key given an opaque handle. +- (*GetStatus)() to get the status of an in-progress asynchronous transaction. +- (*GetValueLen)() to get the length of the value data area of a KV pair. +- (*GetVersion)() to get the version of the CFSTORE API. +- (*Find)() queries the CFSTORE for keys matching a search string. The function returns an opaque handle to the first matching search + result. The function can be used to iterate over the entries, supplying a previously returned key handle to retrieve the next, until + the null handle is returned, indicating there are no more matches. +- Initialise() permitting the client to initialise CFSTORE for use and to subscribe for asynchronous event notifications. +- PowerControl() permitting the client to set the power control level.. +- Uninitialise() permitting the client to de-initialise CFSTORE. + +The API is aligned with the CMSIS-Driver Model in which the CFSTORE API functions are presented as `ARM_CFSTORE_DRIVER` dispatch methods. + +## CMSIS-Driver Model + +The CFSTORE is aligned with the CMSIS-Driver Model pattern as follows: + +- The interface is implemented using an `ARM_CFSTORE_DRIVER` structure with dispatch functions for the API interface methods. +- CMSIS-Driver common methods and CamelCase API naming conventions are adopted as follows: + - (*GetCapabilities)(). + - (*GetStatus)(). + - (*GetVersion)(). + - Initialise(). + - PowerControl(). + - Uninitialise(). + +This document refers to invocation of the `ARM_CFSTORE_DRIVER` dispatch methods using a notional pointer 'drv' to the `ARM_CFSTORE_DRIVER` object instance. +Thus drv->Initialise() refer to the invocation of the CFSTORE API Initialise() method. + +See the [CFSTORE low level Design][CFSTORE_LLD] for the detailed specification for function prototypes. +See the [CMSIS-Driver Documentation][KEIL_CMSIS_DRIVER] for more information. + + +## CFSTORE API Function Use Opaque Handles (`Cfstore_Handle_t` hkey) + +In common with a file interface, CFSTORE API functions return an opaque file handle for accessing a particular KV. In general terms: + +- Create() causes CFSTORE to instantiate in-memory data structures (context) for accessing a KV. On success, CFSTORE returns an opqaue handle + to these data structres by setting the `Cfstore_Handle_t*` hkey argument to a valid handle. The client then "owns" the CFSTORE context, which + CFSTORE updates in response to other API calls using the handle. The client returns ownership of the handle to CFSTORE by calling Close(hkey). + This causes CFSTORE to free in-memory data structures associated with accessing the KV. +- Find() causes CFSTORE to instantiate in-memory data structures (context) for a KV matching the key name query string. On success, + CFSTORE returns an opqaue handle to these data structres by setting the `Cfstore_Handle_t*` next argument to a valid handle. + The client then "owns" the CFSTORE context, which CFSTORE updates in response to other API calls using the handle. + The client returns ownership of the handle to CFSTORE in the following ways: + - By calling Find() again but this time supplying the previously returned 'next' handle as the 'previous' argument. + - By calling Close(next). + + +## API Call Sequence Diagram for GetVersion(), GetCapabilities() + +

+ +

+ +The above diagram shows the client-CFSTORE call sequence demonstrating how the client discovers API and CMSIS-Driver versions supported by the API. + +1. The client calls drv->GetVersion() which returns an `ARM_DRIVER_VERSION` structure. GetVersion() is a synchronous function. +2. The client calls drv->GetCapabilities() which returns an `ARM_Cfstore_Capabilities_t` structure + which reports whether the CFSTORE implementation is either: + - Synchronous or, + - Asynchronous. + +### Synchrononous Mode + +In synchronous mode `ARM_CFSTORE_DRIVER::Dispatch_Method_Xxx()` will return: + +- `ARM_DRIVER_OK` => CFSTORE Dispatch_Method_Xxx() completed successfully +- otherwise CFSTORE Dispatch_Method_Xxx() did not complete successfully + (return code supplies further details). + + +### Asynchronous Mode + +In asynchronous mode `ARM_CFSTORE_DRIVER::Dispatch_Method_Xxx()` will return: + +- `return_value` = `ARM_DRIVER_OK` (==0) => CFSTORE Dispatch_Method_Xxx() + pending. Dispatch_Method_Xxx completion status will be indicated via + an asynchronous call to ARM_Cfstore_Callback_t registered with the + `ARM_CFSTORE_DRIVER::(*Initialise)()`. +- `return_value` > 0 => CFSTORE Dispatch_Method_Xxx() completely + synchronously and successfully. The `return_value` has specific + meaning for the Dispatch_Method_Xxx() e.g. for the Read() method + the `return_value` is the number of bytes read. +- otherwise `return_value` < 0 => CFSTORE Dispatch_Method_Xxx() + completed unsuccessfully (return code supplies further details). + +The client registered asynchronous callback method ARM_Cfstore_Callback_t is +registered by the client using: + +```C +ARM_CFSTORE_DRIVER::(*Initialise)(ARM_Cfstore_Callback_t callback, void* client_context) +``` + +The registered callback has the following prototype: + + +```C +typedef void (*ARM_Cfstore_Callback_t)(int32_t status, ARM_Cfstore_OpCode_e cmd_code, void *client_context,ARM_Cfstore_Handle_t handle); +``` + +Before an asynchronous notification is received, a client can check on the +status of the call by calling `ARM_CFSTORE_DRIVER::(*GetStatus)()`. + + +See the [CFSTORE low level Design][CFSTORE_LLD] for the detailed specification for function prototypes. + + +## API Call Sequence Diagram for Initialise()/Uninitialise() (Sync, Success) + +

+ +

+ +The above diagram shows the client-CFSTORE call sequence for Initialising/Uninitialising the CFSTORE for a synchronous CFSTORE implementation +(drv->GetCapabilites() has returned an `ARM_Cfstore_Capabilities_t` structure with the synchronous flag set). + +1. The client calls drv->Initialise() without supplying an asynchronous callback method or client context. +2. CFSTORE returns OK, which in this case means success. +3. Once initialised, the client can call any other CFSTORE methods, as required. +4. After all client operations have been performed, the client calls drv->Uninitialise() to terminate use of CFSTORE. +5. CFSTORE returns OK, which in this case means success. + +See the [CFSTORE low level Design][CFSTORE_LLD] for the detailed specification for function prototypes. + + +## API Call Sequence Diagram for Initialise()/Uninitialise() (Async, Success) + +

+ +

+ +The above diagram shows the client-CFSTORE call sequence for Initialising/Uninitialising the CFSTORE for an asynchronous CFSTORE implementation +(drv->GetCapabilites() has returned an `ARM_Cfstore_Capabilities_t` structure with the asynchronous flag set). + +1. The client calls drv->Initialise() to subscribe to command completion events by supplying the following arguments: + - a callback method `Cfstore_Client_callback()` which will be invoked by CFSTORE for asynchronous notification of command completion events. + - a client context which will be supplied as an argument to the `Cfstore_Client_callback()` call. +2. CFSTORE returns OK, which in this case means the CFSTORE operation is pending asynchronous completion. +3. CFSTORE completes internal initialisation operations necessary to initialise. +4. Once internal initialisation has been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=INITIALISE, status, client_context)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. +5. Once initialised, the client can call any of the other CFSTORE methods. +6. After all client operations have been performed, the client calls drv->Uninitialise() to terminate use of CFSTORE. +7. CFSTORE returns OK, which in this case means the operation is pending asynchronous completion. +8. CFSTORE completes internal operations necessary to de-initialise. +9. Once internal de-initialisation has been completed, CFSTORE invokes + `Cfstore_Client_callback(OPCODE=UNINITIALISE, status, client_context)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. + CFSTORE will not invoke the callback method again. + +Note this example is the pathological case where all CFSTORE methods return OK i.e. the transactions are pending. +In reality, some calls will be completed synchronous and successfully, indicated by returning a value > 0. + +See the [CFSTORE low level Design][CFSTORE_LLD] for the detailed specification for function prototypes. + + + +## API Call Sequence Diagram for Create() Key (Sync, Success) + +

+ +

+ +The above diagram shows the client-CFSTORE call sequence for creating a KV for a synchronous CFSTORE implementation +(drv->GetCapabilites() has returned an `ARM_Cfstore_Capabilities_t` structure with the synchronous flag set). + +1. The client calls drv->Create(key_name, value_len, key_descriptor, &key_handle) to request CFSTORE to create the KV pair. +2. CFSTORE returns OK, which in this case means the Create() has been completed successfully. +3. The client calls drv->Write(key_handle, data, len) to set the data value in the KV. +4. CFSTORE returns OK, which in this case means the Write() has been completed successfully. +5. The client can call any other CFSTORE methods for the KV using key_handle, as required. +6. When the client has finished KV pair operations, the client calls drv->Close(key_handle) to return the opaque key context to CFSTORE. +7. CFSTORE returns OK, which in this case means the Close() has been completed successfully. +8. The client can repeat operation (1)-(7) to create all KV pairs, as required. +9. Once all CFSTORE KV changes have be made, the client calls drv->Flush() to commit the changes to backing store. +10. CFSTORE returns OK, which in this case means the Flush() has been completed successfully. + +See the [CFSTORE low level Design][CFSTORE_LLD] for the detailed specification for function prototypes. + + +## API Call Sequence Diagram for Create() Key (Async, Success) + +

+ +

+ +The above diagram shows the client-CFSTORE call sequence for creating a KV for an asynchronous CFSTORE implementation +(drv->GetCapabilites() has returned an `ARM_Cfstore_Capabilities_t` structure with the asynchronous flag set). + +1. The client calls drv->Create(key_name, value_len, key_descriptor, &key_handle) to request CFSTORE create the KV pair. +2. CFSTORE returns OK, which in this case means the Create() is a pending transaction. +3. CFSTORE completes internal operations necessary to create the KV pair. +4. Once internal create operations have been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=CREATE, status, client_context, key_handle)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status == OK, key_handle + is an opaque handle to the newly created open key. +5. The client calls drv->Write(key_handle, data, len) to set the data value in the KV. +6. CFSTORE returns OK, which in this case means the Write() is a pending transaction. +7. CFSTORE completes internal operations necessary to write to the KV pair. +8. Once internal write operations have been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=WRITE, status, client_context, key_handle)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status > 0, the value + of status indicates the number of bytes successfully written. +9. The client can perform other operations on the KV pair using the key_handle. +10. The client calls drv->Close(key_handle) to close the recently created key. +11. CFSTORE returns OK, which in this case means the close is a pending transaction. +12. CFSTORE completes internal operations necessary to close to the KV pair. +13. Once internal close operations have been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=CLOSE, status, client_context, key_handle)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status == OK, the + key has successfully been closed and key_handle is NULL. The previously used key_handle should no longer be used. +14. The client can repeat operation (1)-(13) to create, write and close all KV pairs, as required. +15. Once all CFSTORE KV changes have be made, the client calls drv->Flush() to commit the changes to backing store. +16. CFSTORE returns OK, which in this case means the Flush() is a pending transaction. +17. CFSTORE completes internal operations necessary to flush the changes. +18. Once internal flush operations have been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=FLUSH, status, client_context, NULL)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status == OK, the + flush operation was completed successfully. + +Note this example is the pathological case where all CFSTORE methods return OK i.e. the transactions are pending. +In reality, some calls will be completed synchronous and successfully, indicated by returning a value > 0. + +See the [CFSTORE low level Design][CFSTORE_LLD] for the detailed specification for function prototypes. + + +## API Call Sequence Diagram for Open()/Read() Key (Sync, Success) + +

+ +

+ +The above diagram shows the client-CFSTORE call sequence for opening and reading a pre-existing key in the CFSTORE for a synchronous CFSTORE implementation +(drv->GetCapabilites() has returned an `ARM_Cfstore_Capabilities_t` structure with the synchronous flag set). + +1. The client calls drv->Open(key_name, flags, &key_handle) to request CFSTORE to open the KV pair in read-only mode. +2. CFSTORE returns OK, which in this case means the Open() has been completed successfully. key_handle is now set to a valid opaque handle to the open KV. +3. The client calls drv->Rseek(key_handle, offset) to set the read location within the value data. The Read() method supports random-access. +4. CFSTORE returns OK, which in this case means the Rseek() has been completed successfully. +5. The client calls drv->Read(key_handle, data, len) to read the data value in the KV. +6. CFSTORE returns OK, which is this case means the Read() has been completed successfully. +7. The client can call other CFSTORE methods (except Write() as this key_handle is a read-only) for the KV using key_handle, as required. +8. When the client has finished KV pair operations, the client calls drv->Close(key_handle) to return the opaque key context to CFSTORE. +9. CFSTORE returns OK, which in this case means the Close() has been completed successfully. +10. The client can repeat operation (1)-(9) to read all KV pairs, as required. + +See the [CFSTORE low level Design][CFSTORE_LLD] for the detailed specification for function prototypes. + + +## API Call Sequence Diagram for Open()/Read() Key (Async, Success) + +

+ +

+ +The above diagram shows the client-CFSTORE call sequence for opening and reading a pre-existing key in the CFSTORE for an asynchronous CFSTORE implementation +(drv->GetCapabilites() has returned an `ARM_Cfstore_Capabilities_t` structure with the asynchronous flag set). + +1. The client calls drv->Open(key_name, flags, &key_handle) to request CFSTORE to open the KV pair. +2. CFSTORE returns OK, which in this case means the Open() is a pending transaction. +3. CFSTORE completes internal operations necessary to open the KV pair. +4. Once internal open operations have been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=OPEN, status, client_context, key_handle)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status == OK, + key_handle is a valid opaque handle to the newly opened KV. +5. The client calls drv->Rseek(key_handle, offset) to set the read location within the value data. The Read() method supports random-access. +6. CFSTORE returns OK, which in this case means the Rseek() is a pending transaction. +7. CFSTORE completes internal operations necessary to rseek to read location. +8. Once internal rseek operations have been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=RSEEK, status, client_context, key_handle)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. +9. The client calls drv->Read(key_handle, data, len) to read the data value in the KV at the read location. +10. CFSTORE returns OK, which in this case means the Read() is a pending transaction. +11. CFSTORE completes internal operations necessary to read the value data. +12. Once internal read operations have been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=READ, status, client_context, key_handle)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. +13. The client can call other CFSTORE methods for the KV using key_handle, as required. +14. The client calls drv->Close(key_handle) to close the KV handle. +15. CFSTORE returns OK, which in this case means the Close() is a pending transaction. +16. CFSTORE completes internal operations necessary to close to the KV pair. +17. Once internal close operations have been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=CLOSE, status, client_context, key_handle)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status == OK, the + close operation completed successfully, key_handle is null and the previously stored key_handle value is no longer valid. +18. The client can repeat operation (1)-(17) to create, write and close all KV pairs, as required. + +Note this example is the pathological case where all CFSTORE methods return OK i.e. the transactions are pending. +In reality, some calls will be completed synchronous and successfully, indicated by returning a value > 0. + +See the [CFSTORE low level Design][CFSTORE_LLD] for the detailed specification for function prototypes. + + +## API Call Sequence Diagram for Open()/Write() Key (Sync, Success) + +

+ +

+ +The above diagram shows the client-CFSTORE call sequence for opening and writing a pre-existing key in the CFSTORE for a synchronous CFSTORE implementation +(drv->GetCapabilites() has returned an `ARM_Cfstore_Capabilities_t` structure with the synchronous flag set). + +1. The client calls drv->Open(key_name, flags, &key_handle) to request CFSTORE to open the KV pair (read-write access is the default access mode). +2. CFSTORE returns OK, which in this case means the Open() has been completed successfully. +3. The client calls drv->Write(key_handle, data, len) to set the data value in the KV. Note that Write() + only supports sequential-access and that len must not exceed the value_len field specified when the KV pair was created. +4. CFSTORE returns OK, which in this case means the Write() has been completed successfully. +5. The client can call other CFSTORE methods for the KV using key_handle, as required. +6. When the client has finished KV pair operations, the client calls drv->Close(key_handle) to return the opaque key context to CFSTORE. +7. CFSTORE returns OK, which in this case means the Close() has been completed successfully. +8. The client can repeat operation (1)-(7) to create all KV pairs, as required. +9. Once all CFSTORE KV changes have be made, the client calls drv->Flush() to commit the changes to the backing store. +10. CFSTORE returns OK, which in this case means the Flush() has been completed successfully. + +See the [CFSTORE low level Design][CFSTORE_LLD] for the detailed specification for function prototypes. + + +## API Call Sequence Diagram for Open()/Write() Key (Async, Success) + +

+ +

+ +The above diagram shows the client-CFSTORE call sequence for opening and writing a pre-existing key in the CFSTORE for an asynchronous CFSTORE implementation +(drv->GetCapabilites() has returned an `ARM_Cfstore_Capabilities_t` structure with the asynchronous flag set). + +1. The client calls drv->Open(key_name, flags, &key_handle) to request CFSTORE to open the KV pair. +2. CFSTORE returns OK, which in this case means the Open() is a pending transaction. +3. CFSTORE completes internal operations necessary to create the KV pair. +4. Once internal create operations have been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=OPEN, status, client_context, key_handle)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status == OK, + key_handle is a valid handle to the open KV. +5. The client calls drv->Write(key_handle, data, len) to set the data value in the KV. Note that Write() + only supports sequential-access and that len must not exceed the value_len field specified when the KV pair was created. +6. CFSTORE returns OK, which in this case means the Write() is a pending transaction. +7. CFSTORE completes internal operations necessary to write to the KV pair. +8. Once internal write operations have been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=WRITE, status, client_context, key_handle)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. +9. The client can perform other operations on the KV pair using the key_handle. +10. The client calls drv->Close(key_handle) to close the KV key_handle. +11. CFSTORE returns OK, which in this case means the close is a pending transaction. +12. CFSTORE completes internal operations necessary to close to the KV pair. +13. Once internal close operations have been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=CLOSE, status, client_context, key_handle)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. For status == OK, the close + operation was successfully, key_handle is NULL and if the key_handle value was previously stored it is no longer valid. +14. The client can repeat operation (1)-(13) to create, write and close all KV pairs, as required. +15. Once all CFSTORE KV changes have be made, the client calls drv->Flush() to commit the changes to backing store. +16. CFSTORE returns OK, which in this case means the Flush() is a pending transaction. +17. CFSTORE completes internal operations necessary to flush the changes. +18. Once internal flush operations have been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=FLUSH, status, client_context, NULL)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. + +Note this example is the pathological case where all CFSTORE methods return OK i.e. the transactions are pending. +In reality, some calls will be completed synchronous and successfully, indicated by returning a value > 0. + +See the [CFSTORE low level Design][CFSTORE_LLD] for the detailed specification for function prototypes. + + +## API Call Sequence Diagram for Find() Key (Sync, Full and Part Walk Success) + +

+ +

+ +The above diagram shows the client-CFSTORE call sequence for finding pre-existing keys in the CFSTORE for a synchronous CFSTORE implementation. +(drv->GetCapabilites() has returned an `ARM_Cfstore_Capabilities_t` structure with the synchronous flag set). The example shows a complete +walk of all the find results. + +1. The client calls drv->Find(key_name_search_string, &next, prev=NULL) to request CFSTORE to find matching KV pairs. +2. CFSTORE returns OK, which in this case means the Find() has been completed successfully. next points to an open key + handle for the first KV with key_name matching the key_name_search_string. +3. The client decides this KV requires no new changes so iterates to get the next match. It sets prev to be the previously return key handle i.e. prev=next. +4. The client calls drv->Find(key_name_search_string, &next, prev) to request CFSTORE to find matching KV pairs. Calling Find() with the open key handle prev + returns the key handle to CFSTORE which closes the handle. +5. CFSTORE returns OK, which in this case means the Find() has been completed successfully. next points to an open key + handle for the second KV with key_name matching the key_name_search_string. +6. The client decides this KV requires no new changes so iterates to get the next match. It sets prev to be the previously return key handle i.e. prev=next. +7. The client calls drv->Find(key_name_search_string, &next, prev) to request CFSTORE to find matching KV pairs. Calling Find() with the open key handle prev + returns the key handle to CFSTORE which closes the handle. +8. CFSTORE returns OK, which in this case means the Find() has been completed successfully. next points to an open key + handle for the third KV with key_name matching the key_name_search_string. + +For the top alternative "Use Case for Full Walk of All Matching Results": + +- 9. The client decides this KV requires new changes and calls additional operations on this KV to change the value data, for example. +- 10. The client repeats operations as indicated by (6)-(8) for other matching keys. +- 11. After the penultimate call to Find() the client sets prev to be the previously return key handle i.e. prev=next. +- 12. The client calls drv->Find(key_name_search_string, &next, prev) to request CFSTORE to find matching KV pairs. Calling Find() with the open key handle prev + returns the key handle to CFSTORE which closes the handle. +- 13. CFSTORE returns OK, which in this case means the Find() has been completed successfully. However, next is set to NULL + indicating there are no more KVs matching key_name_search_string. The iteration has completed. + +For the bottom alternative "Use Case for Partial Walk of All Matching Results": + +- 9. The client has found the desired KV and performs operations on the KV as required. +- 10. To terminate the iteration, the client calls drv->Close(next) for CFSTORE to close the open file handle. +- 11. CFSTORE returns OK, which in this case means the Close() has been completed successfully. + + +See the [CFSTORE low level Design][CFSTORE_LLD] for the detailed specification for function prototypes. + + +## API Call Sequence Diagram for Find() Key (Async, Full and Part Walk Success) + +

+ +

+ +The above diagram shows the client-CFSTORE call sequence for finding pre-existing keys in the CFSTORE for an asynchronous CFSTORE implementation. +(drv->GetCapabilites() has returned an `ARM_Cfstore_Capabilities_t` structure with the synchronous flag set). The example shows a complete walk +of all the find results. + +1. The client calls drv->Find(key_name_search_string, &next, prev=NULL) to request CFSTORE to find matching KV pairs. +2. CFSTORE returns OK, which in this case means the Find() transaction is pending. +3. CFSTORE completes internal operations necessary to find the next matching KV pair. +4. Once internal operations have been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=FIND, status, client_context, next)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. + For status == OK, next is an open key handle to a KV matching the key_name_search_string. +5. The client decides this KV requires no new changes so iterates to get the next match. + It sets prev to be the previously return key handle i.e. prev=next. +6. The client calls drv->Find(key_name_search_string, &next, prev) to request CFSTORE + to find matching KV pairs. Calling Find() with the open key handle prev + returns the key handle to CFSTORE which closes the handle. +7. CFSTORE returns OK, which in this case means the Find() transaction is pending. +8. CFSTORE completes internal operations necessary to find the next matching KV pair. +9. Once internal operations have been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=FIND, status, client_context, next)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. + For status == OK, next is an open key handle to a KV matching the key_name_search_string. + +For the top alternative "Use Case for Full Walk of All Matching Results": + +- 10. The client decides this KV requires changes so it makes other CFSTORE calls to modify the KV. Operations (6)-(9) are + repeated to find and operate upon other matching KVs. +- 11. Operations (10)-(15) are repeated to find and operate upon other matching KVs. +- 12. After the penultimate call to Find() the client sets prev to be the previously return key handle i.e. prev=next. +- 13. The client calls drv->Find(key_name_search_string, &next, prev) to request CFSTORE to find matching KV pairs. Calling Find() with the open key handle prev + returns the key handle to CFSTORE which closes the handle. +- 14. CFSTORE returns OK, which in this case means the Find() transaction is pending. +- 15. CFSTORE completes internal operations necessary to find the next matching KV pair. +- 16. CFSTORE returns OK, which in this case means the Find() has been completed successfully. However, next is set to NULL + indicating there are no more KVs matching key_name_search_string. The iteration has completed. + +For the bottom alternative "Use Case for Partial Walk of All Matching Results": + +- 10. The client decides this KV requires changes so it makes other CFSTORE calls to modify the KV. +- 11. To terminate the iteration, the client calls drv->Close(next) for CFSTORE to close the open file handle. +- 12. CFSTORE returns OK, which in this case means the Close() transaction is pending. +- 13. CFSTORE completes internal operations necessary to find the next matching KV pair. +- 14. Once internal close operations have been completed, CFSTORE invokes `Cfstore_Client_callback(OPCODE=CLOSE, status, client_context, key_handle)` + to notify the client of the completion status. The previously registered client_context is supplied as an argument. + +Note this example is the pathological case where all CFSTORE methods return OK i.e. the transactions are pending. +In reality, some calls will be completed synchronous and successfully, indicated by returning a value > 0. + +See the [CFSTORE low level Design][CFSTORE_LLD] for the detailed specification for function prototypes. + +## API Call Sequence Diagram for PowerControl() (Sync, Success) + +

+ +

+ +The above diagram shows the client-CFSTORE call sequence for setting the CFSTORE PowerControl() setting. The call is synchronous. + +1. The client calls drv->PowerControl(level) requesting the desired power level. +2. CFSTORE returns that the power control level has been set. + + +## CFSTORE Client Increases/Decreases the Size of the KV Value Blob + +In order to increase the size of the pre-existing KV={key_name, value1, len1} to +{key_name, value2, len2} where len2 != len1 then the client should +call Create() on a pre-existing key, supplying NULL for the key descriptor argument +and the new length for the value_len argument. + +The procedure can be realised in the following way (in the synchronous case) to double +the size of a pre-existing KV value blob to hold a duplicate of the data: + +```C + ARM_Cfstore_Handle_t hkey + ARM_Cfstore_Fmode_t flags = 0; + const char *key_name = "mykeyname"; + uint32_t len = 0; + uint32_t value_len = 0; + void* data = NULL; + ARM_Cfstore_KeyDesc_t kdesc; + + // Open a pre-existing KV to find the length and read the value data + drv->Open(key_name, flags, &hkey); + // store key_name and value + drv->GetValueLen(hkey, &value_len); + data = malloc(value_len); + len = value_len; + drv->Read(hkey, data, &len) + // Read() returns bytes read in len. Assume that Read() call has read all the data + drv->Close(hkey) + + // Call Create() with kdesc=NULL to grow the value length to increase + // the blob size. + drv->Create(key_name, 2 * value_len, NULL, &hkey); + // store the data. This first Write() writes bytes 0 to value_len-1 in the + // value blob (sequential-access). + len = value_len; + drv->Write(hkey, data, &len) + // Write() returns bytes written in len. Assume write has written value_len bytes + // write supports sequential access. The second Write() writes bytes + // value_len to 2*value_len -1 in the value blob. + len = value_len; + drv->Write(hkey, data, &len) + // Write() returns bytes written in len. Assume write has written value_len bytes + drv->Close(hkey) + + // de-init + free(data); +``` + +## CFSTORE Client Writes KV Value Blob Size > Available SRAM + +Consider the case where a client needs to write a value blob whose size exceeds the +available SRAM. When writing a data buffer of size N bytes, CFSTORE may require +N bytes SRAM plus some additional overhead for setting up the storage transaction. +In the case that N exceeds the available SRAM remaining, the client can s +split the writing of the value into M writes, where N/M is smaller than available memory. + +In the case the the Write() call fails with return code `ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY` +then the client should retry the write transaction with a smaller length (less bytes). + +Further, a client of the Write() function should alway check the returned value of the +len argment as this records the actual number of bytes written. CFSTORE may be able to +write a number of bytes less that the total number in the data buffer supplied by the +client. The unwritten bytes should be supplied to a second call to Write(). + + +## CFSTORE Client Changes the Access Permissions of a KV + +In order to changes the Access Control Permission permissions of the pre-existing +KV={key_name, value1, len1, kdesc1} to +{key_name, value1, len1, kdesc2} where kdesc1 != kdesc2 then the client should use the following +procedure: + +- Open the pre-existing KV1 +- Read and store the value length, value data and permissions (if required) +- Delete the pre-existing key KV1 +- Create a new key KV2 with the same name as KV1 but with new permissions kdesc2. +- Write the saved value data into KV2 +- Close KV2. + +The procedure can be realised as illustrated in the following example code: + +```C + /* @brief function to changes the permissions on KV pair + * + * @param key_name + * pre-existing KV key name for which the permissions will be + * changed. + * @param kdesc + * key descriptor describing the new properties including + * permissions. + */ + void myclient_change_kv_perms(const char *key_name, ARM_Cfstore_KeyDesc_t kdesc) + { + + ARM_Cfstore_Handle_t hkey + ARM_Cfstore_Fmode_t flags = 0; + uint32_t value_len = 0; + void* data = NULL; + + // Get KV data from store + drv->Open(key_name, flags, &hkey); + // store key_name and value + drv->GetValueLen(hkey, &value_len); + data = malloc(value_len); + drv->Read(hkey, data, value_len) + drv->Delete(hkey) + drv->Close(hkey) + + // Re-create new KV with same name and new permissions + drv->Create(key_name, value_len, &kdesc, &hkey); + // store key_name and value + drv->Write(hkey, data, value_len) + drv->Close(hkey) + + // de-init + free(data); + } +``` + + +## A CFSTORE GetKeyDesc() Method is Not Supported + +The Create() method uses a key descriptor to request storage properties for the KV pair. +For example, the descriptor may include the following attributes: + +- The NV storage retention level e.g. whether the KV is stored on internal or external NV storage, + battery backed or internal SRAM memory. +- The access control list for the supported groups of owner and other. +- The NV storage security settings e.g. whether the storage should implement hardening against + side channel attacks. + +Associative store APIs often include a GetKeyDesc() convenience method permitting clients to +retrieve the KV descriptor after creation. This is not included in the CFSTORE API for the +following reasons: + +- On Create() the key descriptor specifies options for KV creation e.g. the + descriptor may include both of the following options: + - The KV may be created/stored in internal NV storage with software hardening. + - Alternatively, the KV may be created/stored in external NV storage with software hardening. + + Upon KV creation the CFSTORE may elect to store the KV in internal NV storage. However, over time + internal storage space may come at a premium and CFSTORE may decide to move the KV to a less used + external NV store. The movement of the KV from internal to external storage requires: + - Greater software support (locking against race conditions) to ensure a client always receives + correct information about where the KV is currently stored. + - The storing of the descriptor settings supplied to Create() and the subsequent current descriptor + settings. +- The actual utility of the KV descriptor information to a client after KV creation is limited. The + design philosophy is that the client trusts CFSTORE correctly stores and manages the + KV and that the descriptor attributes should change after creation. + + +## CFSTORE Cancel() Method For Terminating In-Progress Async Transactions is Not Supported + +Associative store APIs often include a Cancel() method for terminating in-flight asynchronous +transactions. This is not supported in the current API to simplify the implementation. All +asynchronous transactions will have associated guard timers to guarantee the termination of errored +transactions. + + +# Contributors + +This document was made possible through the contributions of the following people: + +- Rohit Grover +- Simon Hughes +- Milosch Meriac + + +# References + +* The [CFSTORE Product Requirements][CFSTORE_PRODREQ] +* The [CFSTORE Engineering Requirements][CFSTORE_ENGREQ] +* The [CFSTORE High Level Design Document][CFSTORE_HLD] +* The [CFSTORE Low Level Design Document][CFSTORE_LLD] +* The [CFSTORE Terminology for definition of terms used in CFSTORE documents][CFSTORE_TERM] +* The [Flash Abstraction Layer][FAL] + + +[FAL]: https://github.com/ARMmbed/flash-abstraction +[CFSTORE_PRODREQ]: https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_product_requirements.md +[CFSTORE_ENGREQ]: https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_requirements.md +[CFSTORE_LLD]: https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_lld.md +[CFSTORE_HLD]: https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_hld.md +[CFSTORE_TERM]: https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_terminology.md +[KEIL_CMSIS_DRIVER]: http://www.keil.com/pack/doc/CMSIS/Driver/html/index.html \ No newline at end of file diff --git a/storage/cfstore/doc/design/configuration_store_lld.md b/storage/cfstore/doc/design/configuration_store_lld.md new file mode 100644 index 0000000000..9139115f36 --- /dev/null +++ b/storage/cfstore/doc/design/configuration_store_lld.md @@ -0,0 +1,17 @@ +# Configuration Store Low Level Design +Author: Simon Hughes + +# Revision History +20160127: first version. + +# API + + +## CFSTORE Client API interface + +The CFSTORE client API interface is represented diagramatically in the following UML digarm + +![CFSTORE_UML_HLD_01](https://github.com/ARMmbed/configuration-store/blob/master/doc/design/pics/configuration_store_hld_api_summary.jpg) + + +The above UML diagram shows the low level design of the CFSTORE API. diff --git a/storage/cfstore/doc/design/configuration_store_product_requirements.md b/storage/cfstore/doc/design/configuration_store_product_requirements.md new file mode 100644 index 0000000000..1614130a78 --- /dev/null +++ b/storage/cfstore/doc/design/configuration_store_product_requirements.md @@ -0,0 +1,107 @@ +# IoT\Product Requirements\IOTPREQ-334: Design of the mbed OS Configuration Store + +The Configuration Store forms a building block of mbed OS. It forms a central repository where configuration for many modules can be placed. This hides the complexity of implementing a similar system from each module owner and it encourages rich configuration interfaces for the developer. + +## Background +Configuration is a common operation in embedded systems. It happens at compile time, and run time. Sometimes runtime configuration is persistent across resets, sometimes it is not. Frequently, configuration data is sensitive, such as WiFi passwords, API tokens, encryption keys, and the like. Some configuration is only accessed once in a while. Other configuration must be changed and accessed very frequently and with very little overhead. + +## Sample Use Cases +Several sample use cases were used to derive the requirements of the Configuration Store + +### Network Configuration +Network configuration varies by network type. As a result, presenting a common interface for network configuration is difficult. Using a pointer to a configuration blob simplifies the Network API and concentrates complexity and knowledge about the network implementation in the network driver and the configuration mechanism in the application. + +Networks need hierarchical configuration. A flat model starts to fail when multiple interfaces with similar parameters are used. Most networks need non-volatile, runtime configuration, but Wi-Fi demonstrates this need the best: configuring a Wi-Fi network on a device requires, at minimum, selecting a SSID and entering a password. These must persist past power cycles. Network configuration needs to support overrides. When configuring a network device, it should be possible to recover old configuration until new configuration is committed to non-volatile storage. A network device should ship with sensible default configuration (e.g. DHCP), but this should be overridden when necessary. + +Network configuration requires many kinds of value: integer (Channel number), string (SSID), binary blob (hashed password). There is an argument for floating point (transmit power), but this can be done via integer and fixed-point. + +### Credential Storage +Storing credentials requires secure access to the storage. As a result, it must be possible to apply permissions to parts or the whole of the tree. Exactly how these permissions work is TBD. + +### System initialization +It is conceptually possible to reduce the number of code versions by using more configuration. For example, clock configuration can be done using the config mechanism. If this is the case, then the permanent config store must be accessible early in the boot process, when clocks are configured. + +It is necessary to provide a list of modules that explicitly require an init function to be called. To make this possible, those functions could be listed, in order in the configuration store. This has some advantages over conventional approaches, such as linker sections, in that it provides a much more readable way to inspect the modules that are being initialized. + +### Resource Management +In the future, a resource manager may be an integral part of mbed OS. In this instance, the resource manager needs in-depth information about how peripherals are connected to clocks and power domains. The Configuration Store should contain this information. + +### Peripheral Configuration +If the system interface API (mbed-drivers) were aware of the Configuration Store, then it would be straight-forward to encode defaults via config. This would allow the interface API to extract application-dependent, but still sensible defaults from the config store when they are omitted. This could be combined with Resource Management to automatically correct for clock scaling of buses or the core. + +## Design Goals +A number of design goals must be met for the configuration store to be used widely. These design goals are brought out of +The configuration store must: + +* hierarchical + * configuration data must be groupable + * groups of configuration data should be handled as a unit +* support multiple degrees of persistence + * permanent + * non-volatile + * volatile +* allow config overrides between persistence levels (volatile being the highest priority and permanent the lowest) +* support multiple sources of configuration + * yotta config + * application configuration + * runtime configuration + * remote configuration (e.g. CBOR blobs returned by a server) +* support multiple kinds of values + * integer + * floating point + * string + * arbitrary binary blob +* be compatible with [yotta config](http://yottadocs.mbed.com/reference/config.html) output + * Any format to which JSON is convertible fulfills this requirement +* globally accessible by any module +* able to enforce permissions on entire branches of the config data +* accessible early +* Low memory footprint for parsing +* Low CPU overhead for data accesses + +## CBOR +The simplest approach to guaranteeing interoperability with yotta config is to ensure that the format of the Config Store is convertible to JSON. There are several options for this, but the most promising is CBOR. CBOR may not be appropriate for an in-memory representation, but it is likely appropriate for both permanent and non-volatile representations. + +Selection of CBOR as an intermediate representation also satisfies the hierarchy requirement and multiple value requirement. CBOR may require a significant CPU/memory tradeoff to be made during parse operations, but there are several mitigation strategies that can be employed. + +## Applying permissions +It may be very difficult to apply permissions to a CBOR tree directly. A review of permission management with the uVisor team is necessary. + +## Configuration Overrides +To ensure that the correct configuration is always used, permanent configuration must be overriden by non-volatile configuration. Likewise, non-volatile configuration must be overridden by volatile configuration. + +The exact method of overrides is TBD. + +An initial assessment of this suggests that it would be feasible to accomplish overrides by using only two layers instead of three. On boot, the non-volatile storage is parsed into the volatile storage area. When a node is deleted and its parent is a non-volatile storage element, the key of the deleted node should be re-parsed from non-volatile storage into the location of the deleted node. + +## Early Accessibility +Configuration Store initialization must occur after C library init, but before static object initialization, so that static objects can access the Configuration Store. It is possible that a limited version of the Configuration Store could be used prior to C library init, but it would need to be written very carefully. + +## Configuration Sources +In some cases, it might be desirable to treat configuration delivered by a server as non-volatile. Changes can be applied locally or requested remotely, but the server is always queried on startup to obtain the configuration data. In general, yotta config will aggregate all configuration data and supply it to the build system in a single JSON file. + +Where credentials are provisioned at time of programming, it might be desirable to supply an alternative method of installing a source of configuration. The method for using this alternative source is TBD. + +# Low Level Design +The Configuration Store is built in three tiers: +* Permanent Storage +* Non-volatile Storage +* Volatile Storage + +## Permanent Storage +Permanent configuration is generated as a CBOR translation of the JSON file created by yotta config. It is linked into the final executable. Parsing permanent storage requires a CPU/memory tradeoff to be made. Indices into the CBOR object can be retained either in RAM or ROM to reduce parsing time at the expense of additional memory. Parsing could be done on-demand or in advance. + +## Non-volatile Storage +Non-volatile storage has no specific format requirements, however reusing CBOR parsing from permanent storage may be a good solution. When a change is committed to non-volatile storage, the non-volatile portions of the Configuration store must be re-encoded for storage to flash. + +## Volatile Storage +There are two possibilities for implementing volatile storage: either a document object model, or a flat key-value store with recognition of prefixes. The document object model will present a familiar interface for web programmers, and occupy less memory than a flat key-value store. Therefore, a document object model is the preferred solution. + +### Objects containing constant data +For most operations, the Configuration Store must copy both keys and values into its own storage. In some cases, it may be advantageous to only retain a reference to external data, such as in cases where external data has been stored in a nonvolatile medium. While this is not an immediate requirement, care should be taken not to prevent this future optimization. + + +# References + +- IoT\Product Requirements\IOTPREQ-334 ![CFSTORE_PROD_REQ_REF_01](http://jira.arm.com/browse/IOTPREQ-334) + diff --git a/storage/cfstore/doc/design/configuration_store_project.md b/storage/cfstore/doc/design/configuration_store_project.md new file mode 100644 index 0000000000..03c6c3897b --- /dev/null +++ b/storage/cfstore/doc/design/configuration_store_project.md @@ -0,0 +1,99 @@ +# Configuration Store Project Documentation + + + +# Feature 1 (FT1): SRAM-backed CFSTORE (Complete) +This feature includes the following: + +- CFSTORE API implementation backed by SRAM (non-persistent). +- Synchronous mode implementation. +- Supported on the K64F target. +- mbed greentea tests demonstrating how to use the API. +- High Level Design Document present. +- Alpha grade code quality (testing giving ~50% code coverage). +- area_0 SRAM initialisation using target.json generated CFSTORE_SRAM_START_ADDR & CFSTORE_SRAM_SIZE #defines rather than malloc(). + + +## Features Not Included in this Feature (Complete) + +This feature does not including the following: +- Integration with the Flash Abstraction Layer. +- Integration with uVisor. + + +# Feature 2: CFSTORE Flash Ping-Pong Alpha (Complete) +- CFSTORE API implementation backed by SRAM and Non-Volatile (NV) storage. +- Flash-jounral asynchronous mode support. +- Flash-jounral synchronous mode support. +- Flash-jounral supported on the K64F target. +- High Level Design Document present. + + +# Feature 3.0: CFSTORE+SRAM with mbedOS Q2 Release (Complete) +- CFSTORE SRAM version ported to mbedOS Q2 Release. + + +# Feature 3.1: CFSTORE + Flash-Journal + mbedOS Q2 Release. +- CFSTORE with Flash-Journal integration ported to mbedOS Q2 Release. + +# Feature 4: CFSTORE + uvisor + mbedOS Q2 Release +- CFSTORE with Flash-Journal integration ported to mbedOS Q2 Release. +- uvisor integration working + + +# Guidance for Submitting Issues + +Here are the guidelines for submitting issues to the configuration-store repository: + +- If you have an issue with configuration-store then please file an issue at github.com/armmbed/configuration-store.git +- The issue should be filed with a stand-alone testcase or sample application that only depends on configuration-store and shows the fault on the target platform. +- The issue should include the following information under separate SUMMARY and DETAILS headings: + +``` + + SUMMARY + ======= + APPLCIATION/Library NAME: + OS VERSION: + TARGET: + TOOLCHAIN: + DESCRIPTION: + MODULE DEPENDENCIES: + + DETAILS + ======= + +``` + + + +# References + +## Useful Documents + +- [KeyValueStorage.md](https://github.com/ARMmbed/device-security/blob/master/KeyValueStorage.md) +- [uVisor README.md](https://github.com/ARMmbed/uvisor/blob/master/README.md) +- [FlashAbstraction](https://github.com/ARMmbed/device-security/blob/master/FlashAbstraction.md) + + +## Useful GitHub Repositories + +- [device-security](https://github.com/ARMmbed/device-security) + + +## Useful References + +- [mbed Coding Standard](https://developer.mbed.org/teams/SDK-Development/wiki/mbed-sdk-coding-style) +- [Security Ref](http://resources.sei.cmu.edu/asset_files/BookChapter/2005_009_001_52692.pdf) +- [Security Ref](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1255.pdf) + + +## Useful Jira Tickets + +- [Configuration Store for mbed OS IOT Product Requirements IOTPREQ-334](http://jira.arm.com/browse/IOTPREQ-334) + + diff --git a/storage/cfstore/doc/design/configuration_store_requirements.md b/storage/cfstore/doc/design/configuration_store_requirements.md new file mode 100644 index 0000000000..e537a47ea4 --- /dev/null +++ b/storage/cfstore/doc/design/configuration_store_requirements.md @@ -0,0 +1,410 @@ +# ENGINEERING REQUIREMENTS + +## Definition of Terms + + +https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_terminology.md + +The [CFSTORE Terminology][CFSTORE_TERM] defines terms used in this document. + + +## Secure Key Value Storage\Rationale (REQ-1.xx-SKVS-R) Requirements + +##### REQ-1.01-SKVS-R: +The CFSTORE must provide a non-volatile, hardware-independent secure storage +service that can be used by CFSTORE API clients for storing data owned +by a client security context (uvisor box). + +##### REQ-1.02-SKVS-R: +Clients of the CFSTORE API must be security contexts (boxes) so that security +policies can be enforced. + +##### REQ-1.03-SKVS-R: +The CFSTORE design values simplicity over complexity to promote security by +minimizing the attack surface to secure data. + +##### REQ-1.04-SKVS-R: +The CFSTORE must be implemented as a uvisor secure box. + +##### REQ-1.05-SKVS-R: +The CFSTORE must support the use of Access Control Lists for policing access to +secure data. + +##### REQ-1.06-SKVS-R: +The CFSTORE must support the storage of a {key, value} tuples where +- the key is a zero terminated string of arbitrary length. +- the value is a binary blob of data of arbitrary length + +The {key, value} tuple is a CFSTORE object. + +###### Discussion +A null key (i.e. "" which includes a terminating null) is not a valid because the null string is not a valid security_prefix_name. + +##### REQ-1.07-SKVS-R: CFSTORE Stores Binary Blobs +The CFSTORE must provide support so that other (client) components can implement +more complex storage types other than a binary blob e.g. by +wrapping the underlying CFSTORE {key, value} tuple with additional +type information. + +##### REQ-1.08-SKVS-R: CFSTORE Key Structure as Name Strings +This requirement has been removed as it is a subset of REQ-1.06-SKVS-R [REQ-1.06-SKVS-R]. + +##### REQ-1.09-SKVS-R: Key Creation and Ownership Part 1 +CFSTORE keys must be owned by the security context. A security context (box) +is identified by a unique security_name_prefix and this identifier +must be used when the key is created. CFSTORE must support the use of the +security_name_prefix as the leading substring of the CFSTORE key string, in which +case the key_name_prefix is the security_name_prefix and identifies the +owner of the key. + +##### REQ-1.10-SKVS-R: Security Context +In order to create objects in the CFSTORE, a security context (box) must have +a security_name_prefix. + +##### REQ-1.11-SKVS-R: CFSTORE object +This requirement has been removed as it is a duplicate of REQ-1.10-SKVS-R + +## Secure Key Value Storage High Level Design (REQ-1.2.xx-SKVS-HLD) Requirements + +##### REQ-1.2.01-SKVS-HLD: +The CFSTORE must be able to detect the available types of storage media +in the system and report associated storage media attributes. + +##### REQ-1.2.02-SKVS-HLD: +The CFSTORE may report the following storage media data retention level attribute, +when available: +- only during device activity +- during sleep +- during deep-sleep +- battery-backed, device can be powered off +- internal non-volatile memory +- external non-volatile memory + +##### REQ-1.2.03-SKVS-HLD: +For a particular storage medium, the CFSTORE may report the following device +data security protection features, when available: +- no security, just safety +- write-once-read-only-memory (WORM) +- against internal software attacks using ACLs +- roll-back protection +- immovable (for internal memory mapping, to stop relocated block to move). + This attribute must only be set provided: + - the device memory is mapped into the CPU address space + - access is only granted to specific CFSTORE API system clients e.g. FOTA, + DMA. +- hardening against device software (malware running on the device) +- hardening against board level attacks (debug probes, copy protection + fuses) +- hardening against chip level attacks (tamper-protection) +- hardening against side channel attacks +- tamper-proof memory (will be deleted on tamper-attempts using board level + or chip level sensors) + +##### REQ-1.2.04-SKVS-HLD: +The CFSTORE may be used to implement KV storage protection services +(e.g. flash image roll-back protection, confidentiality) for off-chip +(external) storage media (e.g. SPI/I2C/NAND Flash). + +##### REQ-1.2.05-SKVS-HLD: +The device data security protection immovable attribute may only be set + + +## Secure Key Value Storage\High Level API Description\Key Value Storage (REQ-3.1.xx-SKVS-HLAPID-KVS) Requirements. + +##### REQ-3.1.01-SKVS-HLAPID-KVS: CFSTORE Global Key Namespace +The CFSTORE must implement a system global hierarchical tree name-space for +keys. The namespace is shared by all software components operating within +the system. Examples of key names include the following: + - 'com.arm.mbed.wifi.accesspoint[5].essid' + - 'com.arm.mbed.wifi.accesspoint[home].essid' + - 'yotta.your-yotta-registry-module-name.your-value' + - 'yotta.hello-world.animal{dog}{foot}[3]' + The key name string forms a path where 'Path Directory Entries' are + separated by the '.' character. + +##### REQ-3.1.02-SKVS-HLAPID-KVS: CFSTORE Key Name String Format Max Length +For CFSTORE keys The maximum length of a CFSTORE key is 220 characters excluding +the terminating null. + +##### REQ-3.1.03-SKVS-HLAPID-KVS: CFSTORE Key Name String Allowed Characters +CFSTORE key name strings must only contain the following characters: +- [a-zA-Z0-9.] +- '-' + +##### REQ-3.1.04-SKVS-HLAPID-KVS: Key Name Path Directory Entry List {} +Path Directory Entries may have list indicators designated by {}. For +example, + - 'com.arm.mbed.wifi.accesspoint{5}.essid' + - 'com.arm.mbed.wifi.accesspoint{home}.essid' + - 'yotta.hello-world.animal{dog}{foot}{3}' +In the above the list item specifiers are respectively: + - '5' + - 'home' + - 'dog', 'foot, '3' +As list item specifiers are part of the key name string, the list item +substring must be composed of allowable characters. + +##### REQ-3.1.05-SKVS-HLAPID-KVS: CFSTORE Global Key Yotta Namespace +The key name prefix 'yotta' is reserved for use by the yotta module. Other +prefixes may be reserved. + +##### REQ-3.1.06-SKVS-HLAPID-KVS: CFSTORE Key Names Mapable to Common OS Filesystem Name +The CFSTORE key names must be mappable to common OS Filesystem file names. This requirment +permits a CFSTORE backend to map individual KVs to files with the filename being the +key name, for example. Supported OS Filesytems must include DOS, NTFS, EXT3, EXT4 and +JFFS. + + +## Secure Key Value Storage\High Level API Description\Access Control Security (REQ-3.2.xx-SKVS-HLAPID-ACS) Requirements. + +##### REQ-3.2.01-SKVS-HLAPID-KACS: +The CFSTORE must enforce security policies as defined by Access Control Lists. + +##### REQ-3.2.02-SKVS-HLAPID-KACS: CFSTORE Key Creation Part 2 +The CFSTORE objects must be created with an ACL. The ACL is attached to the object +so that access permissions to the KV data can be enforced. + +##### REQ-3.2.03-SKVS-HLAPID-KACS: +The CFSTORE Access Control Lists must support the groups for 'owner' and 'other', +with optional permissions read, write and executable. The owner group +permissions describe the access permissions of the owner of the object. The +other group permissions describe the access permissions for entities other +than the owner. The writable and executable permissions are mutually +exclusive. + +##### REQ-3.2.04-SKVS-HLAPID-KACS: +A CFSTORE API client must be able to query the CFSTORE for a list of KV pairs provided +the KV pair ACL permissions allow the client access. The query result must +contain all client owner KVs. The query results may include non-client +owned KVs provided the other group permissions grant access. + + +## Secure Key Value Storage\API Logic\Finding Keys (REQ-5.1.xx-SKVS-APIL-FK) Requirements. + +##### REQ-5.1.01-SKVS-APIL-FK: Key Searching/Finding Scoped by ACL +The CFSTORE must provide an interface to query for active keys in the global +storage. The query must: +- return results based on the ACL provided by the client +- support wild card searches using the '*' character. The '*' character + can occur at most once any point in the search string. For example: + - com.arm.mbed.wifi.accesspoint*.essid + - yotta.your-yotta-registry-module-name.* + - yotta.hello-world.animal{dog}{foot}{*} + - yotta.hello-world.animal{dog}{foot}* + - yotta.hello-world.animal{dog*3} + +##### REQ-5.1.02-SKVS-APIL-FK: CFSTORE Global Key Namespace Reserves Character '*' +The character '*' is reserved in the CFSTORE global key namespace. The current +functions of this character as follows: +- a wild card character in searches. +- a wild card character used in the delete operation. + + +##### REQ-5.1.03-SKVS-APIL-FK: Key Searching/Finding Resume +In order to support the return of a large list of key query results (perhaps +exceeding the ability of the caller to consume in a single operation), the +query interface must support the ability to restart/resume the query to +retrieve a subsequent set of records to those already received. + +##### REQ-5.1.04-SKVS-APIL-FK: Key Searching/Finding Internals (key versions) +The CFSTORE must be robust against incomplete, corrupted or aborted write +operations to NV store caused for example, by loss of power during the +write. + + +## Secure Key Value Storage\API Logic\Get Storage Information (REQ-5.2.xx-SKVS-APIL-GSI) Requirements. + +##### REQ-5.2.01-SKVS-APIL-GSI: storage_detect +The CFSTORE must provide an API so that clients can discover the CFSTORE storage +capabilities. Storage capabilities may include: +- write-block sizes for O_BLOCK_WRITE mode (sequence of writes into the + same value) +- supported Data Retention Levels +- supported Device Data Security Protection Features + +##### REQ-5.2.02-SKVS-APIL-GSI: Minimal Storage Support +The CFSTORE must provide minimal storage media services including the following: +- SRAM/SDRAM memory with no security guarantees. + + +## Secure Key Value Storage\API Logic\Creating & Opening Keys for Writing (REQ-5.3.xx-SKVS-APIL-COKFW) Requirements. + +##### REQ-5.3.01-SKVS-APIL-COKFW: storage_key_create with O_CREATE +CFSTORE keys must be explicitly created using 'storage_key_create' with the following +parameters: +- security ACLs (owner & others) +- the intended retention levels (bit mask to allow caching if needed) +- indicating the expected Device Data Security Protection Features +- the key pointer (zero-terminated) +- the value size +- alignment bits of the value hardware address (only for O_CONTINUOUS). + The structure start is aligned accordingly to ensure that the value + blob is aligned on a multiple of the 2^alignment_bits +- mode flags (O_CREATE, O_CONTINUOUS, O_LAZY_FLUSH, O_BLOCK_WRITE, + O_ALLOCATE_AT_OFFEST). + - O_CREATE. The call will create the KV pair. If a + pre-existing KV with the same name is present in CFSTORE then the + storage_key_create will fail with FILE_EXISTS. + - O_CONTINUOUS. The KV value will be stored in a continuous range + of hardware addresses. + - O_LAZY_FLUSH + - O_BLOCK_WRITE + - O_ALLOCATE_AT_OFFEST + +###### Discussion +The following should not be included in the API (e.g. because the principle of encapsulation is broken, or specific to a particular clients requirements): +- optionally the offset address (restricted feature, ideally only granted + to the FOTA security context) + +##### REQ-5.3.02-SKVS-APIL-COKFW: storage_key_create without O_CREATE +Pre-existing CFSTORE objects can be updated by calling the storage_key_create +API with the O_CREATE not set. +In case a pre-existing key is updated (O_CREATE not set) and the previous +ACL allows writing to the caller: +- all key-value fragments of the previous key are set as inactive +- the new key is allocated (in fragments if permitted) +- all permissions and settings are copied from the previous key +- the version number is incremented compared to the previous key (see + REQ-5.3.05-SKVS-APIL-COKFW). + +##### REQ-5.3.03-SKVS-APIL-COKFW: O_CONTINUOUS for executable objects +CFSTORE will manage an executable KV as though the mode O_CONTINUOUS flag +is set. + +##### REQ-5.3.04-SKVS-APIL-COKFW: O_CONTINUOUS for non-executable objects +A CFSTORE client may specify the mode O_CONTINUOUS flag for non-executable +objects. + +##### REQ-5.3.05-SKVS-APIL-COKFW: Versioning of KVs +KVs in NV storage should have version numbers. When writing an updated +KV (with changed value data for example), a new copy of the KV data with +updated version number should be written to NV store. The earlier version is +left as a fallback copy of data that may be deleted at some point in time, +when the NV storage is required, or more than a certain number of +versions is exceeded. + +## Secure Key Value Storage\Updating and Settings and Permissions (REQ-6.1.xx-SKVS-USP) Requirements. + +##### REQ-6.1.01-SKVS-USP: +CFSTORE does not permit the updating of KV pair permissions or settings. This is to promote security. + + +## Secure Key Value Storage\Updating and Settings and Permissions\Deleting Keys (REQ-6.2.xx-SKVS-USP-DK) Requirements. + +##### REQ-6.2.01-SKVS-USP-DK: +Only the owner of the CFSTORE KV pair can delete the object. The wildcard +'*' character can be specified to delete an owned subtree of the CFSTORE +global key namespace. + +##### REQ-6.2.02-SKVS-USP-DK: +This requirement has been removed. + +## Secure Key Value Storage\Updating and Settings and Permissions\Opening Keys for Reading (REQ-6.2.xx-SKVS-USP-OKFR) Requirements. + +##### REQ-6.3.xx-SKVS-USP-OKFR storage_key_open_read +CFSTORE objects must be explicitly opened with storage_key_open_read(key_name) +before operations on the KV pair can be performed. The KV must +pre-exist in the store before it can be opened. + + +## Secure Key Value Storage\Updating and Settings and Permissions\Seeking in Key Values (REQ-6.3.xx-SKVS-USP-SIKV) Requirements. + +##### REQ-6.4.01-SKVS-USP-SIKV storage_value_rseek +The function storage_value_rseek can be used on a opaque reference to a KV +to change the read position inside a value i.e. the storage_value_read +method supports random-access. + +##### REQ-6.4.02-SKVS-USP-SIKV storage_value_write has no write location +storage_value_write does not support the concept of a write location that can be modified i.e. random access write support is not supported. + +##### REQ-6.4.03-SKVS-USP-SIKV storage_value_write sequential-access +storage_value_write supports sequential-access. Random-access to the value data is not supported. + + +## Secure Key Value Storage\Updating and Settings and Permissions\Writing Keys (REQ-6.4.xx-SKVS-USP-WK) Requirements. + +##### REQ-6.5.01-SKVS-USP-WK +CFSTORE KV values can be written in one or more operation. For example, if a value blob has size n bytes, then 2 n/2 byte write operations have the following effect: +- the first write sets bytes 0-n/2-1 in the value blob. +- the second write sets bytes n/2 to n-1 in the value blob. +This may require a write position to be maintained, for example. The ability to seek the write location must not be supported. + +##### REQ-6.5.02-SKVS-USP-WK +This requirement has been removed. + +##### REQ-6.5.03-SKVS-USP-WK +The KV stored in NV store should have an associated CRC so that: +- the integrity of the data can be protected +- the corruption of the data can be detected e.g. if power is lost during the flush to NV store. +The CRC should be stored at the end of the KV so it is written last. The CRC write transaction is termed "finalising" the KV. + +##### REQ-6.5.04-SKVS-USP-WK +KVs that have not been flushed to NV storage should be flushed at reboot time. + +##### REQ-6.5.05-SKVS-USP-WK +KVs may be fragmented into several smaller pieces for NV storage. In the case that a KV is fragmented, each fragment should have a an associated CRC as per REQ-6.5.03-SKVS-USP-WK. + +##### REQ-6.5.06-SKVS-USP-WK +A KV value or KV fragment can only be finalised by its owner. + +##### REQ-6.5.07-SKVS-USP-WK +Closing of a written KV in the finalization of all open value-fragment for that KV. + +##### REQ-6.5.08-SKVS-USP-WK +Flushing of the KVs causes finalisation of the store. + +##### REQ-6.5.09-SKVS-USP-WK +Non-finalised object must not be readable. + + +## Secure Key Value Storage\Updating and Settings and Permissions\Executable Keys and Firmware Updates (REQ-6.6.xx-SKVS-USP-EKFU) +Requirements. + +##### REQ-6.6.01-SKVS-USP-EKFU +To facilitate modular firmware updates in future, executable keys are supported by the API + +##### REQ-6.6.02-SKVS-USP-EKFU +For immovable & O_CONTINUOUS keys, the absolute hardware address and the relative address of a value key can be queried using the API +This API feature is restricted to boxes that require that function (FOTA, DMA). By keeping most key-value pairs movable the flash can be de-fragmented. + + +## Secure Key Value Storage\Miscellaneous (REQ-7.1.xx-SKVS-M)Requirements. + +##### REQ-7.1.01-SKVS-M +The CFSTORE will implement a C Language interface. + +##### REQ-7.1.02-SKVS-M +The CFSTORE does not support hot-pluggable storage devices e.g. SD flash. + +##### REQ-7.1.03-SKVS-M KV Value Data May Not Fit into Available (SRAM) Memory. +CFSTORE must be capable of writing a KV to NV backing store where the value +data length >> size of available SRAM. This requirement therefore implies +that CFSTORE must support the case that the whole of the KV value data cannot all be +resident in SRAM memory at one time. + + +# Outstanding Issues With This Document + +##### REQ-5.3.01-SKVS-APIL-COKFW: storage_key_create with O_CREATE +- How does the offset-address work? +- What is the definition of O_LAZY_FLUSH +- What is the definition of O_BLOCK_WRITE +- What is the definition of O_ALLOCATE_AT_OFFEST + +##### REQ-5.3.02-SKVS-APIL-COKFW: storage_key_create without O_CREATE +- To which "previous ACL" does this requirement refer? +- Is an implementation internally creating a new copy of the key? + +##### REQ-6.5.01-SKVS-USP-WK +- Are there any additional requirements arising from these statements? + + +# Contributors + +This document was made possible through the contributions of the following people: +- Rohit Grover +- Simon Hughes +- Milosch Meriac + + +[CFSTORE_TERM]: https://github.com/ARMmbed/configuration-store/blob/master/doc/design/configuration_store_terminology.md diff --git a/storage/cfstore/doc/design/configuration_store_terminology.md b/storage/cfstore/doc/design/configuration_store_terminology.md new file mode 100644 index 0000000000..91526db06f --- /dev/null +++ b/storage/cfstore/doc/design/configuration_store_terminology.md @@ -0,0 +1,34 @@ +# Configuration-Store Terminology + +## Defintion of Terms + +###### ACL +Access Control List + +###### CFSTORE +Configuration Store + +###### FOTA +Firmware Over The Air + +###### HLD +High Level Design + +###### KV +Key Value Pair, also represented as {key, value} + +###### Key Name Prefix +This is indentical tot he Security Name Prefix. + +###### LLD +Low Level Design + +###### NV +Non Volatile + +###### OS +Operating System + +###### Security Name Prefix +If a client security context (uvisor box) needs to create CFSTORE KV entries, it must have a (mandatory) key name prefix. This is called the security_prefix_name. + diff --git a/storage/cfstore/doc/design/configuration_store_test_plan.md b/storage/cfstore/doc/design/configuration_store_test_plan.md new file mode 100644 index 0000000000..166b1138de --- /dev/null +++ b/storage/cfstore/doc/design/configuration_store_test_plan.md @@ -0,0 +1,64 @@ +# Test Plan + +This document describes the test plan and test cases for the configuration-store project. + +# x86 Testing + +The x86-linux-native target is supported and the tests can be run on a linux PC, for example. + +# FRDM-K64F-GCC Testing + +The frdm-k64f-gcc target is supported and the tests can be run on the k64f target. + +# Overview of Test Cases + +## Access Control List() API Tests + +Currently, the ACL tests are not implemented as the uvisor integration is still outstanding. + +## Close() API Tests + +These test cases are to be documented. + +## Add and Delete() API Tests + +These test cases are to be documented. + + +## Create() API Tests + +These test cases are to be documented. + + +## Find() API Tests + +These test cases are to be documented. + + +## Flush() API Tests + +These test cases are to be documented. + + +## Misc API Tests + +These test cases are to be documented. + + +## Open() API Tests + +These test cases are to be documented. + + +## Read() API Tests + +These test cases are to be documented. + + +## Write() API Tests + +These test cases are to be documented. + + + + diff --git a/storage/cfstore/doc/design/pics/cfstore_hld_seqdiag_find_full_part_async.png b/storage/cfstore/doc/design/pics/cfstore_hld_seqdiag_find_full_part_async.png new file mode 100644 index 0000000000..d8c48e6ab3 Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstore_hld_seqdiag_find_full_part_async.png differ diff --git a/storage/cfstore/doc/design/pics/cfstore_hld_seqdiag_find_full_part_sync.png b/storage/cfstore/doc/design/pics/cfstore_hld_seqdiag_find_full_part_sync.png new file mode 100644 index 0000000000..003dbeaeae Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstore_hld_seqdiag_find_full_part_sync.png differ diff --git a/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_create_async.png b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_create_async.png new file mode 100644 index 0000000000..9304b299ab Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_create_async.png differ diff --git a/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_create_sync.png b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_create_sync.png new file mode 100644 index 0000000000..453ea9ee4e Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_create_sync.png differ diff --git a/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_find_full_async.png b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_find_full_async.png new file mode 100644 index 0000000000..99e6922b5e Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_find_full_async.png differ diff --git a/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_find_full_sync.png b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_find_full_sync.png new file mode 100644 index 0000000000..24676bbbc8 Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_find_full_sync.png differ diff --git a/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_find_part_async.png b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_find_part_async.png new file mode 100644 index 0000000000..adaa5e03ec Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_find_part_async.png differ diff --git a/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_find_part_sync.png b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_find_part_sync.png new file mode 100644 index 0000000000..efc1cb8b7a Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_find_part_sync.png differ diff --git a/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_getversion_getcapabilities_sync.png b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_getversion_getcapabilities_sync.png new file mode 100644 index 0000000000..3fe37146c5 Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_getversion_getcapabilities_sync.png differ diff --git a/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_init_uninit_async.png b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_init_uninit_async.png new file mode 100644 index 0000000000..510fee95fe Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_init_uninit_async.png differ diff --git a/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_init_uninit_sync.png b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_init_uninit_sync.png new file mode 100644 index 0000000000..114b173208 Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_init_uninit_sync.png differ diff --git a/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_open_read_async.png b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_open_read_async.png new file mode 100644 index 0000000000..4628817f9f Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_open_read_async.png differ diff --git a/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_open_read_sync.png b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_open_read_sync.png new file mode 100644 index 0000000000..2f87b36425 Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_open_read_sync.png differ diff --git a/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_open_write_async.png b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_open_write_async.png new file mode 100644 index 0000000000..d217f83763 Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_open_write_async.png differ diff --git a/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_open_write_sync.png b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_open_write_sync.png new file mode 100644 index 0000000000..a7049f9306 Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_open_write_sync.png differ diff --git a/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_power_control_sync.png b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_power_control_sync.png new file mode 100644 index 0000000000..488e7c47e4 Binary files /dev/null and b/storage/cfstore/doc/design/pics/cfstrore_hld_seqdiag_power_control_sync.png differ diff --git a/storage/cfstore/doc/design/pics/configuration_store_hld_api_summary.jpg b/storage/cfstore/doc/design/pics/configuration_store_hld_api_summary.jpg new file mode 100644 index 0000000000..f5067a4f2f Binary files /dev/null and b/storage/cfstore/doc/design/pics/configuration_store_hld_api_summary.jpg differ diff --git a/storage/cfstore/doc/design/umlet/configuartion_store_hld.uxf b/storage/cfstore/doc/design/umlet/configuartion_store_hld.uxf new file mode 100644 index 0000000000..1ab3401720 --- /dev/null +++ b/storage/cfstore/doc/design/umlet/configuartion_store_hld.uxf @@ -0,0 +1,363 @@ + + + 8 + + UMLInterface + + 0 + 872 + 784 + 312 + + ARM_DRIVER_CFSTORE +Config-Store (CS) Interface +-- +ARM_DRIVER_VERSION (*GetVersion)(void); +int32_t (*Close)(ARM_CFSTORE_HANDLE hkey); +int32_t (*Create)(const char* key_name, ARM_CFSTORE_SIZE value_len, const ARM_CFSTORE_KEYDESC* kdesc, ARM_CFSTORE_HANDLE hkey); +int32_t (*Delete)(ARM_CFSTORE_HANDLE hkey); +int32_t (*Find)(const char* key_name_query, const ARM_CFSTORE_HANDLE previous, ARM_CFSTORE_HANDLE next); +int32_t (*Flush)(void); +int32_t (*GetKeyName)(ARM_CFSTORE_HANDLE hkey, char* key_name, uint8_t *key_len); +ARM_CFSTORE_CAPABILITIES (*GetCapabilities)(void); +ARM_CFSTORE_STATUS (*GetStatus)(void); +int32_t (*GetValueLen)(ARM_CFSTORE_HANDLE hkey, ARM_CFSTORE_SIZE *value_len); +int32_t (*Initialize)(ARM_CFSTORE_CALLBACK callback, void* client_context); +int32_t (*PowerControl)(ARM_POWER_STATE state); +int32_t (*Read)(ARM_CFSTORE_HANDLE hkey, void* data, ARM_CFSTORE_SIZE* len); +int32_t (*Open)(const char* key_name, ARM_CFSTORE_FMODE flags, ARM_CFSTORE_HANDLE hkey); +int32_t (*Rseek)(ARM_CFSTORE_HANDLE hkey, ARM_CFSTORE_OFFSET offset); +int32_t (*Uninitialise)(void); +int32_t (*Write)(ARM_CFSTORE_HANDLE hkey, const char* data, ARM_CFSTORE_SIZE* len); + + + + + + UMLClass + + 488 + 1168 + 192 + 112 + + ARM_CFSTORE_ACCESS_CONTROL_LIST +-- +uint32_t perm_owner_read : 1; +uint32_t perm_owner_write : 1; +uint32_t perm_owner_execute : 1; +uint32_t perm_other_read : 1; +uint32_t perm_other_write : 1; +uint32_t perm_other_execute : 1; + + + + + UMLClass + + 688 + 1168 + 264 + 144 + + ARM_STOR_DATA_RETENTION_LEVEL +-- +/* supported volatility values of different sotrage mediums */ +DATA_RETENTION_WHILE_DEVICE_ACTIVE, +DATA_RETENTION_ACROSS_SLEEP, +DATA_RETENTION_ACROSS_DEEP_SLEEP, +DATA_RETENTION_BATTERY_BACKED, +DATA_RETENTION_NVM, + + + + + UMLClass + + 1320 + 1168 + 184 + 136 + + ARM_CFSTORE_FMODE +-- +uint32_t continuous : 1; +uint32_t lazy_flush : 1; +uint32_t flush_on_close : 1; +uint32_t read : 1; +uint32_t write : 1; +uint32_t execute : 1; +uint32_t storage_detect : 1; + + + + + + Relation + + 536 + 1072 + 544 + 112 + + lt=<- + 10.0;120.0;660.0;10.0 + + + Relation + + 1056 + 1072 + 368 + 112 + + lt=<- + 440.0;120.0;10.0;10.0 + + + UMLClass + + 968 + 1168 + 344 + 256 + + ARM_STOR_SECURITY_FEATURES +-- +uint8_t acls : 1; ///< protection against internal software attacks using ACLs. +uint8_t rollback_protection : 1; ///< roll-back protection. +uint8_t tamper_proof : 1; ///< tamper-proof memory (will be deleted on tamper-attempts using board level or chip level sensors). +uint8_t internal_flash : 1; ///< Internal flash. +uint8_t reserved : 4; + +/** +* Encode support for hardening against various classes of attacks. +*/ +struct ARM_STOR_HARDENING +{ + uint8_t device_software : 1; ///< device software (malware running on the device). + uint8_t board_level_attacks : 1; ///< board level attacks (debug probes, copy protection fuses.) + uint8_t chip_level_attacks : 1; ///< chip level attacks (tamper-protection). + uint8_t side_channel_attacks : 1; ///< side channel attacks. + uint8_t reserved : 4; +} hardening_against; ///< hardening. + + + + + Relation + + 816 + 1072 + 264 + 112 + + lt=<- + 10.0;120.0;310.0;10.0 + + + Relation + + 1056 + 1072 + 24 + 112 + + lt=<- + 10.0;120.0;10.0;10.0 + + + UMLActor + + 1304 + 0 + 104 + 96 + + Box 1 +Security Context + + + + UMLActor + + 1408 + 0 + 104 + 96 + + Box i +Security Context + + + + UMLActor + + 1504 + 0 + 104 + 96 + + Box n +Security Context + + + + UMLUseCase + + 1376 + 160 + 184 + 96 + + Config Store +{key,value} storage operations + + + + + Relation + + 1360 + 48 + 128 + 128 + + <<uses>> + 10.0;10.0;140.0;140.0 + + + Relation + + 1464 + 48 + 56 + 128 + + <<uses>> + 10.0;10.0;10.0;140.0 + + + Relation + + 1464 + 48 + 120 + 128 + + <<uses>> + 130.0;10.0;10.0;140.0 + + + UMLNote + + 224 + 656 + 304 + 64 + + Note.. +mbed_config_store_hld +v0.04 +20160225 + + + + + + UMLClass + + 824 + 968 + 264 + 112 + + ARM_CFSTORE_KEYDESC +-- +/*key descriptor attributes */ +ARM_CFSTORE_ACCESS_CONTROL_LIST acl; +ARM_STOR_DATA_RETENTION_LEVEL drl; +ARM_STOR_SECURITY_FEATURES security; +ARM_CFSTORE_FMODE flags; + + + + + + + UMLClass + + 904 + 720 + 240 + 232 + + ARM_CFSTORE_OPCODE; +-- +CFSTORE_OPCODE_CLOSE +CFSTORE_OPCODE_CREATE +CFSTORE_OPCODE_DELETE +CFSTORE_OPCODE_FIND +CFSTORE_OPCODE_FLUSH +CFSTORE_OPCODE_GET_KEY_NAME +CFSTORE_OPCODE_GET_STATUS +CFSTORE_OPCODE_GET_VALUE_LEN +CFSTORE_OPCODE_INITIALIZE +CFSTORE_OPCODE_OPEN +CFSTORE_OPCODE_POWER_CONTROL +CFSTORE_OPCODE_READ +CFSTORE_OPCODE_RSEEK +CFSTORE_OPCODE_UNINITIALIZE +CFSTORE_OPCODE_WRITE +CFSTORE_OPCODE_MAX + + + + + UMLClass + + 576 + 608 + 808 + 64 + + ARM_CFSTORE_CALLBACK +-- +typedef void (*ARM_CFSTORE_CALLBACK)(int32_t status, ARM_CFSTORE_OPCODE cmd_code, void *client_context, ARM_CFSTORE_HANDLE handle); + + + + + Relation + + 392 + 880 + 576 + 104 + + lt=<- + 700.0;110.0;10.0;10.0 + + + Relation + + 392 + 664 + 536 + 240 + + lt=<- + 650.0;10.0;10.0;280.0 + + + Relation + + 392 + 816 + 528 + 88 + + lt=<- + 640.0;10.0;10.0;90.0 + + diff --git a/storage/cfstore/doc/project/configuration_store_releases.md b/storage/cfstore/doc/project/configuration_store_releases.md new file mode 100644 index 0000000000..903b183646 --- /dev/null +++ b/storage/cfstore/doc/project/configuration_store_releases.md @@ -0,0 +1,68 @@ +# Configuration Store Releases +Author: Simon Hughes + +# Overview + +This page documents the supported requirements in each release. The Configuration Store Requirements are documented here: + +[CFSTORE_REQUIREMENTS](https://github.com/ARMmbed/meVo/blob/master/config_store/doc/design/configuration_store_requirements.md) + + +# Releases + +## Release 0.3.2 + +The requirements status (supported versus not supported) in this release are as follows: + +- REQ-1.01-SKVS-R: Not supported. Currently only SRAM back CFSTORE is supported. +- REQ-1.02-SKVS-R: Not supported. uvisor integration is still outstanding. +- REQ-1.03-SKVS-R: Supported. +- REQ-1.04-SKVS-R: Supported. +- REQ-1.05-SKVS-R: Supported. +- REQ-1.06-SKVS-R: Supported. +- REQ-1.07-SKVS-R: CS Stores Binary Blobs: Supported. +- REQ-1.08-SKVS-R: CS Key Structure as Name Strings: Supported. +- REQ-1.09-SKVS-R: Key Creation and Ownership Part 1: Not supported. uvisor integration is still outstanding. +- REQ-1.10-SKVS-R: Security Context: Not supported. uvisor integration is still outstanding. +- REQ-1.2.01-SKVS-HLD: Not supported. Currently only SRAM back CFSTORE is supported. +- REQ-1.2.02-SKVS-HLD: "only during device activity" supported but other options not supported. +- REQ-1.2.03-SKVS-HLD: Not supported. +- REQ-1.2.04-SKVS-HLD: Not supported. +- REQ-1.2.05-SKVS-HLD: Supported. +- REQ-3.1.01-SKVS-HLAPID-KVS: CS Global Key Namespace: Supported. +- REQ-3.1.02-SKVS-HLAPID-KVS: CS Key Name String Format Max Length: Supported. +- REQ-3.1.03-SKVS-HLAPID-KVS: CS Key Name String Allowed Characters: Supported. +- REQ-3.1.04-SKVS-HLAPID-KVS: Key Name Path Directory Entry List []: Supported. +- REQ-3.1.05-SKVS-HLAPID-KVS: CS Global Key Yotta Namespace: Supported. +- REQ-3.2.01-SKVS-HLAPID-KACS: Not supported as requires uvisor integration. +- REQ-3.2.02-SKVS-HLAPID-KACS: CS Key Creation Part 2: Supported. +- REQ-3.2.03-SKVS-HLAPID-KACS: Partially supported as requires uvisor integration. +- REQ-3.2.04-SKVS-HLAPID-KACS: Supported. +- REQ-5.1.01-SKVS-APIL-FK: Key Searching/Finding Scoped by ACL: Supported. +- REQ-5.1.02-SKVS-APIL-FK: CS Global Key Namespace Reserves Character '*': Supported. +- REQ-5.1.03-SKVS-APIL-FK: Key Searching/Finding Resume: Supported. +- REQ-5.1.04-SKVS-APIL-FK: Key Searching/Finding Internals (key versions): Not supported. +- REQ-5.2.01-SKVS-APIL-GSI: storage_detect: Not supported. +- REQ-5.2.02-SKVS-APIL-GSI: Minimal Storage Support: Supported. +- REQ-5.3.01-SKVS-APIL-COKFW: storage_key_create with O_CREATE: Partially suppported. +- REQ-5.3.02-SKVS-APIL-COKFW: storage_key_create without O_CREATE: Not supported. +- REQ-5.3.03-SKVS-APIL-COKFW: O_CONTINUOUS for executable objects: Not supported. +- REQ-5.3.04-SKVS-APIL-COKFW: O_CONTINUOUS for non-executable objects: Not supported. +- REQ-6.1.01-SKVS-USP: Supported. +- REQ-6.2.01-SKVS-USP-DK: Supported. +- REQ-6.2.02-SKVS-USP-DK: Supported. +- REQ-6.3.01-SKVS-USP-OKFR storage_key_open_read: Supported. +- REQ-6.4.01-SKVS-USP-SIKV storage_value_rseek: Supported. +- REQ-6.4.02-SKVS-USP-SIKV storage_value_write has no write location: Supported. +- REQ-6.4.03-SKVS-USP-SIKV storage_value_write sequential-access: Supported. +- REQ-6.5.01-SKVS-USP-WK: Partially supported. +- REQ-6.5.01-SKVS-USP-WK: Not supported. +- REQ-6.6.01-SKVS-USP-EKFU: Not supported. +- REQ-6.6.02-SKVS-USP-EKFU: Not supported. +- REQ-7.1.01-SKVS-M: Supported. +- REQ-7.1.02-SKVS-M: Supported. +- REQ-7.1.03-SKVS-M KV Value Data May Not Fit into Available (SRAM) Memory. Not supported. + + + + diff --git a/storage/cfstore/source/cfstore_config.h b/storage/cfstore/source/cfstore_config.h new file mode 100644 index 0000000000..e30e54600f --- /dev/null +++ b/storage/cfstore/source/cfstore_config.h @@ -0,0 +1,60 @@ +/** @file cfstore_debug.h + * + * component debug header file. + */ + + +#ifndef __CFSTORE_CONFIG_H +#define __CFSTORE_CONFIG_H + +/* + * CFSTORE_CONFIG_BACKEND_FLASH_ENABLED + * = 1 >1 build with the flash + * CFSTORE_CONFIG_BACKEND_SRAM_ENABLED + * CFSTORE_CONFIG_BACKEND_UVISOR_ENABLED + * CFSTORE_CONFIG_MBED_OS_VERSION + * 3 => mbedosV3 + * 4 => morpheus + */ + +/* default values */ +#define CFSTORE_CONFIG_BACKEND_FLASH_ENABLED 1 +#define CFSTORE_CONFIG_BACKEND_SRAM_ENABLED 0 +#define CFSTORE_CONFIG_BACKEND_UVISOR_ENABLED 0 +#define CFSTORE_CONFIG_MBED_OS_VERSION 3 + +/* default build config overridden by package manager configuarion + * + * __MBED__ + * Morpheus build system (mbed-classic) defines the __MBED__ symbol + * + * YOTTA_CFG_CFSTORE_BACKEND_SRAM + * build only for sram backend (no flash integration) + * YOTTA_CFG_CFSTORE_UVISOR_ENABLE + * build with uvisor enable + * + * */ + +#ifdef __MBED__ +#undef CFSTORE_CONFIG_MBED_OS_VERSION +#define CFSTORE_CONFIG_MBED_OS_VERSION 4 +/* currently build only sram version (port flash-journal later) */ + +#define YOTTA_CFG_CFSTORE_BACKEND_SRAM + +/* define the symbol that yotta would define for the k64f target */ +#define TARGET_LIKE_FRDM_K64F_GCC + +/* at present time building for sram so set yotta symbol for sync mode i.e. async_ops = 0*/ +#define YOTTA_CFG_CONFIG_HARDWARE_MTD_ASYNC_OPS 0 +#endif /* __MBED__ */ + +#if defined YOTTA_CFG_CFSTORE_BACKEND_SRAM +#undef CFSTORE_CONFIG_BACKEND_FLASH_ENABLED +#define CFSTORE_CONFIG_BACKEND_FLASH_ENABLED 0 +#undef CFSTORE_CONFIG_BACKEND_SRAM_ENABLED +#define CFSTORE_CONFIG_BACKEND_SRAM_ENABLED 1 +#endif /* YOTTA_CFG_CFSTORE_BACKEND_SRAM */ + + +#endif /*__CFSTORE_CONFIG_H*/ diff --git a/storage/cfstore/source/cfstore_debug.h b/storage/cfstore/source/cfstore_debug.h new file mode 100644 index 0000000000..dcedc3dd2d --- /dev/null +++ b/storage/cfstore/source/cfstore_debug.h @@ -0,0 +1,118 @@ +/** @file cfstore_debug.h + * + * component debug header file. + */ + + +#ifndef __CFSTORE_DEBUG +#define __CFSTORE_DEBUG + +#include +#include + + +/* Debug Support */ + +#define CFSTORE_LOG_NONE 0 +#define CFSTORE_LOG_ERR 1 +#define CFSTORE_LOG_WARN 2 +#define CFSTORE_LOG_NOTICE 3 +#define CFSTORE_LOG_INFO 4 +#define CFSTORE_LOG_DEBUG 5 +#define CFSTORE_LOG_FENTRY 6 + +#define CFSTORE_LOG(_fmt, ...) \ + do \ + { \ + printf(_fmt, __VA_ARGS__); \ + }while(0); + +#define noCFSTORE_DEBUG +#ifdef CFSTORE_DEBUG + +extern uint32_t cfstore_optDebug_g; +extern uint32_t cfstore_optLogLevel_g; +extern uint32_t cfstore_optLogTracepoint_g; + + +/* uncomment for asserts to work */ +/* #undef NDEBUG */ +// todo: port to mbedOSV3++ #include + +#define CFSTORE_INLINE +// todo: port to mbedOSV3++ #define CFSTORE_ASSERT CORE_UTIL_ASSERT +#define CFSTORE_ASSERT(...) + +#define CFSTORE_DBGLOG(_fmt, ...) \ + do \ + { \ + if(cfstore_optDebug_g && (cfstore_optLogLevel_g >= CFSTORE_LOG_DEBUG)) \ + { \ + printf(_fmt, __VA_ARGS__); \ + } \ + }while(0); + + +#define CFSTORE_ERRLOG(_fmt, ...) \ + do \ + { \ + if(cfstore_optDebug_g && (cfstore_optLogLevel_g >= CFSTORE_LOG_ERR)) \ + { \ + printf(_fmt, __VA_ARGS__); \ + } \ + }while(0); + + +#define CFSTORE_FENTRYLOG(_fmt, ...) \ + do \ + { \ + if(cfstore_optDebug_g && (cfstore_optLogLevel_g >= CFSTORE_LOG_FENTRY)) \ + { \ + printf(_fmt, __VA_ARGS__); \ + } \ + }while(0); + + +/* tracepoints */ +#define CFSTORE_TP_NONE 0x0 +#define CFSTORE_TP_CLOSE (1<<0) +#define CFSTORE_TP_CREATE (1<<1) +#define CFSTORE_TP_DELETE (1<<2) +#define CFSTORE_TP_FILE (1<<3) +#define CFSTORE_TP_FIND (1<<4) +#define CFSTORE_TP_FLUSH (1<<5) +#define CFSTORE_TP_FSM (1<<6) +#define CFSTORE_TP_INIT (1<<7) +#define CFSTORE_TP_MEM (1<<8) +#define CFSTORE_TP_OPEN (1<<9) +#define CFSTORE_TP_READ (1<<10) +#define CFSTORE_TP_WRITE (1<<11) +#define CFSTORE_TP_VERBOSE1 (1<<12) +#define CFSTORE_TP_VERBOSE2 (1<<13) +#define CFSTORE_TP_VERBOSE3 (1<<14) +#define CFSTORE_TP_FENTRY (1<<31) + +#define CFSTORE_TP(_tp, _fmt, ...) \ +do \ +{ \ + if(cfstore_optDebug_g && (cfstore_optLogLevel_g >= CFSTORE_LOG_DEBUG)) \ + { \ + if((cfstore_optLogTracepoint_g & (_tp)) == (uint32_t)(_tp)) \ + { \ + printf(_fmt, __VA_ARGS__); \ + } \ + } \ +}while(0); + + +#else +#define CFSTORE_ASSERT(_x) do { } while(0) +#define CFSTORE_INLINE inline +#define CFSTORE_DBGLOG(_fmt, ...) do { } while(0) +#define CFSTORE_ERRLOG(_fmt, ...) do { printf(_fmt, __VA_ARGS__); } while(0) +#define CFSTORE_FENTRYLOG(_fmt, ...) do { } while(0) +#define CFSTORE_TP(_tp, _fmt, ...) do { } while(0) +#endif /* CFSTORE_DEBUG */ + + +#endif /*__CFSTORE_DEBUG*/ diff --git a/storage/cfstore/source/cfstore_fnmatch.c b/storage/cfstore/source/cfstore_fnmatch.c new file mode 100644 index 0000000000..8ef6ae085f --- /dev/null +++ b/storage/cfstore/source/cfstore_fnmatch.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _NO_FNMATCH + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 4/16/94"; +#endif /* LIBC_SCCS and not lint */ + +/* In order to support ARM toolchain, this have been removed from the original + * fnmatch.c from newlib. + * #include + */ + +/* + * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. + * Compares a filename or pathname to a pattern. + */ + +#include +#include +#include +#include +#include + +#define EOS '\0' + +#define RANGE_MATCH 1 +#define RANGE_NOMATCH 0 +#define RANGE_ERROR (-1) + + +/* In order to support ARM toolchain and simplify the number of newlib posix files used, + * this have been copied from collate.c, and the license for this code has been included at the + * here: + * + * Copyright (c) 1995 Alex Tatmanjants + * at Electronni Visti IA, Kiev, Ukraine. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +int __collate_load_error = 1; + +/* In order to support ARM toolchain and simplify the number of newlib posix files used, + * the following has been copied from collcmp.c, and the license for this code is + * included here: + * + * Copyright (C) 1996 by Andrey A. Chernov, Moscow, Russia. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Compare two characters converting collate information + * into ASCII-compatible range, it allows to handle + * "[a-z]"-type ranges with national characters. + */ + +int __collate_range_cmp (c1, c2) + int c1, c2; +{ + static char s1[2], s2[2]; + int ret; + + c1 &= UCHAR_MAX; + c2 &= UCHAR_MAX; + if (c1 == c2) + return (0); + + s1[0] = c1; + s2[0] = c2; + if ((ret = strcoll(s1, s2)) != 0) + return (ret); + return (c1 - c2); +} + + +static int rangematch(const char *, char, int, char **); + +int +fnmatch(pattern, string, flags) + const char *pattern, *string; + int flags; +{ + const char *stringstart; + char *newp; + char c, test; + + for (stringstart = string;;) + switch (c = *pattern++) { + case EOS: + if ((flags & FNM_LEADING_DIR) && *string == '/') + return (0); + return (*string == EOS ? 0 : FNM_NOMATCH); + case '?': + if (*string == EOS) + return (FNM_NOMATCH); + if (*string == '/' && (flags & FNM_PATHNAME)) + return (FNM_NOMATCH); + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + ++string; + break; + case '*': + c = *pattern; + /* Collapse multiple stars. */ + while (c == '*') + c = *++pattern; + + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + + /* Optimize for pattern with * at end or before /. */ + if (c == EOS) + if (flags & FNM_PATHNAME) + return ((flags & FNM_LEADING_DIR) || + strchr(string, '/') == NULL ? + 0 : FNM_NOMATCH); + else + return (0); + else if (c == '/' && flags & FNM_PATHNAME) { + if ((string = strchr(string, '/')) == NULL) + return (FNM_NOMATCH); + break; + } + + /* General case, use recursion. */ + while ((test = *string) != EOS) { + if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) + return (0); + if (test == '/' && flags & FNM_PATHNAME) + break; + ++string; + } + return (FNM_NOMATCH); + case '[': + if (*string == EOS) + return (FNM_NOMATCH); + if (*string == '/' && (flags & FNM_PATHNAME)) + return (FNM_NOMATCH); + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + + switch (rangematch(pattern, *string, flags, &newp)) { + case RANGE_ERROR: + goto norm; + case RANGE_MATCH: + pattern = newp; + break; + case RANGE_NOMATCH: + return (FNM_NOMATCH); + } + ++string; + break; + case '\\': + if (!(flags & FNM_NOESCAPE)) { + if ((c = *pattern++) == EOS) { + c = '\\'; + --pattern; + } + } + /* FALLTHROUGH */ + default: + norm: + if (c == *string) + ; + else if ((flags & FNM_CASEFOLD) && + (tolower((unsigned char)c) == + tolower((unsigned char)*string))) + ; + else + return (FNM_NOMATCH); + string++; + break; + } + /* NOTREACHED */ +} + +static int +rangematch(pattern, test, flags, newp) + const char *pattern; + char test; + int flags; + char **newp; +{ + int negate, ok; + char c, c2; + + /* + * A bracket expression starting with an unquoted circumflex + * character produces unspecified results (IEEE 1003.2-1992, + * 3.13.2). This implementation treats it like '!', for + * consistency with the regular expression syntax. + * J.T. Conklin (conklin@ngai.kaleida.com) + */ + if ( (negate = (*pattern == '!' || *pattern == '^')) ) + ++pattern; + + if (flags & FNM_CASEFOLD) + test = tolower((unsigned char)test); + + /* + * A right bracket shall lose its special meaning and represent + * itself in a bracket expression if it occurs first in the list. + * -- POSIX.2 2.8.3.2 + */ + ok = 0; + c = *pattern++; + do { + if (c == '\\' && !(flags & FNM_NOESCAPE)) + c = *pattern++; + if (c == EOS) + return (RANGE_ERROR); + + if (c == '/' && (flags & FNM_PATHNAME)) + return (RANGE_NOMATCH); + + if (flags & FNM_CASEFOLD) + c = tolower((unsigned char)c); + + if (*pattern == '-' + && (c2 = *(pattern+1)) != EOS && c2 != ']') { + pattern += 2; + if (c2 == '\\' && !(flags & FNM_NOESCAPE)) + c2 = *pattern++; + if (c2 == EOS) + return (RANGE_ERROR); + + if (flags & FNM_CASEFOLD) + c2 = tolower((unsigned char)c2); + + if (__collate_load_error ? + c <= test && test <= c2 : + __collate_range_cmp(c, test) <= 0 + && __collate_range_cmp(test, c2) <= 0 + ) + ok = 1; + } else if (c == test) + ok = 1; + } while ((c = *pattern++) != ']'); + + *newp = (char *)pattern; + return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH); +} + +#endif /* !_NO_FNMATCH */ diff --git a/storage/cfstore/source/cfstore_fnmatch.h b/storage/cfstore/source/cfstore_fnmatch.h new file mode 100644 index 0000000000..afe65f346c --- /dev/null +++ b/storage/cfstore/source/cfstore_fnmatch.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/include/fnmatch.h,v 1.10 2002/03/23 17:24:53 imp Exp $ + * @(#)fnmatch.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _FNMATCH_H_ +#define _FNMATCH_H_ + +#define FNM_NOMATCH 1 /* Match failed. */ + +#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */ +#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */ +#define FNM_PERIOD 0x04 /* Period must be matched by period. */ + +#if defined(_GNU_SOURCE) || !defined(_ANSI_SOURCE) && !defined(_POSIX_SOURCE) +#define FNM_LEADING_DIR 0x08 /* Ignore / after Imatch. */ +#define FNM_CASEFOLD 0x10 /* Case insensitive search. */ +#define FNM_IGNORECASE FNM_CASEFOLD +#define FNM_FILE_NAME FNM_PATHNAME +#endif + +//todo: #include +//#include + +//__BEGIN_DECLS +int fnmatch(const char *, const char *, int); +//__END_DECLS + +#endif /* !_FNMATCH_H_ */ diff --git a/storage/cfstore/source/cfstore_list.h b/storage/cfstore/source/cfstore_list.h new file mode 100644 index 0000000000..7b9bd4cf33 --- /dev/null +++ b/storage/cfstore/source/cfstore_list.h @@ -0,0 +1,63 @@ +/** @file cfstore_list.h + * + * 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. + */ +#ifndef _CFSTORE_LIST_H_ +#define _CFSTORE_LIST_H_ + +/* + * Doubly linked list implementation based on the following design + * and psuedo-code: + * Introduction to Algorithms, TH Cormen, CE Leiserson, Rl Rivest, + * ISBN 0-262-03141-8 (1989), Pages 206-207. + */ +typedef struct cfstore_list_node_t +{ + struct cfstore_list_node_t *next; + struct cfstore_list_node_t *prev; +} cfstore_list_node_t; + + +#define CFSTORE_ZERO_NODE(_node_cFStOrE) \ + do{ \ + (_node_cFStOrE)->next=NULL; \ + (_node_cFStOrE)->prev=NULL; \ + }while(0) + +#define CFSTORE_INIT_LIST_HEAD(_node_cFStOrE) \ + do { \ + (_node_cFStOrE)->next = (_node_cFStOrE); \ + (_node_cFStOrE)->prev = (_node_cFStOrE); \ + } while (0) + +/* brief insert the new_node between 2 other nodes, the one before being node_before, the one after being node_after */ +static inline void cfstore_listAdd(cfstore_list_node_t* node_before, cfstore_list_node_t * new_node, cfstore_list_node_t* node_after) +{ + /* init new node before insertion */ + new_node->next = node_after; + new_node->prev = node_before; + node_before->next = new_node; + node_after->prev = new_node; +} + +/* brief remove the node D from the list by making the nodes before and after D point to each other */ +static inline void cfstore_listDel(cfstore_list_node_t *node) +{ + node->prev->next = node->next; + node->next->prev = node->prev; + CFSTORE_ZERO_NODE(node); +} +#endif /* _CFSTORE_LIST_H_ */ diff --git a/storage/cfstore/source/cfstore_test.c b/storage/cfstore/source/cfstore_test.c new file mode 100644 index 0000000000..b78b25b037 --- /dev/null +++ b/storage/cfstore/source/cfstore_test.c @@ -0,0 +1,564 @@ +/* @file cfstore_test.c + * + * 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. + * + * test support code implementation file. + */ +#include +#include +#include +#include +#include +#include "cfstore_config.h" +#include "cfstore_debug.h" +#include "cfstore_test.h" +#include + + +/* ruler for measuring text strings */ +/* 1 1 1 1 1 1 1 1 1 1 2 2 2 */ +/* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 */ +/* 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 */ + +const uint8_t cfstore_test_byte_data_table[CFSTORE_TEST_BYTE_DATA_TABLE_SIZE] = { + 0x2d, 0xf3, 0x31, 0x4c, 0x11, 0x4f, 0xde, 0x0d, 0xbd, 0xbc, 0xa6, 0x78, 0x36, 0x5c, 0x1d, 0x28, + 0x5f, 0xa9, 0x10, 0x65, 0x54, 0x45, 0x21, 0x1a, 0x88, 0xfe, 0x76, 0x45, 0xb9, 0xac, 0x65, 0x9a, + 0x34, 0x9d, 0x73, 0x10, 0xb4, 0xa9, 0x2e, 0x90, 0x95, 0x68, 0xac, 0xfe, 0xc5, 0x2d, 0x15, 0x03, + 0x34, 0x70, 0xf1, 0x1d, 0x48, 0xa1, 0xa0, 0xed, 0x5c, 0x2f, 0xf5, 0x2b, 0xb9, 0x84, 0xbb, 0x45, + 0x32, 0xdd, 0xb1, 0x33, 0x95, 0x2a, 0xbc, 0x26, 0xf0, 0x89, 0xba, 0xf4, 0xbd, 0xf9, 0x5d, 0x2e, + 0x6e, 0x11, 0xc6, 0xa7, 0x78, 0xfc, 0xc9, 0x0e, 0x6b, 0x38, 0xba, 0x14, 0x1b, 0xab, 0x4c, 0x20, + 0x91, 0xe4, 0xb0, 0xf1, 0x2b, 0x14, 0x07, 0x6b, 0xb5, 0xcd, 0xe3, 0x49, 0x75, 0xac, 0xe8, 0x98, + 0xf1, 0x58, 0x8f, 0xd9, 0xc4, 0x8f, 0x00, 0x17, 0xb5, 0x06, 0x6a, 0x33, 0xbd, 0xa7, 0x40, 0x5a, + 0xbf, 0x49, 0xf7, 0x27, 0x1b, 0x4c, 0x3e, 0x6f, 0xe3, 0x08, 0x1f, 0xfd, 0xa6, 0xd4, 0xc7, 0x5f, + 0xa4, 0xa6, 0x82, 0xad, 0x19, 0xd5, 0x5c, 0xd8, 0x3a, 0x49, 0x85, 0xc9, 0x21, 0x83, 0xf6, 0xc6, + 0x84, 0xf9, 0x76, 0x89, 0xf3, 0x2d, 0x17, 0x50, 0x97, 0x38, 0x48, 0x9a, 0xe1, 0x82, 0xcd, 0xac, + 0xa8, 0x1d, 0xd7, 0x96, 0x5e, 0xb3, 0x08, 0xa8, 0x3a, 0xc7, 0x2b, 0x05, 0xaf, 0xdc, 0x16, 0xdf, + 0x48, 0x0f, 0x2a, 0x7e, 0x3a, 0x82, 0xd7, 0x80, 0xd6, 0x49, 0x27, 0x5d, 0xe3, 0x07, 0x62, 0xb3, + 0xc3, 0x6c, 0xba, 0xb2, 0xaa, 0x9f, 0xd9, 0x03, 0x0d, 0x27, 0xa8, 0xe0, 0xd6, 0xee, 0x79, 0x4b, + 0xd6, 0x97, 0x99, 0xb7, 0x11, 0xd6, 0x0d, 0x34, 0xae, 0x99, 0x4a, 0x93, 0x95, 0xd0, 0x5a, 0x34, + 0x19, 0xa2, 0x69, 0x57, 0xcf, 0x7c, 0x3d, 0x98, 0x88, 0x5d, 0x04, 0xf2, 0xd7, 0xac, 0xa5, 0x63 +}; + + +/* @brief set of test data for sequential write tests */ +cfstore_test_rw_data_entry_t cfstore_test_rw_data_table[] = +{ + { 25, 'z' }, + { 00, 'a' }, + { 24, 'y' }, + { 01, 'b' }, + { 23, 'x' }, + { 02, 'c' }, + { 22, 'w' }, + { 03, 'd' }, + { 21, 'v' }, + { 04, 'e' }, + { 20, 'u' }, + { 05, 'f' }, + { 19, 't' }, + { 06, 'g' }, + { 18, 's' }, + { 07, 'h' }, + { 17, 'r' }, + { 8, 'i' }, + { 16, 'q' }, + { 9, 'j' }, + { 15, 'p' }, + { 10, 'k' }, + { 14, 'o' }, + { 11, 'l' }, + { 13, 'n' }, + { 12, 'm' }, + { CFSTORE_TEST_RW_TABLE_SENTINEL, '@' }, +}; + +const char* cfstore_test_opcode_str[] = +{ + "UNDEFINED", + "CFSTORE_OPCODE_CLOSE", + "CFSTORE_OPCODE_CREATE", + "CFSTORE_OPCODE_DELETE", + "CFSTORE_OPCODE_FIND", + "CFSTORE_OPCODE_FLUSH", + "CFSTORE_OPCODE_GET_KEY_NAME", + "CFSTORE_OPCODE_GET_STATUS", + "CFSTORE_OPCODE_GET_VALUE_LEN", + "CFSTORE_OPCODE_INITIALIZE", + "CFSTORE_OPCODE_OPEN", + "CFSTORE_OPCODE_POWER_CONTROL", + "CFSTORE_OPCODE_READ", + "CFSTORE_OPCODE_RSEEK", + "CFSTORE_OPCODE_UNINITIALIZE", + "CFSTORE_OPCODE_WRITE", + "CFSTORE_OPCODE_MAX" +}; +/* @brief test utility function to check a node appears correctly in the cfstore + * @note this function expects cfstore to have been initialised with + * a call to ARM_CFSTORE_DRIVER::Initialize() + */ +int32_t cfstore_test_check_node_correct(const cfstore_kv_data_t* node) +{ + char* read_buf; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + memset(&flags, 0, sizeof(flags)); + + ret = drv->Open(node->key_name, flags, hkey); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to open node (key_name=\"%s\", value=\"%s\")(ret=%d)\r\n", __func__, node->key_name, node->value, (int) ret); + goto out0; + } + len = strlen(node->value) + 1; + read_buf = (char*) malloc(len); + if(read_buf == NULL) { + CFSTORE_ERRLOG("%s:Error: failed to allocated read buffer \r\n", __func__); + goto out1; + } + memset(read_buf, 0, len); + ret = drv->Read(hkey, read_buf, &len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to write key (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + goto out2; + } + /* check read data is as expected */ + if(strncmp(read_buf, node->value, strlen(node->value)) != 0){ + CFSTORE_ERRLOG("%s:Error: read value data (%s) != KV value data (key_name=\"%s\", value=\"%s\")\r\n", __func__, read_buf, node->key_name, node->value); + ret = ARM_DRIVER_ERROR; + } +out2: + if(read_buf) free(read_buf); +out1: + drv->Close(hkey); + hkey = NULL; +out0: + return ret; +} + + +/* @brief test utility function to delete the cfstore key identified by key_name + * @note this function expects cfstore to have been initialised with + * a call to ARM_CFSTORE_DRIVER::Initialize() + */ +int32_t cfstore_test_delete(const char* key_name) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered.\r\n", __func__); + memset(&flags, 0, sizeof(flags)); + ret = drv->Open(key_name, flags, hkey); + if(ret < ARM_DRIVER_OK){ + return ret; + } + if(hkey != NULL){ + ret = drv->Delete(hkey); + drv->Close(hkey); + } + return ret; +} + +/* @brief test utility function to delete all of the KVs in the cfstore + * @note this function expects cfstore to have been initialised with + * a call to ARM_CFSTORE_DRIVER::Initialize() + */ +int32_t cfstore_test_delete_all(void) +{ + const char* key_name_query = "*"; + char key_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + uint8_t len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_HANDLE_INIT(next); + ARM_CFSTORE_HANDLE_INIT(prev); + + CFSTORE_FENTRYLOG("%s:entered.\r\n", __func__); + while((ret = drv->Find(key_name_query, prev, next)) == ARM_DRIVER_OK) + { + len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + drv->GetKeyName(next, key_name, &len); + CFSTORE_TP(CFSTORE_TP_DELETE, "%s:deleting key_name=%s, len=%d\r\n", __func__, key_name, (int) len); + ret = drv->Delete(next); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to delete key_name=%s, len=%d\r\n", __func__, key_name, (int) len); + return ret; + } + CFSTORE_HANDLE_SWAP(prev, next); + } + if(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND) { + /* as expected, no more keys have been found by the Find()*/ + ret = ARM_DRIVER_OK; + } + // todo: find portable format specification CFSTORE_FENTRYLOG("%s:exiting (ret=%ld).\r\n", __func__, ret); + CFSTORE_FENTRYLOG("%s:exiting (ret=%" PRId32 ").\r\n", __func__, ret); + return ret; +} + + +/* @brief test utility function to create a KV in the cfstore + * @note this function expects cfstore to have been initialised with + * a call to ARM_CFSTORE_DRIVER::Initialize() + */ +int32_t cfstore_test_create(const char* key_name, const char* data, ARM_CFSTORE_SIZE* len, ARM_CFSTORE_KEYDESC* kdesc) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE value_len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_HANDLE_INIT(hkey); + + CFSTORE_FENTRYLOG("%s:entered.\r\n", __func__); + value_len = *len; + kdesc->drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + ret = drv->Create(key_name, value_len, kdesc, hkey); + if(ret < ARM_DRIVER_OK){ + return ret; + } + value_len = *len; + ret = drv->Write(hkey, data, &value_len); + if(ret < ARM_DRIVER_OK){ + drv->Close(hkey); + return ret; + } + if(value_len != *len){ + drv->Close(hkey); + return ARM_DRIVER_ERROR; + } + drv->Close(hkey); + return ret; +} + +/* @brief test utility function to create KVs from the supplied table + * @note this function expects cfstore to have been initialised with + * a call to ARM_CFSTORE_DRIVER::Initialize() + */ +int32_t cfstore_test_create_table(const cfstore_kv_data_t* table) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + cfstore_kv_data_t* node = NULL; + ARM_CFSTORE_KEYDESC kdesc; + + CFSTORE_FENTRYLOG("%s:entered.\r\n", __func__); + memset(&kdesc, 0, sizeof(kdesc)); + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + while(table->key_name != NULL) + { + len = strlen(table->value); + ret = cfstore_test_create(table->key_name, table->value, &len, &kdesc); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to create node (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + return ret; + } + table++; + } + return ret; +} + +cfstore_kv_data_t cfstore_test_init_1_data[] = { + CFSTORE_INIT_1_TABLE_HEAD, + { "b", "1"}, + { "c", "12"}, + { "d", "123"}, + { "e", "1234"}, + { "g", "12345"}, + { "h", "123456"}, + { "i", "1234567"}, + { "j", "12345678"}, + { "k", "123456789"}, + { "l", "1234567890"}, + { "m", "12345678901"}, + { "n", "123456789012"}, + { "o", "1234567890123"}, + { "p", "12345678901234"}, + { "q", "123456789012345"}, + { "r", "1234567890123456"}, + { "0", "a"}, + { "01", "ab"}, + { "012", "abc"}, + { "0123", "abcd"}, + { "01234", "abcde"}, + { "012345", "abcdef"}, + { "0123456", "abcdefg"}, + { "01234567", "abcdefgh"}, + { "012345678", "abcdefghi"}, + { "0123456789", "abcdefghj"}, + { "0123456789a", "abcdefghjk"}, + { "0123456789ab", "abcdefghjkl"}, + { "0123456789abc", "abcdefghjklm"}, + { "0123456789abcd", "abcdefghjklmn"}, + { "0123456789abcde", "abcdefghjklmno"}, + { "0123456789abcdef", "abcdefghjklmnop"}, + { "0123456789abcdef0", "abcdefghjklmnopq"}, + { "0123456789abcdef01", "abcdefghjklmnopqr"}, + { "0123456789abcdef012", "abcdefghjklmnopqrs"}, + { "0123456789abcdef0123", "abcdefghjklmnopqrst"}, + { "0123456789abcdef01234", "abcdefghjklmnopqrstu"}, + { "0123456789abcdef012345", "abcdefghjklmnopqrstuv"}, + CFSTORE_INIT_1_TABLE_MID_NODE, + { "0123456789abcdef01234567", "abcdefghjklmnopqrstuvwx"}, + { "0123456789abcdef012345678", "abcdefghjklmnopqrstuvwxy"}, + { "0123456789abcdef0123456789", "abcdefghjklmnopqrstuvwxyz"}, + { "0123456789abcdef0123456789a", "b"}, + { "0123456789abcdef0123456789ab", "c"}, + { "0123456789abcdef0123456789abc", "d"}, + { "0123456789abcdef0123456789abcd", "e"}, + { "0123456789abcdef0123456789abcde", "f"}, + { "0123456789abcdef0123456789abcdef", "g"}, + { "com.arm.mbed.wifi.accesspoint.essid", ""}, + { "com.arm.mbed.wifi.accesspoint.essid2", ""}, + { "yotta.your-yotta-registry-module-name.module1", ""}, + { "yotta.hello-world.animal{wobbly-dog}{foot}frontLeft", "missing"}, + { "yotta.hello-world.animal{wobbly-dog}{foot}frontRight", "present"}, + { "yotta.hello-world.animal{wobbly-dog}{foot}backLeft", "half present"}, + { "piety.demands.us.to.honour.truth.above.our.friends", "Aristotle"}, + { "basement.medicine.pavement.government.trenchcoat.off.cough.off.kid.did.when.again.alleyway.friend.cap.pen.dollarbills.ten.foot.soot.put.but.anyway.say.May.DA.kid.did.toes.bows.those.hose.nose.clothes.man.blows.well.well", "TheRollingStone" }, + CFSTORE_INIT_1_TABLE_TAIL, + { NULL, NULL}, +}; + + +/* @brief utility test function to initialise cfstore sram area with some + * KV's to manipulate + * @note this function expects cfstore to have been initialised with + * a call to ARM_CFSTORE_DRIVER::Initialize() + */ +int32_t cfstore_test_init_1(void) +{ + char* read_buf = NULL; + const uint8_t key_name_max_len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + uint8_t key_name_len = 0; + char key_name_buf[key_name_max_len]; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_SIZE max_len = 0; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + cfstore_kv_data_t* node = NULL; + ARM_CFSTORE_KEYDESC kdesc; + ARM_CFSTORE_HANDLE_INIT(hkey); + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + memset(&kdesc, 0, sizeof(kdesc)); + memset(key_name_buf, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + + /*scan for max length of value blob*/ + node = cfstore_test_init_1_data; + while(node->key_name != NULL) + { + len = strlen(node->value); + if(len > max_len){ + max_len = len; + max_len++; + } + node++; + } + read_buf = (char*) malloc(max_len); + if(read_buf == NULL) { + CFSTORE_ERRLOG("%s:Error: failed to allocated read buffer \r\n", __func__); + return ret; + } + kdesc.drl = ARM_RETENTION_WHILE_DEVICE_ACTIVE; + node = cfstore_test_init_1_data; + while(node->key_name != NULL) + { + CFSTORE_DBGLOG("%s:About to create new node (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + ret = drv->Create(node->key_name, strlen(node->value), &kdesc, hkey); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to create node (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + return ret; + } + + CFSTORE_DBGLOG("%s:length of KV=%d (key_name=\"%s\", value=\"%s\")\r\n", __func__, (int) len, node->key_name, node->value); + len = strlen(node->value); + ret = drv->Write(hkey, (char*) node->value, &len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to write key (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + drv->Close(hkey); + return ret; + } + if(len != strlen(node->value)){ + CFSTORE_ERRLOG("%s:Error: failed to write full value data (key_name=\"%s\", value=\"%s\"), len=%d\r\n", __func__, node->key_name, node->value, (int) len); + drv->Close(hkey); + return ARM_DRIVER_ERROR; + } + /* read the data back*/ + len = strlen(node->value); + memset(read_buf, 0, max_len); + ret = drv->Read(hkey, read_buf, &len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to read key (key_name=\"%s\", value=\"%s\")\r\n", __func__, node->key_name, node->value); + drv->Close(hkey); + return ret; + } + if(len != strlen(node->value)){ + CFSTORE_ERRLOG("%s:Error: failed to read full value data (key_name=\"%s\", value=\"%s\"), len=%d, ret=%d\r\n", __func__, node->key_name, node->value, (int) len, (int) ret); + drv->Close(hkey); + return ARM_DRIVER_ERROR; + } + key_name_len = key_name_max_len; + memset(key_name_buf, 0, key_name_len); + drv->GetKeyName(hkey, key_name_buf, &key_name_len); + if(len != strlen(node->value)){ + CFSTORE_ERRLOG("%s:Error: failed to GetKeyName() (key_name=\"%s\", value=\"%s\"), len=%d\r\n", __func__, node->key_name, node->value, (int) len); + drv->Close(hkey); + return ARM_DRIVER_ERROR; + } + CFSTORE_LOG("Created KV successfully (key_name=\"%s\", value=\"%s\")\r\n", key_name_buf, read_buf); + drv->Close(hkey); + node++; + } + free(read_buf); + return ret; +} + +/* @brief test utility function to check a particular KV exists in the + * cfstore using Find() interface + * @note this function expects cfstore to have been initialised with + * a call to ARM_CFSTORE_DRIVER::Initialize() + */ +int32_t cfstore_test_kv_is_found(const char* key_name, bool* bfound) +{ + CFSTORE_FENTRYLOG("%s:entered.\r\n", __func__); + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_HANDLE_INIT(prev); + ARM_CFSTORE_HANDLE_INIT(next); + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + + CFSTORE_ASSERT(bfound != NULL); + CFSTORE_ASSERT(key_name != NULL); + *bfound = 0; + + ret = drv->Find(key_name, prev, next); + if(ret == ARM_DRIVER_OK){ + *bfound = 1; + CFSTORE_DBGLOG("%s:Found key_name=\"%s\", about to call close.\r\n", __func__, key_name); + drv->Close(next); + } + return ret; +} + + +/* @brief support function for generating a kv_name + * @param name buffer to hold kv name + * @param len length of kv name to generate + * @note braces are not included in the generated names as the names are + * of varible length and theyre may be unmatched + * + */ +#define CFSTORE_TEST_KV_NAME_BUF_MAX_DATA (10+26+26+4) +int32_t cfstore_test_kv_name_gen(char* name, const size_t len) +{ + size_t i; + const char buf[CFSTORE_TEST_KV_NAME_BUF_MAX_DATA+1] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_@"; + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + for(i = 0; i < len; i++) + { + name[i] = buf[i % CFSTORE_TEST_KV_NAME_BUF_MAX_DATA]; + } + return ARM_DRIVER_OK; +} + +/* @brief test utility function to read the value blob of a specified KV + * @note this function expects cfstore to have been initialised with + * a call to ARM_CFSTORE_DRIVER::Initialize() + */ +int32_t cfstore_test_read(const char* key_name, char* data, ARM_CFSTORE_SIZE* len) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + memset(&flags, 0, sizeof(flags)); + if(key_name == NULL) { + CFSTORE_ERRLOG("%s:invalid key_name argument \r\n", __func__); + goto out0; + } + if(data == NULL) { + CFSTORE_ERRLOG("%s:invalid data argument \r\n", __func__); + goto out0; + } + if(len == NULL) { + CFSTORE_ERRLOG("%s:invalid len argument \r\n", __func__); + goto out0; + } + ret = drv->Open(key_name, flags, hkey); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to open node (key_name=\"%s\")(ret=%d)\r\n", __func__, key_name, (int) ret); + goto out1; + } + ret = drv->Read(hkey, data, len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to read key (key_name=\"%s\"\r\n", __func__, key_name); + goto out2; + } +out2: + drv->Close(hkey); +out1: +out0: + return ret; +} + +/* @brief write the value blob of a specified KV + * @note this function expects cfstore to have been initialised with + * a call to ARM_CFSTORE_DRIVER::Initialize() + */ +int32_t cfstore_test_write(const char* key_name, const char* data, ARM_CFSTORE_SIZE* len) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + ARM_CFSTORE_HANDLE_INIT(hkey); + ARM_CFSTORE_FMODE flags; + + CFSTORE_FENTRYLOG("%s:entered\r\n", __func__); + memset(&flags, 0, sizeof(flags)); + if(key_name == NULL) { + CFSTORE_ERRLOG("%s:Error: invalid key_name argument \r\n", __func__); + goto out0; + } + if(data == NULL) { + CFSTORE_ERRLOG("%s:Error: invalid data argument \r\n", __func__); + goto out0; + } + if(len == NULL) { + CFSTORE_ERRLOG("%s:Error: invalid len argument \r\n", __func__); + goto out0; + } + flags.write = 1; + ret = drv->Open(key_name, flags, hkey); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to open node (key_name=\"%s\")(ret=%d)\r\n", __func__, key_name, (int) ret); + goto out1; + } + ret = drv->Write(hkey, data, len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to write key (key_name=\"%s\")\r\n", __func__, key_name); + goto out2; + } +out2: + drv->Close(hkey); +out1: +out0: + return ret; +} + + diff --git a/storage/cfstore/source/cfstore_test.h b/storage/cfstore/source/cfstore_test.h new file mode 100644 index 0000000000..056c4ae828 --- /dev/null +++ b/storage/cfstore/source/cfstore_test.h @@ -0,0 +1,84 @@ +/** @file cfstore_test.h + * + * 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. + * + * Header file for test support data structures and function API. + */ +#ifndef __CFSTORE_TEST_H +#define __CFSTORE_TEST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "configuration-store/configuration_store.h" + +/* Defines */ +#define CFSTORE_INIT_1_TABLE_HEAD { "a", ""} +#define CFSTORE_INIT_1_TABLE_MID_NODE { "0123456789abcdef0123456", "abcdefghijklmnopqrstuvwxyz"} +#define CFSTORE_INIT_1_TABLE_TAIL { "yotta.hello-world.animal{wobbly-dog}{foot}backRight", "present"} +#define CFSTORE_TEST_RW_TABLE_SENTINEL 0xffffffff +#define CFSTORE_TEST_BYTE_DATA_TABLE_SIZE 256 +#define CFSTORE_UTEST_MSG_BUF_SIZE 256 +#define CFSTORE_UTEST_DEFAULT_TIMEOUT_MS 10000 +#define CFSTORE_MBED_HOSTTEST_TIMEOUT 60 + +/* support macro for make string for utest _MESSAGE macros, which dont support formatted output */ +#define CFSTORE_TEST_UTEST_MESSAGE(_buf, _max_len, _fmt, ...) \ + do \ + { \ + snprintf((_buf), (_max_len), (_fmt), __VA_ARGS__); \ + }while(0); + + +/* + * Structures + */ + +/* kv data for test */ +typedef struct cfstore_kv_data_t { + const char* key_name; + const char* value; +} cfstore_kv_data_t; + +typedef struct cfstore_test_rw_data_entry_t +{ + uint32_t offset; + char rw_char; +} cfstore_test_rw_data_entry_t; + + +extern cfstore_kv_data_t cfstore_test_init_1_data[]; +extern cfstore_test_rw_data_entry_t cfstore_test_rw_data_table[]; +extern const char* cfstore_test_opcode_str[]; +extern const uint8_t cfstore_test_byte_data_table[CFSTORE_TEST_BYTE_DATA_TABLE_SIZE]; + +int32_t cfstore_test_check_node_correct(const cfstore_kv_data_t* node); +int32_t cfstore_test_create(const char* key_name, const char* data, size_t* len, ARM_CFSTORE_KEYDESC* kdesc); +int32_t cfstore_test_create_table(const cfstore_kv_data_t* table); +int32_t cfstore_test_delete(const char* key_name); +int32_t cfstore_test_delete_all(void); +int32_t cfstore_test_init_1(void); +int32_t cfstore_test_kv_is_found(const char* key_name, bool* bfound); +int32_t cfstore_test_kv_name_gen(char* name, const size_t len); +int32_t cfstore_test_read(const char* key_name, char* data, size_t* len); +int32_t cfstore_test_write(const char* key_name, const char* data, size_t* len); + +#ifdef __cplusplus +} +#endif + +#endif /* __CFSTORE_TEST_H */ diff --git a/storage/cfstore/source/cfstore_utest.h b/storage/cfstore/source/cfstore_utest.h new file mode 100644 index 0000000000..45ac78c89d --- /dev/null +++ b/storage/cfstore/source/cfstore_utest.h @@ -0,0 +1,76 @@ +/** @file cfstore_test.h + * + * 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. + * + * Header file for test support data structures and function API. + */ +#ifndef __CFSTORE_UTEST_H +#define __CFSTORE_UTEST_H + +#include +#include "cfstore_debug.h" +#include "cfstore_test.h" + +using namespace utest::v1; + +void cfstore_utest_default_callback(int32_t status, ARM_CFSTORE_OPCODE cmd_code, void *client_context, ARM_CFSTORE_HANDLE handle) +{ + (void) status; + (void) client_context; + (void) handle; + + CFSTORE_FENTRYLOG("%s:entered: status=%d, cmd_code=%d (%s) handle=%p\n", __func__, (int) status, (int) cmd_code, cfstore_test_opcode_str[cmd_code], handle); + switch(cmd_code) + { + case CFSTORE_OPCODE_INITIALIZE: + case CFSTORE_OPCODE_FLUSH: + case CFSTORE_OPCODE_UNINITIALIZE: + case CFSTORE_OPCODE_CLOSE: + case CFSTORE_OPCODE_CREATE: + case CFSTORE_OPCODE_DELETE: + case CFSTORE_OPCODE_FIND: + case CFSTORE_OPCODE_GET_KEY_NAME: + case CFSTORE_OPCODE_GET_STATUS: + case CFSTORE_OPCODE_GET_VALUE_LEN: + case CFSTORE_OPCODE_OPEN: + case CFSTORE_OPCODE_POWER_CONTROL: + case CFSTORE_OPCODE_READ: + case CFSTORE_OPCODE_RSEEK: + case CFSTORE_OPCODE_WRITE: + default: + CFSTORE_DBGLOG("%s:debug: received asynchronous notification for opcode=%d (%s)", __func__, cmd_code, cmd_code < CFSTORE_OPCODE_MAX ? cfstore_test_opcode_str[cmd_code] : "unknown"); + } + CFSTORE_DBGLOG("%s:about to validate callback\n", __func__); + Harness::validate_callback(); + return; +} + + +static control_t cfstore_utest_default_start(const size_t call_count) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_DRIVER* drv = &cfstore_driver; + char cfstore_utest_msg[CFSTORE_UTEST_MSG_BUF_SIZE]; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + ret = drv->Initialize(cfstore_utest_default_callback, NULL); + CFSTORE_TEST_UTEST_MESSAGE(cfstore_utest_msg, CFSTORE_UTEST_MSG_BUF_SIZE, "%s:Error: failed to initialize CFSTORE (ret=%" PRId32 ")\n", __func__, ret); + TEST_ASSERT_MESSAGE(ret >= ARM_DRIVER_OK, cfstore_utest_msg); + return CaseTimeout(CFSTORE_UTEST_DEFAULT_TIMEOUT_MS); +} + +#endif /* __CFSTORE_UTEST_H */ diff --git a/storage/cfstore/source/cfstore_uvisor.h b/storage/cfstore/source/cfstore_uvisor.h new file mode 100644 index 0000000000..fb5936d2fd --- /dev/null +++ b/storage/cfstore/source/cfstore_uvisor.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2013-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 __CFSTORE_UVISOR_H__ +#define __CFSTORE_UVISOR_H__ + +/* target specifc ACLs */ +#if defined(TARGET_LIKE_FRDM_K64F) + +#define CFSTORE_UVISOR_LED_ON false +#define CFSTORE_UVISOR_LED_OFF true +#define CFSTORE_UVISOR_MAIN_LED LED_BLUE +#define CFSTORE_UVISOR_MAIN_BTN SW2 +#define CFSTORE_UVISOR_MAIN_BTN_PUPD PullUp +#define CFSTORE_UVISOR_MAIN_ACL(acl_list_name) \ + static const UvisorBoxAclItem acl_list_name[] = { \ + {MCG, sizeof(*MCG), UVISOR_TACLDEF_PERIPH}, \ + {SIM, sizeof(*SIM), UVISOR_TACLDEF_PERIPH}, \ + {PORTB, sizeof(*PORTB), UVISOR_TACLDEF_PERIPH}, \ + {PORTC, sizeof(*PORTC), UVISOR_TACLDEF_PERIPH}, \ + {RTC, sizeof(*RTC), UVISOR_TACLDEF_PERIPH}, \ + {LPTMR0, sizeof(*LPTMR0), UVISOR_TACLDEF_PERIPH}, \ + {PIT, sizeof(*PIT), UVISOR_TACLDEF_PERIPH}, \ + {SMC, sizeof(*SMC), UVISOR_TACLDEF_PERIPH}, \ + {UART0, sizeof(*UART0), UVISOR_TACLDEF_PERIPH}, \ + } + +#elif defined(TARGET_LIKE_STM32F429I_DISCO) + +#define CFSTORE_UVISOR_LED_ON false +#define CFSTORE_UVISOR_LED_OFF true +#define CFSTORE_UVISOR_MAIN_LED LED1 +#define CFSTORE_UVISOR_MAIN_BTN USER_BUTTON +#define CFSTORE_UVISOR_MAIN_BTN_PUPD PullDown +#define CFSTORE_UVISOR_MAIN_ACL(acl_list_name) \ + static const UvisorBoxAclItem acl_list_name[] = { \ + {TIM2, sizeof(*TIM2), UVISOR_TACLDEF_PERIPH}, \ + {TIM5, sizeof(*TIM5), UVISOR_TACLDEF_PERIPH}, \ + {GPIOA, sizeof(*GPIOA), UVISOR_TACLDEF_PERIPH}, \ + {GPIOG, sizeof(*GPIOG), UVISOR_TACLDEF_PERIPH}, \ + /* FIXME: secure RCC/EXTI/SYSCFG/FLASH */ \ + {RCC, sizeof(*RCC), UVISOR_TACLDEF_PERIPH}, \ + {EXTI, sizeof(*EXTI), UVISOR_TACLDEF_PERIPH}, \ + {SYSCFG, sizeof(*SYSCFG), UVISOR_TACLDEF_PERIPH}, \ + {FLASH, sizeof(*FLASH), UVISOR_TACLDEF_PERIPH}, \ + {PWR, sizeof(*PWR), UVISOR_TACLDEF_PERIPH}, \ + {USART1, sizeof(*USART1), UVISOR_TACLDEF_PERIPH}, \ + {(void *) 0x42470000, 0x1000, UVISOR_TACLDEF_PERIPH}, \ + } + +#elif defined(TARGET_LIKE_EFM32GG_STK) \ + || defined(TARGET_LIKE_EFM32LG_STK) \ + || defined(TARGET_LIKE_EFM32WG_STK) + +#define CFSTORE_UVISOR_LED_ON false +#define CFSTORE_UVISOR_LED_OFF true +#define CFSTORE_UVISOR_MAIN_LED LED1 +#define CFSTORE_UVISOR_MAIN_BTN BTN0 +#define CFSTORE_UVISOR_MAIN_BTN_PUPD PullUp +#define CFSTORE_UVISOR_MAIN_ACL(acl_list_name) \ + static const UvisorBoxAclItem acl_list_name[] = { \ + {GPIO, sizeof(*GPIO), UVISOR_TACLDEF_PERIPH}, \ + {UART0, sizeof(*UART0), UVISOR_TACLDEF_PERIPH}, \ + {TIMER0, sizeof(*TIMER0), UVISOR_TACLDEF_PERIPH}, \ + /* FIXME: Secure CMU */ \ + {CMU, sizeof(*CMU), UVISOR_TACLDEF_PERIPH}, \ + {RTC, sizeof(*RTC), UVISOR_TACLDEF_PERIPH}, \ + /* FIXME: Secure MSC */ \ + {MSC, sizeof(*MSC), UVISOR_TACLDEF_PERIPH}, \ + /* mbed-hal-silabs requires the DI page to be readable */ \ + {(void*) 0x0FE08000, 0x1000, UVISOR_TACLDEF_SECURE_CONST}, \ + } + +#elif defined(TARGET_LIKE_EFM32PG_STK) + +#define CFSTORE_UVISOR_LED_ON false +#define CFSTORE_UVISOR_LED_OFF true +#define CFSTORE_UVISOR_MAIN_LED LED1 +#define CFSTORE_UVISOR_MAIN_BTN BTN0 +#define CFSTORE_UVISOR_MAIN_BTN_PUPD PullUp +#define CFSTORE_UVISOR_MAIN_ACL(acl_list_name) \ + static const UvisorBoxAclItem acl_list_name[] = { \ + {GPIO, sizeof(*GPIO), UVISOR_TACLDEF_PERIPH}, \ + {USART0, sizeof(*USART0), UVISOR_TACLDEF_PERIPH}, \ + {TIMER0, sizeof(*TIMER0), UVISOR_TACLDEF_PERIPH}, \ + /* FIXME: Secure CMU */ \ + {CMU, sizeof(*CMU), UVISOR_TACLDEF_PERIPH}, \ + {EMU, sizeof(*EMU), UVISOR_TACLDEF_PERIPH}, \ + {RTCC, sizeof(*RTCC), UVISOR_TACLDEF_PERIPH}, \ + /* FIXME: Secure MSC */ \ + {MSC, sizeof(*MSC), UVISOR_TACLDEF_PERIPH}, \ + /* mbed-hal-silabs requires the DI page to be readable */ \ + {(void*) 0x0FE08000, 0x1000, UVISOR_TACLDEF_SECURE_CONST}, \ + } + +#else + +#define CFSTORE_UVISOR_LED_ON true +#define CFSTORE_UVISOR_LED_OFF false +#define CFSTORE_UVISOR_MAIN_LED NC +#define CFSTORE_UVISOR_MAIN_BTN NC +#define CFSTORE_UVISOR_MAIN_BTN_PUPD PullNone +#define CFSTORE_UVISOR_MAIN_ACL(acl_list_name) \ + static const UvisorBoxAclItem acl_list_name[] = {} + +#endif + +#endif diff --git a/storage/cfstore/source/configuration_store.c b/storage/cfstore/source/configuration_store.c new file mode 100644 index 0000000000..c943172e5e --- /dev/null +++ b/storage/cfstore/source/configuration_store.c @@ -0,0 +1,4098 @@ +/** @file configuration_store.c + * + * 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 +#include +#include +#include + +#if defined CFSTORE_CONFIG_MBED_OS_VERSION && CFSTORE_CONFIG_MBED_OS_VERSION == 3 +#include +#endif /* CFSTORE_CONFIG_MBED_OS_VERSION == 3 */ +#include "cfstore_config.h" +#ifdef YOTTA_CFG_CFSTORE_UVISOR +#include "uvisor-lib/uvisor-lib.h" +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ +#if defined CFSTORE_CONFIG_BACKEND_FLASH_ENABLED && CFSTORE_CONFIG_BACKEND_FLASH_ENABLED == 1 +#include +#include +#include +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ +#include +#include "cfstore_debug.h" +#include "cfstore_list.h" + + +#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 */ +#endif + + +/* + * Externs + */ +#if defined CFSTORE_CONFIG_BACKEND_FLASH_ENABLED && CFSTORE_CONFIG_BACKEND_FLASH_ENABLED == 1 +extern ARM_DRIVER_STORAGE ARM_Driver_Storage_(0); +ARM_DRIVER_STORAGE *cfstore_storage_drv = &ARM_Driver_Storage_(0); +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ + +/* + * Defines + * + * CFSTORE_FLASH_STACK_BUF_SIZE + * when performing flush, if the program_unit <= CFSTORE_FLASH_STACK_BUF_SIZE octets then a + * stack buffer is used to perform the tail write. Otherwise a buffer is malloced + * + * CFSTORE_FLASH_AREA_SIZE_MIN + * 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 + * value that indicates an operation has been done i.e. a value > 0 + */ +#define CFSTORE_KEY_NAME_CHARS_ACCEPTABLE "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}.-_@" +#define CFSTORE_KEY_NAME_QUERY_CHARS_ACCEPTABLE CFSTORE_KEY_NAME_CHARS_ACCEPTABLE"*" +#define CFSTORE_HKVT_REFCOUNT_MAX 0xff +#define CFSTORE_LOCK_REFCOUNT_MAX 0xffff +#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_fsm_null NULL +#define CFSTORE_SENTINEL 0x7fffffff +#define CFSTORE_CALLBACK_RET_CODE_DEFAULT 0x1 +#define ARM_DRIVER_OK_DONE 1 + +/* + * Simple Types + */ +#define CFSTORE_LOCK uint32_t + + +/* + * Structures + */ + +/** @brief + * + * @param key_permissions + * bottom 6 bits contain the ACLs-bits (owner read/write/execute, + * other read/write/execute). The remaining bits in this field are + * used for the Device Data Security Protection Features bit field, + * bits are low-active + * @param perm_owner_read + * if set => this KV is owner readable + * @param perm_owner_write + * if set => this KV is owner writable + * @param perm_owner_execute + * if set => this KV is owner executable + * @param perm_other_read + * if set => this KV is world readable + * @param perm_other_write + * if set => this KV is world writable + * @param perm_other_execute + * if set => this KV is world executable + * @param klength + * key name size including zero-padding + * @param vlength + * this value fragment length + * @param refcount + * Number of handles open on this hkvt + * + * @param delete + * indicates this KV is being deleted + */ +typedef struct cfstore_area_header_t +{ + uint32_t vlength; + uint8_t klength; + uint8_t perm_owner_read : 1; + uint8_t perm_owner_write : 1; + uint8_t perm_owner_execute : 1; + uint8_t perm_other_read : 1; + uint8_t perm_other_write : 1; + uint8_t perm_other_execute : 1; + uint8_t reserved : 2; + uint8_t refcount; + struct flags_t { + uint8_t delete : 1; + uint8_t reserved : 7; + } flags ; +} cfstore_area_header_t; + + +/* helper struct */ +typedef struct cfstore_area_hkvt_t +{ + uint8_t *head; + uint8_t *key; + uint8_t *value; + uint8_t *tail; +} cfstore_area_hkvt_t; + + +/* helper struct */ +typedef struct cfstore_client_notify_data_t +{ + uint32_t opcode; + int32_t status; + ARM_CFSTORE_HANDLE handle; +} cfstore_client_notify_data_t; + +/* @brief test fsm states and events */ +typedef enum cfstore_fsm_state_t { + cfstore_fsm_state_stopped = 0, + cfstore_fsm_state_initing, + cfstore_fsm_state_reading, + cfstore_fsm_state_logging, + cfstore_fsm_state_committing, + cfstore_fsm_state_resetting, + cfstore_fsm_state_ready, /* ready for next flash journal command to arise */ + cfstore_fsm_state_max +} cfstore_fsm_state_t; + +/* @brief test fsm events */ +typedef enum cfstore_fsm_event_t { + cfstore_fsm_event_init_done = 0, + cfstore_fsm_event_read_done, + cfstore_fsm_event_log_done, + cfstore_fsm_event_commit_req, + cfstore_fsm_event_commit_done, + cfstore_fsm_event_reset_done, + cfstore_fsm_event_max, +} cfstore_fsm_event_t; + +typedef int32_t (*cfstore_fsm_handler)(void* ctx); + +/* @brief flash finite state machine helper function */ +typedef struct cfstore_fsm_t +{ + cfstore_fsm_state_t state; + cfstore_fsm_event_t event; +} cfstore_fsm_t; + + +#ifdef CFSTORE_DEBUG +#if defined CFSTORE_CONFIG_BACKEND_FLASH_ENABLED && CFSTORE_CONFIG_BACKEND_FLASH_ENABLED == 1 +/* strings used for debug trace */ +static const char* cfstore_flash_opcode_str[] = +{ + "FLASH_JOURNAL_OPCODE_INITIALIZE", + "FLASH_JOURNAL_OPCODE_GET_INFO", + "FLASH_JOURNAL_OPCODE_READ_BLOB", + "FLASH_JOURNAL_OPCODE_LOG_BLOB", + "FLASH_JOURNAL_OPCODE_COMMIT", + "FLASH_JOURNAL_OPCODE_RESET", +}; + +static const char* cfstore_flash_state_str[] = +{ + "stopped", + "initializing", + "reading", + "logging", + "committing", + "resetting", + "ready", + "unknown" +}; + +static const char* cfstore_flash_event_str[] = +{ + "init_done", + "read_done", + "log_done", + "commit_req", + "commit_done", + "reset_done", + "unknown" +}; +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ +#endif /* CFSTORE_DEBUG */ + + +/* + * Forward decl + */ +#if defined CFSTORE_CONFIG_BACKEND_FLASH_ENABLED && CFSTORE_CONFIG_BACKEND_FLASH_ENABLED == 1 +static int32_t cfstore_fsm_state_handle_event(cfstore_fsm_t* fsm, cfstore_fsm_event_t event, void* context); +static int32_t cfstore_fsm_state_set(cfstore_fsm_t* fsm, cfstore_fsm_state_t new_state, void* ctx); +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ +static int32_t cfstore_get_key_name_ex(cfstore_area_hkvt_t *hkvt, char* key_name, uint8_t *key_name_len); + + +/* Walking Area HKVT's While Inserted a New HKVT: + * Implementation Note 1 [NOTE1] + * + * The implementation must address the following problem: + * - client1 may be creating a new KV into area_0, which means inserting the + * header-key-value-tail data into area_0. + * - concurrently, client2 (through a call to Find()) is walking KVs in area_0, + * and the walk has to be safe against the insertion of the new KV. + * + * This problem is addressed in the by using the cfstore_ctx_g.rw_lock to police + * access to the area when making changes. + * - Walking the KVs in area_0 is performed using the header structures, + * which contain key and value lengths required to find the start of the + * next hkvt. These must not change under the client. + * - The Find() walk is terminated when the hkvt header pointer is found to + * point to cfstore_ctx_g.area_0_tail i.e. when this arises then the + * iterator knows its come to the end of the hkvt's in the area. + * - When inserting a new KV, the last operation to be performed is to + * update cfstore_ctx_g.area_0_tail to point to the new tail. This + * operation also reveals the new KV to other operations including + * the Find(). All the header, key, value and tail data for the + * HKVT must be setup correctly before the tail pointer is updated. + * + * Memory Management (todo: future support) + * Implementation Note 2 [NOTE2] + * CFSTORE supports using a client provisioned SRAM slab rather than using realloc() to allocated heap + * memory. This has the following advantages: + * - the client is in control of the memory allocation. + * - realloc() cannot fail (e.g. due to memory leaks losing memory) as the sram has been preprovisioned. + * This makes the system more resilient. + * The client specifes the sram slab in the following way: + * - having target.json defined yotta_config.h symbol for CFSTORE_SRAM_START_ADDR, CFSTORE_SRAM_SIZE + * and #ifdef on these values to use that memory area for area_0 rather than using malloc. + * - for the case where a client tries to create a KV which causes area_0 to exceed CFSTORE_SRAM_SIZE + * then the operation is failed. + * - modify the API so that the client is responsible for allocating the memory the the CFSTORE internal + * data structures, with the size of the internal data structure exposed through a #define. + * The contents of the buffer are opaque to the client. The reasons for this are as follows: + * - to allow the cfstore implementation not to use malloc(). + * - the memory allocation policy for allocating the memory of CFSTORE internal data structures + * can be decided and implemented by the client + * - for clients written in C++, its possible to have a static class with the memory for the + * internal context, and the static class memory area is given to CFSTORE for use, so it + * provides good C++ support. + * - The SRAM area can be allocated with the previous point, and the handle associated data + * structures i.e. cfstore_file_t, can be covered by the supplied buffers to those functions + * creating handles. + * - currently neither target.json nor config.json allow a symbol in yotta_config.h to be defined + * for the current case of CFSTORE being a yotta module/library. + * + * UVISOR Integration (todo) + * Implementation Note 3 [NOTE3] + * Outstanding Questions: + * - uvisor_ctx. Should all functions use this to access the global data context? + * - see cfstore_ctx_get() for an implementation + * - compile in cfstore_ctx_g only when not using uvisor + * - how do you allocate heap memory objects with uvisor protections? + * - doesnt seem to be an api for this yet. + * - will be required for sram storage of KVs i.e. "the area". + * - will be required for file objects + * - Q: is it safe to store the caller_box_id in the cfstore_file_t? + * A: no, because the cfstore_file_t is held in client controlled memory (opaque hkey) + * so the client can modify from under cfstore, breaching security if it was used + * by other cfstore methods. + * - method for securing access: + * - create()/open() checks namespace etc, and then creates/opens cfstore_file_t + * and returns hkey (opaque cfstore_file_t) for subsequent use by api calls. + * - read/write/rseek etc check the kv pathname accessible via cfstore_file_t::head + * is within the callers namespace. + * - we are trusting the caller to be secure and not be malicious? + * - put "uvisor-lib" : "^2.0.0" in module.json. not necessary as mbed-drivers has this dep. + * - flash-journal change from using NVIC_Set/GetVector() to VIRQ_Set/GetVector() + * + */ + +/* + * @brief CS global context that maintains state + * + * @param area_0_start + * pointer to start of malloc-ed memory block for containing area_0 + * + * @param area_0_head + * pointer to area_0 header struct within the memblock. + * - ((cfstore_area_header_t*) area_0)->refcount is the number of + * open handles in the whole of area_0. + * - accessed in app & intr context; hence needs CS protection. + * + * @param area_0_tail + * pointer to address in the sram after the last byte of the last + * KV. Note there can be padding after the area_0_tail to align the + * sram area with flash program_unit (or 1 if SRAM only version) + * to facilitate reading/writing to flash. + * - accessed in app & intr context; hence needs CS protection. + * + * @param area_0_end + * pointer to end area_0 of area_0 memblock (last memory address). + * + * @param rw_area0_lock + * lock used to make CS re-entrant e.g. only 1 flush operation can be + * performed at a time while no readers/writers have handles open + * to KVs. The lock is to protect access to the following: + * - cfstore_ctx_g.area_0_head/cfstore_ctx_g.area_0_tail. Realloc() + * in Delete() and Create() can cause these pointers to change. + * + * @param client_notify_data + * fsm handler functions set a flag for a client notification call + * to be made after fsm handler functions have been completed. This + * block holds the client notification status data for the callback. + * + * @param area_dirty_flag + * flag indicating that the area has been written and therefore is + * dirty with respect to the data persisted to flash. + * + * @expected_blob_size expected_blob_size = area_0_tail - area_0_head + pad + * In the case of reading from flash into sram, this will be be size + * of the flash blob (rounded to a multiple program_unit if not + * already so). + * In the case of writing to flash, this the size of all the KV's + * plus padding so the sram blob size is a multiple of flash + * program_unit. + * - accessed in app & intr context; hence needs CS protection. + */ +typedef struct cfstore_ctx_t +{ + cfstore_list_node_t file_list; + int32_t init_ref_count; + CFSTORE_LOCK rw_area0_lock; + ARM_POWER_STATE power_state; + uint8_t *area_0_head; + uint8_t *area_0_tail; + cfstore_fsm_t fsm; + int32_t status; + + /* client notification data */ + void* client_context; + ARM_CFSTORE_CALLBACK client_callback; + cfstore_client_notify_data_t client_notify_data; + + /* flags */ + uint32_t client_callback_notify_flag : 1; + uint32_t area_dirty_flag : 1; + uint32_t f_reserved0 : 30; + +#if defined CFSTORE_CONFIG_BACKEND_FLASH_ENABLED && CFSTORE_CONFIG_BACKEND_FLASH_ENABLED == 1 + /* flash journal related data */ + FlashJournal_t jrnl; + FlashJournal_Info_t info; + FlashJournal_OpCode_t cmd_code; + uint64_t expected_blob_size; +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ +} cfstore_ctx_t; + + +/* + * @brief file structure for KV, one per open file handle. + * + * @param head + * pointer to head of KV + * + * @param rlocation + * read location of rseek to move + * + * @param read + * indicates file is readable, + * @param writable + * indicates file is readable, + * @param executable + * indicates file is readable, + * @param uvisor_client_box_id + * box id of caller using this file. set on create/open and thereafter used by other methods to check accesses. + * Q: is it safe to store this here? Is it of any value? i.e. a client can change the value + * after cfstore has set it so cfstore cant rely on it being secure. + */ +typedef struct cfstore_file_t +{ + cfstore_list_node_t node; + uint32_t rlocation; + uint32_t wlocation; + uint8_t *head; + ARM_CFSTORE_FMODE flags; +#ifdef YOTTA_CFG_CFSTORE_UVISOR + // todo: add this into mix. + //int uvisor_client_box_id; +#endif + +} cfstore_file_t; + +/* @brief structure used to compose table for mapping flash journal error codes to cfstore error codes */ +typedef struct cfstore_flash_journal_error_code_node +{ + int32_t flash_journal_error_code; + int32_t cfstore_error_code; +} cfstore_flash_journal_error_code_node; + + +/* + * Globals + */ +#ifndef YOTTA_CFG_CONFIG_HARDWARE_MTD_ASYNC_OPS +static ARM_CFSTORE_CAPABILITIES cfstore_caps_g = { .asynchronous_ops = 1, .uvisor_support_enabled = 0 }; +#else +static ARM_CFSTORE_CAPABILITIES cfstore_caps_g = { .asynchronous_ops = YOTTA_CFG_CONFIG_HARDWARE_MTD_ASYNC_OPS, .uvisor_support_enabled = 0 }; +#endif /* YOTTA_CFG_CONFIG_HARDWARE_MTD_ASYNC_OPS */ + +static const ARM_DRIVER_VERSION cfstore_driver_version_g = { .api = ARM_CFSTORE_API_VERSION, .drv = ARM_CFSTORE_DRV_VERSION }; + +#ifndef YOTTA_CFG_CFSTORE_UVISOR +/* uvisor is not being used so instantiate a context */ +static cfstore_ctx_t cfstore_ctx_g = { + .file_list.next = NULL, + .file_list.prev = NULL, + .init_ref_count = 0, + .rw_area0_lock = 0, + .power_state = ARM_POWER_FULL, + .area_0_head = NULL, + .area_0_tail = NULL, + .client_callback = NULL, + .client_context = NULL, + .f_reserved0 = 0, +}; +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +#ifdef YOTTA_CFG_CFSTORE_UVISOR + +/* + * Configure the secure box compartment + */ +static const char const cfstore_uvisor_namespace_root_g[] = "com.arm.mbed."; +// UVISOR_BOX_NAMESPACE("com.arm.mbed.configuration-store"); +// macro: static const char *const __uvisor_box_namespace = box_namespace +static const char *const __uvisor_box_namespace = "com.arm.mbed.configuration-store"; + +/* although the descriptor is empty, the main box descriptor is inherited and added to whats here. */ +static const UvisorBoxAclItem cfstore_acl_list_g[] = { + /* todo: this needs completing with correct data for the secure flash partition above the binary + * + 0xabaadfood = start of secure flash in address map (flash journal partition + 0xbeefbeef = size in bytes of secure flash partition + {(void *) 0xabaadfood, 0xbeefbeef, UVISOR_TACLDEF_PERIPH}, + */ + /* put reference to k64 subfamily reference manual and cmsis k64f target header as to where this comes from */ + {FTFE, sizeof(*FTFE), UVISOR_TACLDEF_PERIPH}, +}; + +/* UVISOR_BOX_CONFIG_CTX(configuration_store, UVISOR_BOX_STACK_SIZE, cfstore_ctx_t); + * + * It would be better to use the following macro: + * UVISOR_BOX_CONFIG(configuration_store, cfstore_acl_list_g, UVISOR_BOX_STACK_SIZE, cfstore_ctx_t); + * rather than the unpacked macro code that follows. + * + * #define __UVISOR_BOX_CONFIG(box_name, acl_list, acl_list_count, stack_size, context_size) \ + * \ + * uint8_t __attribute__((section(".keep.uvisor.bss.boxes"), aligned(32))) \ + * box_name ## _reserved[UVISOR_STACK_SIZE_ROUND(((UVISOR_MIN_STACK(stack_size) + (context_size))*8)/6)]; \ + * \ + * static const __attribute__((section(".keep.uvisor.cfgtbl"), aligned(4))) UvisorBoxConfig box_name ## _cfg = { \ + * UVISOR_BOX_MAGIC, \ + * UVISOR_BOX_VERSION, \ + * UVISOR_MIN_STACK(stack_size), \ + * context_size, \ + * __uvisor_box_namespace, \ + * acl_list, \ + * acl_list_count \ + * }; \ + * \ + * extern const __attribute__((section(".keep.uvisor.cfgtbl_ptr"), aligned(4))) void * const box_name ## _cfg_ptr = &box_name ## _cfg; + * + * However, the macro currently generates warnings that need to be fixed i.e. + * ===================================================================================================================================================================================== + * d:/datastore/public/jobs/yr2016/2247/sdh_dev_10/configuration-store/source/configuration_store.c:490:1: error: initializer element is not constant + * UVISOR_BOX_CONFIG(configuration_store, cfstore_acl_list_g, UVISOR_BOX_STACK_SIZE, cfstore_ctx_t); + * ^ + * d:/datastore/public/jobs/yr2016/2247/sdh_dev_10/configuration-store/source/configuration_store.c:490:1: error: (near initialization for 'configuration_store_cfg.box_namespace') + * In file included from d:/datastore/public/jobs/yr2016/2247/sdh_dev_10/configuration-store/yotta_modules/uvisor-lib/uvisor-lib/uvisor-lib.h:38:0, + * from d:/datastore/public/jobs/yr2016/2247/sdh_dev_10/configuration-store/source/configuration_store.c:27: + * d:/datastore/public/jobs/yr2016/2247/sdh_dev_10/configuration-store/source/configuration_store.c:490:19: warning: 'configuration_store_cfg_ptr' initialized and declared 'extern' + * UVISOR_BOX_CONFIG(configuration_store, cfstore_acl_list_g, UVISOR_BOX_STACK_SIZE, cfstore_ctx_t); + * ^ + * d:/datastore/public/jobs/yr2016/2247/sdh_dev_10/configuration-store/yotta_modules/uvisor-lib/uvisor-lib/box_config.h:74:95: note: in definition of macro '__UVISOR_BOX_CONFIG' + * extern const __attribute__((section(".keep.uvisor.cfgtbl_ptr"), aligned(4))) void * const box_name ## _cfg_ptr = &box_name ## _cfg; + * ^ + * d:/datastore/public/jobs/yr2016/2247/sdh_dev_10/configuration-store/yotta_modules/uvisor-lib/uvisor-lib/box_config.h:57:55: note: in expansion of macro '__UVISOR_BOX_CONFIG_CONTEXT' + * #define __UVISOR_BOX_MACRO(_1, _2, _3, _4, NAME, ...) NAME + * ^ + * d:/datastore/public/jobs/yr2016/2247/sdh_dev_10/configuration-store/yotta_modules/uvisor-lib/uvisor-lib/box_config.h:101:5: note: in expansion of macro 'UVISOR_BOX_CONFIG_ACL' + * UVISOR_BOX_CONFIG_ACL(__VA_ARGS__) + * ^ + * d:/datastore/public/jobs/yr2016/2247/sdh_dev_10/configuration-store/source/configuration_store.c:490:1: note: in expansion of macro 'UVISOR_BOX_CONFIG' + * UVISOR_BOX_CONFIG(configuration_store, cfstore_acl_list_g, UVISOR_BOX_STACK_SIZE, cfstore_ctx_t); + * ^ + * ninja: build stopped: subcommand failed. + * error: command ['ninja'] failed + * ===================================================================================================================================================================================== + * The UVISOR_BOX_CONFIG() macro expands to include the following: + * extern const __attribute__((section(".keep.uvisor.cfgtbl_ptr"), aligned(4))) void * const configuration_store_cfg_ptr = &configuration_store_cfg; + * The extern at the beginning of the line creates a warning when in a c file, and so needs to be removed/fixed. + * There are also many other warnings from the macro expansion which need to be investigated further. + * + * todo: possible investigation: move configuration_store.c -> configuration_store.cpp + */ +uint8_t __attribute__((section(".keep.uvisor.bss.boxes"), aligned(32))) configuration_store_reserved[UVISOR_STACK_SIZE_ROUND(((UVISOR_MIN_STACK(UVISOR_BOX_STACK_SIZE) + (sizeof(cfstore_ctx_t)))*8)/6)]; +static const __attribute__((section(".keep.uvisor.cfgtbl"), aligned(4))) UvisorBoxConfig configuration_store_cfg = { + UVISOR_BOX_MAGIC, + UVISOR_BOX_VERSION, + UVISOR_MIN_STACK(UVISOR_BOX_STACK_SIZE), + sizeof(cfstore_ctx_t), + "com.arm.mbed.configuration-store", //problem using__uvisor_box_namespace defined above so inserting string directly here + cfstore_acl_list_g, + UVISOR_ARRAY_COUNT(cfstore_acl_list_g) +}; + +const __attribute__((section(".keep.uvisor.cfgtbl_ptr"), aligned(4))) void * const configuration_store_cfg_ptr = &configuration_store_cfg; +UVISOR_EXTERN cfstore_ctx_t * const uvisor_ctx; + +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +/* + * client notifier helper function + */ +static void cfstore_client_notify_data_init(cfstore_client_notify_data_t* data, uint32_t opcode, int32_t status, ARM_CFSTORE_HANDLE handle) +{ + memset(data, 0, sizeof(cfstore_client_notify_data_t)); + data->opcode = opcode; + data->status = status; + data->handle = handle; +} + +/* + * cfstore_ctx_t methods + */ + +/* @brief helper function to reset cfstore_ctx_g state when out of memory is received from malloc */ +static void cfstore_ctx_reset(cfstore_ctx_t* ctx) +{ + CFSTORE_ASSERT(ctx!= NULL); + CFSTORE_INIT_LIST_HEAD(&ctx->file_list); + ctx->area_0_head = NULL; + ctx->area_0_tail = NULL; + return; +} + +/* @brief helper function to report whether the initialisation flag has been set in the cfstore_ctx_g */ +static bool cfstore_ctx_is_initialised(cfstore_ctx_t* ctx) +{ + CFSTORE_ASSERT(ctx!= NULL); + return ctx->init_ref_count > 0 ? true : false; +} + +/* @brief helper function to return a pointer to the global cfstore context. */ +static inline cfstore_ctx_t* cfstore_ctx_get(void) +{ +#ifdef YOTTA_CFG_CFSTORE_UVISOR + /* use the secure cfstore_ctx_t struct allocated by uvisor for use */ + return (cfstore_ctx_t*) uvisor_ctx; +#else + /* use the insecure statically allocated data struct */ + return &cfstore_ctx_g; +#endif +} + +/* @brief helper function to compute the size of the sram area in bytes */ +static ARM_CFSTORE_SIZE cfstore_ctx_get_area_len(void) +{ + ARM_CFSTORE_SIZE size = 0; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + size = (ARM_CFSTORE_SIZE) (ctx->area_0_tail - ctx->area_0_head); + return size; +} + +/* @brief helper function to get the program_unit */ +static inline uint32_t cfstore_ctx_get_program_unit(cfstore_ctx_t* ctx) +{ + CFSTORE_ASSERT(ctx!= NULL); +#if defined CFSTORE_CONFIG_BACKEND_FLASH_ENABLED && CFSTORE_CONFIG_BACKEND_FLASH_ENABLED == 1 + return ctx->info.program_unit; +#else + /* the program unit is 1 so byte aligned when no flash backend present */ + (void) ctx; + return 1; +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ + +} + +static inline void cfstore_ctx_client_notify(cfstore_ctx_t* ctx, cfstore_client_notify_data_t* data) +{ + CFSTORE_FENTRYLOG("%s:entered: ctx=%p, ctx->client_callback=%p, ctx->client_context=%p\n", __func__, ctx, ctx->client_callback, ctx->client_context); + if(ctx->client_callback){ + ctx->client_callback(data->status, (ARM_CFSTORE_OPCODE) data->opcode, ctx->client_context, data->handle); + } + return; +} + +/* + * CFSTORE_YOTTA_CFG_CFSTORE_SRAM_ADDR + * client can supply a SRAM slab address and size for + * CFSTORE internal use. This is a default addr + * for development use. Should be defined by client + * CFSTORE_YOTTA_CFG_CFSTORE_SRAM_SIZE + * size of sram area. Should be define by client + */ +#ifndef CFSTORE_YOTTA_CFG_CFSTORE_SRAM_ADDR +/* if the client doesnt provide a memory slab then CFSTORE uses realloc internally*/ + +#ifndef CFSTORE_DEBUG +#define CFSTORE_FREE free +#define CFSTORE_MALLOC malloc +#define CFSTORE_REALLOC realloc +#else + +static uint32_t cfstore_malloc_size_g = 0; +#define CFSTORE_MALLOC malloc + +static void* CFSTORE_REALLOC(void *ptr, size_t size) +{ + void* mem; + + mem = realloc(ptr, size); + CFSTORE_TP(CFSTORE_TP_MEM, "%s:ptr=%p, mem=%p, old_size=%u, new_size=%u.\n", __func__, ptr, mem, (int) cfstore_malloc_size_g, (int) size); + cfstore_malloc_size_g = size; + return mem; +} + +static void CFSTORE_FREE(void *ptr) +{ + free(ptr); + CFSTORE_TP(CFSTORE_TP_MEM, "%s:ptr=%p, old_size=%u, new_size=%u.\n", __func__, ptr, (int) cfstore_malloc_size_g, 0); + cfstore_malloc_size_g = 0; + return; +} +#endif /* CFSTORE_DEBUG */ + +/* memory tracking */ + +#else +#define CFSTORE_FREE CFSTORE_ASSERT(0) +#define CFSTORE_MALLOC CFSTORE_ASSERT(0) +#define CFSTORE_REALLOC cfstore_realloc + + + +/* function to realloc from a client provided memory slab + * size = new size of area used by sram + * ptr is always head of slab + * + * The cfstore_realloc() function changes the size of the memory + * block pointed to by ptr to size bytes, backed by the client + * provided memory slab. The contents will be unchanged in the + * range from the start of the region up to the minimum of the + * old and new sizes. If the new size is larger than the old size, + * the added memory will not be initialized. + * + * ptr + * ptr should be set to null on the first call to this function and + * for size > 0 && size <= CFSTORE_YOTTA_CFG_CFSTORE_SRAM_SIZE + * CFSTORE_YOTTA_CFG_CFSTORE_SRAM_ADDR will be returned. + * On subsequent calls, ptr must have been returned by an earlier + * call to this function. + * + * size + * if size is equal to zero, and ptr is not NULL, then the call is + * equivalent to reseting the memory area and NULL will be returned. + */ +void *cfstore_realloc(void *ptr, ARM_CFSTORE_SIZE size) +{ + static uint8_t *cfstore_sram_head = NULL; + static uint8_t *cfstore_sram_tail = NULL; + + if(size > 0) { + if(size <= CFSTORE_YOTTA_CFG_CFSTORE_SRAM_SIZE) { + if(ptr == NULL) { + memset(CFSTORE_YOTTA_CFG_CFSTORE_SRAM_ADDR, 0, CFSTORE_YOTTA_CFG_CFSTORE_SRAM_SIZE); + cfstore_sram_head = CFSTORE_YOTTA_CFG_CFSTORE_SRAM_ADDR; + } + cfstore_sram_tail = cfstore_sram_head + size; + return (void*) cfstore_sram_head; + } + /* requested size is too big so fail the operation by setting + * head/tail to NULL */ + } + /* size == 0 => reset */ + cfstore_sram_head = NULL; + cfstore_sram_tail = NULL; + return (void*) cfstore_sram_head; +} + +#endif /* CFSTORE_YOTTA_CFG_CFSTORE_SRAM_ADDR */ + +/* + * Platform Specific Function Implementations + */ +#ifdef TARGET_LIKE_FRDM_K64F_GCC +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; + (void) tag; + CFSTORE_DBGLOG("%s:before critical_section_exit()(lock=%lu)\n", tag, *lock); + (*lock)--; + /* todo: put mbedosv3++ critical section exit here */ + CFSTORE_DBGLOG("%s:after critical_section_exit()(lock=%lu)\n", tag, *lock); +} + +static inline void cfstore_critical_section_lock(CFSTORE_LOCK* lock, const char* tag) +{ + (void) lock; + (void) tag; + CFSTORE_DBGLOG("%s:before critical_section_enter()(lock=%lu)\n", tag, *lock); + /* todo: put mbedosv3++ critical section enter here */ + (*lock)++; + CFSTORE_DBGLOG("%s:after critical_section_enter()(lock=%lu)\n", tag, *lock); +} + +static CFSTORE_INLINE int32_t cfstore_hkvt_refcount_dec(cfstore_area_hkvt_t* hkvt, uint8_t *refcount) +{ + cfstore_area_header_t *hdr = (cfstore_area_header_t*) hkvt->head; + + /* todo: put mbedosv3++ critical section enter here */ + hdr->refcount--; + if(refcount) *refcount = hdr->refcount; + /* todo: put mbedosv3++ critical section exit here */ + return ARM_DRIVER_OK; +} + +static CFSTORE_INLINE int32_t cfstore_hkvt_refcount_inc(cfstore_area_hkvt_t* hkvt, uint8_t *refcount) +{ + int32_t ret = ARM_CFSTORE_DRIVER_ERROR_HANDLE_COUNT_MAX; + cfstore_area_header_t *hdr = (cfstore_area_header_t*) hkvt->head; + + /* todo: put mbedosv3++ critical section enter here */ + if(hdr->refcount < CFSTORE_HKVT_REFCOUNT_MAX) + { + hdr->refcount++; + if(refcount) *refcount = hdr->refcount; + ret = ARM_DRIVER_OK; + } + /* todo: put mbedosv3++ critical section exit here */ + return ret; +} +#endif /* TARGET_LIKE_FRDM_K64F_GCC */ + + +#ifdef 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); } + +static CFSTORE_INLINE int32_t cfstore_hkvt_refcount_dec(cfstore_area_hkvt_t* hkvt, uint8_t *refcount) +{ + cfstore_area_header_t *hdr = (cfstore_area_header_t*) hkvt->head; + uint32_t __refcount; + + __refcount =__sync_fetch_and_sub(&hdr->refcount, 1); + if(refcount) *refcount = __refcount; + return ARM_DRIVER_OK; +} + +static CFSTORE_INLINE int32_t cfstore_hkvt_refcount_inc(cfstore_area_hkvt_t* hkvt, uint8_t *refcount) +{ + int32_t ret = ARM_CFSTORE_DRIVER_ERROR_HANDLE_COUNT_MAX; + uint32_t __refcount; + cfstore_area_header_t *hdr = (cfstore_area_header_t*) hkvt->head; + + if( (__refcount = __sync_fetch_and_add(&hdr->refcount, 1)) < CFSTORE_LOCK_REFCOUNT_MAX) { + if(refcount) *refcount = __refcount; + ret = ARM_DRIVER_OK; + } else { + /* maximum count reach, back down and return error*/ + __sync_fetch_and_sub(&hdr->refcount, 1); + } + return ret; +} + +#endif /* TARGET_LIKE_X86_LINUX_NATIVE */ + + +/* + * security/permissions helper functions + */ +#ifdef noCFG_CFSTORE_UVISOR + +/** + * @brief check that a client (cfstore-uvisor client box) is the "owner" of the + * KV. Owner means the client that can create or created the KV. This is + * determined by the clients namespace and whether the KV path name falls + * within that name space + * @param key_name + * the name of the KV being created. + * the validation that the key_name is composed of permissible chars is + * carried out before this function is called. + * @note + * Conceptually, cfstore supports the following KV path namespaces: + * - com.arm.mbed. + * - guids of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx where x is a hex digit. + * + * In the cfstore implementation, explicit checking of the structure of the + * namespace string is not required. Cfstore only need enforce that: + * the root of the KV pathname == cfstore client uvisor namespace. + */ +static int32_t cfstore_uvisor_is_client_kv_owner(char* key_name, int32_t* cfstore_uvisor_box_id) +{ + int32_t calling_box_id; + int32_t ret; + /* We store the calling_box_namespace on our stack, lest somebody else modify it. */ + char calling_box_namespace[UVISOR_MAX_BOX_NAMESPACE_LENGTH]; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + memset(calling_box_namespace, 0, sizeof(calling_box_namespace)); + /* Get the ID of the box that called this box through the most recent secure gateway. */ + calling_box_id = uvisor_box_id_caller(); + if(calling_box_id < 0){ + CFSTORE_ERRLOG("%s: Error: uvisor uvisor_box_id_caller() returned invalid id (calling_box_id=%" PRId32 "\n", __func__, calling_box_id); + return ARM_CFSTORE_DRIVER_ERROR_UVISOR_BOX_ID; + } + if(cfstore_uvisor_box_id){ + *cfstore_uvisor_box_id = calling_box_id; + } + if(calling_box_id == 0){ + /* the cfstore uvisor client is the main box. + * main box is not allowed to create a key as a client is only permitted to create KVs in their namespace. */ + CFSTORE_ERRLOG("%s: Error: uvisor box id identifies cfstore client cannot create KVs (calling_box_id=%" PRId32 "\n", __func__, calling_box_id); + return ARM_CFSTORE_DRIVER_ERROR_UVISOR_BOX_ID; + } + /* Copy the name of the calling box to our stack. */ + ret = uvisor_box_namespace(calling_box_id, calling_box_namespace, sizeof(calling_box_namespace)); + if(ret < 0){ + /* error */ + CFSTORE_ERRLOG("%s: Error: unable to recover uvisor box namespace\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_UVISOR_NAMESPACE; + } + /* check the cfstore client uvisor box namespace is non-trivial */ + if(strlen(calling_box_namespace) == 0){ + CFSTORE_ERRLOG("%s: Error: uvisor box namespace is zero length\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_UVISOR_NAMESPACE; + } + /* check that the key name is within the root domain namespace */ + if(strncmp(calling_box_namespace, key_name, sizeof(calling_box_namespace)) != 0) { + /* The key_name does not fall within the cfstore-uvisor client namespace and therefore the create is not allowed */ + CFSTORE_ERRLOG("%s: Error: key name (%s) is not permitted to be created within client uvisor box namespace (%s) of cfstore client\n", __func__, key_name, calling_box_namespace); + return ARM_CFSTORE_DRIVER_ERROR_NO_PERMISSIONS; + } + /* We've passed all our checks, so we allow the calling box. */ + return ARM_DRIVER_OK; +} +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ + +/** + * @brief check that the cfstore client (caller, which is a uvisor box) + * is only trying to access its own namespace. + * + * @note This function is the cfstore equivalent of "is_calling_box_allowed" + */ +static int32_t cfstore_uvisor_security_context_prefix_check(const char* key_name) +{ + /*todo: implement : A client uvisor security context should exist with + * a security_prefix_name that matches the first part of the + * key_name. Make sure this is the case. */ + + // if the caller is the main box then deny access, as only secure uvisor boxes + // are permitted to access cfstore. + + // get box_id of caller + // get namespace of caller + // if the keyname is in the namespace then permit, otherwise deny + + (void) key_name; + return ARM_DRIVER_OK; +} + +/* @brief check that a client (cfstore-uvisor client box) is the "owner" of the + * KV (wrapper). see cfstore_uvisor_is_client_kv_owner() for more details. + */ +static int32_t cfstore_is_client_kv_owner(const char* key_name, int32_t* cfstore_uvisor_box_id) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); +/* + #ifdef YOTTA_CFG_CFSTORE_UVISOR + return cfstore_uvisor_is_client_kv_owner(key_name, cfstore_uvisor_box_id); +#else + return ARM_DRIVER_OK; +#endif +*/ + (void) key_name; + (void) cfstore_uvisor_box_id; + return ARM_DRIVER_OK; +} + +/* @brief helper function to determine whether this client can close a given KV */ +static bool cfstore_is_kv_client_closable(cfstore_file_t* file) +{ + /* todo: integrate with uvisor to get boxId (security prefix name) + * - check the kv key_name prefix matches the security context to determine whether client is + * allowed to close the given key_name. + */ + /* until can implement this functionality, assume client can close KV */ + (void) file; + return true; +} + +/* @brief helper function to determine whether this client can delete a given KV */ +static bool cfstore_is_kv_client_deletable(cfstore_file_t* file) +{ + /* todo: integrate with uvisor to get boxId (security prefix name) + * - check the kv key_name prefix matches the security context to determine whether client is + * allowed to delete the given key_name. + */ + /* until can implement this functionality, assume client can delete KV */ + (void) file; + return true; +} + +#ifdef YOTTA_CFG_CFSTORE_UVISOR_to_debug +/* @brief helper function to determine whether this cfstore-uvisor client box can read a given KV */ +static bool cfstore_is_kv_client_readable(cfstore_area_hkvt_t* hkvt) +{ + bool bret = false; + int32_t ret = ARM_DRIVER_ERROR; + char key_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + uint8_t key_name_len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + cfstore_area_header_t *hdr = (cfstore_area_header_t*) hkvt->head; + + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + memset(key_name, 0, key_name_len); + ret = cfstore_get_key_name_ex(hkvt, key_name, &key_name_len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: cfstore_get_key_name_ex() returned error.\n", __func__); + return bret; + } + ret = cfstore_is_client_kv_owner(key_name, NULL); + if(ret == ARM_DRIVER_OK){ + /* cfstore-usvisor client box is the "owner" of the key */ + bret = hdr->perm_owner_read; + } else { + /* cfstore-usvisor client box is not the "owner" of the key i.e. is the "other" */ + bret = hdr->perm_other_read; + } + return bret; +} + +/* @brief helper function to determine whether this client can write a given KV */ +static bool cfstore_is_kv_client_writable(cfstore_area_hkvt_t* hkvt) +{ + bool bret = false; + int32_t ret = ARM_DRIVER_ERROR; + char key_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + uint8_t key_name_len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + cfstore_area_header_t *hdr = (cfstore_area_header_t*) hkvt->head; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + memset(key_name, 0, key_name_len); + ret = cfstore_get_key_name_ex(hkvt, key_name, &key_name_len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: cfstore_get_key_name_ex() returned error.\n", __func__); + return bret; + } + ret = cfstore_is_client_kv_owner(key_name, NULL); + if(ret == ARM_DRIVER_OK){ + /* cfstore-usvisor client box is the "owner" of the key */ + bret = hdr->perm_owner_write; + } else { + /* cfstore-usvisor client box is not the "owner" of the key i.e. is the "other" */ + bret = hdr->perm_other_write; + } + return bret; +} + +/* @brief helper function to determine whether this client can execute a given KV */ +static bool cfstore_is_kv_client_executable(cfstore_area_hkvt_t* hkvt) +{ + bool bret = false; + int32_t ret = ARM_DRIVER_ERROR; + char key_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + uint8_t key_name_len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + cfstore_area_header_t *hdr = (cfstore_area_header_t*) hkvt->head; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + memset(key_name, 0, key_name_len); + ret = cfstore_get_key_name_ex(hkvt, key_name, &key_name_len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: cfstore_get_key_name_ex() returned error.\n", __func__); + return bret; + } + ret = cfstore_is_client_kv_owner(key_name, NULL); + if(ret == ARM_DRIVER_OK){ + /* cfstore-usvisor client box is the "owner" of the key */ + bret = hdr->perm_owner_execute; + } else { + /* cfstore-usvisor client box is not the "owner" of the key i.e. is the "other" */ + bret = hdr->perm_other_execute; + } + return bret; +} +#endif // YOTTA_CFG_CFSTORE_UVISOR_to_debug + +/* @brief helper function to determine whether this client can read a given KV */ +static bool cfstore_is_kv_client_readable(cfstore_area_hkvt_t* hkvt) +{ + /* todo: integrate with uvisor to get boxId (security prefix name) + * - check the kv key_name prefix matches the security context to determine whether client is + * owner or other. + * - if(owner) + * { + * // client is owner of kv + * if( ((cfstore_area_header_t*)(hkvt->head))->perm_owner_read == true) { + * return true; + * } + * } else { + * // client is other + * if( ((cfstore_area_header_t*)(hkvt->head))->perm_other_read == true) { + * return true; + * } + * return false; + */ + /* until can implement this functionality, assume client has read access to KV */ + (void) hkvt; + return true; +} + +/* @brief helper function to determine whether this client can write a given KV */ +static bool cfstore_is_kv_client_writable(cfstore_area_hkvt_t* hkvt) +{ + cfstore_area_header_t *hdr = (cfstore_area_header_t*) hkvt->head; + + /* todo: integrate with uvisor to get boxId (security prefix name) + * - check the kv key_name prefix matches the security context to determine whether client is + * owner or other. + * - if(owner) + * { + * // client is owner of kv + * if( ((cfstore_area_header_t*)(hkvt->head))->perm_owner_write == true) { + * return true; + * } + * } else { + * // client is other + * if( ((cfstore_area_header_t*)(hkvt->head))->perm_other_write == true) { + * return true; + * } + * return false; + */ + /* until can implement this functionality, assume client has write access to KV */ + + /* check that the owner has write permission */ + return hdr->perm_owner_write; +} + +/* @brief helper function to determine whether this client can execute a given KV */ +static bool cfstore_is_kv_client_executable(cfstore_area_hkvt_t* hkvt) +{ + /* todo: integrate with uvisor to get boxId (security prefix name) + * - check the kv key_name prefix matches the security context to determine whether client is + * owner or other. + * - if(owner) + * { + * // client is owner of kv + * if( ((cfstore_area_header_t*)(hkvt->head))->perm_owner_execute == true) { + * return true; + * } + * } else { + * // client is other + * if( ((cfstore_area_header_t*)(hkvt->head))->perm_other_execute == true) { + * return true; + * } + * return false; + */ + /* until can implement this functionality, assume client has execute access to KV */ + (void) hkvt; + return true; +} + + +/* + * flags helper function + */ +static bool cfstore_acl_is_default(ARM_CFSTORE_ACCESS_CONTROL_LIST acl) +{ + if( acl.perm_owner_read == false && + acl.perm_owner_write == false && + acl.perm_owner_execute == false && + acl.perm_other_read == false && + acl.perm_other_write == false && + acl.perm_other_execute == false ) + { + /* flags are set to indicate "adopt some meaningful default behaviour" */ + return true; + } + return false; +} + +/* + * flags helper function + */ +static bool cfstore_flags_is_default(ARM_CFSTORE_FMODE flags) +{ + if( flags.read == 0 && + flags.write == 0 && + flags.continuous == 0 && + flags.flush_on_close == 0 && + flags.lazy_flush == 0 && + flags.storage_detect == 0 ) + { + /* flags are set to indicate "adopt some meaningful default behaviour" */ + return true; + } + return false; +} + +static CFSTORE_INLINE bool cfstore_hkvt_get_flags_delete(cfstore_area_hkvt_t *hkvt) +{ + return ((cfstore_area_header_t*) hkvt->head)->flags.delete; +} + +static CFSTORE_INLINE void cfstore_hkvt_set_flags_delete(cfstore_area_hkvt_t *hkvt, bool flag) +{ + CFSTORE_ASSERT(hkvt != NULL); + ((cfstore_area_header_t*) hkvt->head)->flags.delete = flag; +} + + +/* + * struct cfstore_area_hkvt_t helper operations + */ +static CFSTORE_INLINE uint8_t cfstore_hkvt_get_key_len(cfstore_area_hkvt_t* hkvt) +{ + cfstore_area_header_t *header; + CFSTORE_ASSERT(hkvt != NULL); + header = (cfstore_area_header_t*) hkvt->head; + return header->klength; +} + +static CFSTORE_INLINE uint32_t cfstore_hkvt_get_value_len(cfstore_area_hkvt_t* hkvt) +{ + cfstore_area_header_t *header; + CFSTORE_ASSERT(hkvt != NULL); + header = (cfstore_area_header_t*) hkvt->head; + return header->vlength; +} + +static CFSTORE_INLINE ARM_CFSTORE_SIZE cfstore_hkvt_get_size(cfstore_area_hkvt_t* hkvt) +{ + ARM_CFSTORE_SIZE kv_size = 0; + + kv_size += sizeof(cfstore_area_header_t); + kv_size += cfstore_hkvt_get_key_len(hkvt); + kv_size += cfstore_hkvt_get_value_len(hkvt); + return kv_size; +} + +static CFSTORE_INLINE void cfstore_hkvt_init(cfstore_area_hkvt_t* hkvt) +{ + memset(hkvt, 0, sizeof(cfstore_area_hkvt_t)); +} + + +static CFSTORE_INLINE bool cfstore_hkvt_is_valid(cfstore_area_hkvt_t *hkvt, uint8_t *area_0_tail) +{ + if(hkvt->head && hkvt->head != area_0_tail && hkvt->key && hkvt->value && hkvt->tail) { + return true; + } + return false; +} + +static CFSTORE_INLINE uint32_t cfstore_hkvt_set_value_len(cfstore_area_hkvt_t* hkvt, uint32_t value_len) +{ + uint32_t vlength; + cfstore_area_header_t *hdr; + CFSTORE_ASSERT(hkvt != NULL); + hdr = (cfstore_area_header_t*) hkvt->head; + vlength = hdr->vlength; + hdr->vlength = value_len; + return vlength; +} + +/* @brief helper function to detect if there are any KV's stored in the sram area */ +static bool cfstore_area_has_hkvt(void) +{ + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + /* head and tail pointer equal means there are no KVs stored */ + if(ctx->area_0_head == ctx->area_0_tail){ + /* there are no KV's stored*/ + return false; + } + return true; +} + + +/* @brief helper function to get the first KV in the sram area */ +static cfstore_area_hkvt_t cfstore_get_hkvt_from_head_ptr(uint8_t* head) +{ + cfstore_area_hkvt_t hkvt; + + CFSTORE_ASSERT(head != NULL); + memset((void*) &hkvt, 0, sizeof(hkvt)); + hkvt.head = head; + hkvt.key = hkvt.head + sizeof(cfstore_area_header_t); + hkvt.value = hkvt.key + ((cfstore_area_header_t*) hkvt.head)->klength; + hkvt.tail = hkvt.value + ((cfstore_area_header_t*) hkvt.head)->vlength; + return hkvt; +} + + +/* @brief helper function to convert a opaque handle to a struct cfstore_area_hkvt_t */ +static cfstore_area_hkvt_t cfstore_get_hkvt(ARM_CFSTORE_HANDLE hkey) +{ + cfstore_file_t* file = (cfstore_file_t*) hkey; + return cfstore_get_hkvt_from_head_ptr((uint8_t*) file->head); +} + + +/* @brief helper function to convert a opaque handle to a struct cfstore_area_hkvt_t */ +static int32_t cfstore_get_head_hkvt(cfstore_area_hkvt_t* hkvt) +{ + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + CFSTORE_ASSERT(hkvt != NULL); + if(!cfstore_area_has_hkvt()){ + CFSTORE_TP(CFSTORE_TP_VERBOSE1, "%s:CFSTORE has no KVs\n", __func__); + memset((void*) hkvt, 0, sizeof(cfstore_area_hkvt_t)); + return ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND; + } + + CFSTORE_TP(CFSTORE_TP_VERBOSE1, "%s:CFSTORE has KVs\n", __func__); + *hkvt = cfstore_get_hkvt_from_head_ptr(ctx->area_0_head); + return ARM_DRIVER_OK; +} + + +/* @brief helper function to walk the sram area from the previous hkvt to + * the next hkvt. + * @param prev + * pointer to previous hkvt. If null then the search is started + * from the beginning of the sram area. + * @param next + * pointer to next hkvt for which the pointers need calculating. + */ +static int32_t cfstore_get_next_hkvt(cfstore_area_hkvt_t* prev, cfstore_area_hkvt_t* next) +{ + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + CFSTORE_ASSERT(prev != NULL); + CFSTORE_ASSERT(next != NULL); + + if(prev->tail == ctx->area_0_tail){ + CFSTORE_TP(CFSTORE_TP_VERBOSE1, "%s:reached the end of the list. return NULL entry\n", __func__); + memset((void*) next, 0, sizeof(cfstore_area_hkvt_t)); + return ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND; + } + /* use the prev tail pointer to find the next head pointer */ + *next = cfstore_get_hkvt_from_head_ptr((uint8_t*) prev->tail); + return ARM_DRIVER_OK; +} + + +/* + * Flash support functions + */ + +static CFSTORE_INLINE void cfstore_hkvt_dump(cfstore_area_hkvt_t* hkvt, const char* tag); + +/* set the tail pointer */ +static int32_t cfstore_flash_set_tail(void) +{ + int32_t ret = ARM_DRIVER_ERROR; + uint8_t* ptr = NULL; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + uint8_t* tail = NULL; + cfstore_area_hkvt_t hkvt; + + /* walk the area to find the last KV */ + CFSTORE_FENTRYLOG("%s:entered: \n", __func__); + CFSTORE_ASSERT(ctx != NULL); + cfstore_hkvt_init(&hkvt); + ptr = ctx->area_0_head; + /* ctx->area_0_tail has been set to the end of the sram area allocated, but this is now refined so + * as to point to the end of the last KV */ + tail = ctx->area_0_tail; + while(ptr < tail) { + hkvt = cfstore_get_hkvt_from_head_ptr(ptr); + cfstore_hkvt_dump(&hkvt, __func__); + /* when the length between the hkvt.tail and tail (set to the end of the area including padding) + * is less than the minimum KV length then we have found the last KV, and can set the + * area_0_tail correctly to the end of the last KV */ + if((uint32_t)(tail - hkvt.tail) < sizeof(cfstore_area_header_t)){ + /* ptr is last KV in area as there isn't space for another header */ + ctx->area_0_tail = hkvt.tail; + ret = ARM_DRIVER_OK; + break; + } + ptr = hkvt.tail; + } + return ret; +} + +#if defined CFSTORE_CONFIG_BACKEND_FLASH_ENABLED && CFSTORE_CONFIG_BACKEND_FLASH_ENABLED == 1 + +/* + * flash helper functions + */ + +/* @brief table for mapping flash journal error codes to equivalent cfstore error codes */ +static cfstore_flash_journal_error_code_node cfstore_flash_journal_error_code_map[]= +{ + { JOURNAL_STATUS_OK, ARM_DRIVER_OK}, + { JOURNAL_STATUS_ERROR, ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_ERROR}, + { JOURNAL_STATUS_BUSY, ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_BUSY}, + { JOURNAL_STATUS_TIMEOUT, ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_TIMEOUT}, + { JOURNAL_STATUS_UNSUPPORTED, ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_UNSUPPORTED}, + { JOURNAL_STATUS_PARAMETER, ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_PARAMETER}, + { JOURNAL_STATUS_BOUNDED_CAPACITY, ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_BOUNDED_CAPACITY}, + { JOURNAL_STATUS_STORAGE_API_ERROR, ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_STORAGE_API_ERROR}, + { JOURNAL_STATUS_STORAGE_IO_ERROR, ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_STORAGE_IO_ERROR}, + { JOURNAL_STATUS_NOT_INITIALIZED, ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_NOT_INITIALIZED}, + { JOURNAL_STATUS_EMPTY, ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_EMPTY}, + { JOURNAL_STATUS_SMALL_LOG_REQUEST, ARM_CFSTORE_DRIVER_ERROR_JOURNAL_STATUS_SMALL_LOG_REQUEST}, + { CFSTORE_SENTINEL, CFSTORE_SENTINEL} +}; + +static int32_t cfstore_flash_map_error(int32_t flash_journal_status_code) +{ + cfstore_flash_journal_error_code_node* node = cfstore_flash_journal_error_code_map; + + while(node->flash_journal_error_code != (int32_t) CFSTORE_SENTINEL) + { + if(flash_journal_status_code == node->flash_journal_error_code) + { + return node->cfstore_error_code; + } + } + return ARM_CFSTORE_DRIVER_ERROR_INTERNAL; +} + + +/* @brief Callback registered with flash journal for async operation + * completion notifications. + * + * @note The callback is called at interrupt context. + * The critical section to used police access to context variables + * modified by both the interrupt and application context processing. + * The interrupt context prevents application context from running and + * hence its only necessary to use the critical_section_xxx in the + * application execution context. + * + * In flash journal async mode, when: + * - a FlashJournal_xxx() function has been invoked, and + * - before the async completion has been received and processed + * the application context code should alway co-ordinate access to + * context variables modified by interrupt and application context + * by use of the critical_section_xxx. + */ +static void cfstore_flash_journal_callback(int32_t status, FlashJournal_OpCode_t cmd_code) +{ + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + 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_INITIALIZE: + ctx->fsm.event = cfstore_fsm_event_init_done; + break; + case FLASH_JOURNAL_OPCODE_READ_BLOB: + ctx->fsm.event = cfstore_fsm_event_read_done; + break; + case FLASH_JOURNAL_OPCODE_LOG_BLOB: + ctx->fsm.event = cfstore_fsm_event_log_done; + break; + case FLASH_JOURNAL_OPCODE_COMMIT: + ctx->fsm.event = cfstore_fsm_event_commit_done; + break; + case FLASH_JOURNAL_OPCODE_RESET: + ctx->fsm.event = cfstore_fsm_event_reset_done; + break; + case FLASH_JOURNAL_OPCODE_GET_INFO: + default: + CFSTORE_ERRLOG("%s:Error: notification of unsupported cmd_code event (status=%d, cmd_code=%d)\n", __func__, (int) status, (int) cmd_code); + return; + } + ctx->status = status; + ctx->cmd_code = cmd_code; + cfstore_fsm_state_handle_event(&ctx->fsm, ctx->fsm.event, (void*) ctx); + return; +} + + +/* @brief */ +static int32_t cfstore_fsm_stop_on_entry(void* context) +{ + cfstore_ctx_t* ctx = (cfstore_ctx_t*) context; + + /* reset fsm state */ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + CFSTORE_ASSERT(ctx->fsm.state == cfstore_fsm_state_stopped); + + ctx->fsm.event = cfstore_fsm_event_max; + ctx->cmd_code = (FlashJournal_OpCode_t)((int) FLASH_JOURNAL_OPCODE_RESET+1); + return ARM_DRIVER_OK; +} + +/* static int32_t cfstore_fsm_stop_on_exit(void* context) {(void) context; }*/ + + +/* @brief fsm on entry function for the initing state + * @note + * flash journal sync mode: (see async mode notes) + * flash journal async mode: + * This is typically called in app context (not intr context) for both flash + * journal sync and asyc modes. There are no outstanding async requests + * so it cannot be interrupted, and therefore doesnt need CS protection. + */ +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); + CFSTORE_FENTRYLOG("%s:here\n", __func__); + CFSTORE_TP(CFSTORE_TP_FSM, "%s:FlashJournal_initialize ret=%" PRId32 "\n", __func__, ret); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to initialize flash journaling layer (ret=%" PRId32 ")\n", __func__, 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_INITIALIZE); + } + return ret; +} + + +/* @brief fsm initing state handler function + * @note + * flash journal sync mode: + * CS protection not required as there are no callbacks. + * flash journal async mode: + * This is typically called at intr context (not app context) when flash + * journal invokes the callback handler for FLASH_JOURNAL_OPCODE_INITIALIZE + * Hence as running at intr level, no CS protection is required. + */ +static int32_t cfstore_fsm_initing(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_initing); + CFSTORE_ASSERT(ctx->cmd_code == FLASH_JOURNAL_OPCODE_INITIALIZE); + + /* only change state if status > 0*/ + if(ctx->status > 0){ + ret = cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_reading, ctx); + } else if(ctx->status < 0) { + CFSTORE_ERRLOG("%s:Error: failed to initialize flash journaling layer (ret=%" PRId32 ")\n", __func__, ctx->status); + cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_stopped, ctx); + } + return ret; +} + +/* static int32_t cfstore_fsm_init_on_exit(void* context) */ + + +/* @brief fsm on entry function for the reading state + * @note + * flash journal sync mode: + * CS protection not required as there are no callbacks. + * flash journal async mode: + * This is typically called at intr context (not app context) when flash + * journal invokes the callback handler for FLASH_JOURNAL_OPCODE_INITIALIZE + * Hence as running at intr level, no CS protection is required. + */ +static int32_t cfstore_fsm_read_on_entry(void* context) +{ + uint8_t* ptr = NULL; + int32_t ret = 0; + FlashJournal_Status_t status = JOURNAL_STATUS_ERROR; + cfstore_ctx_t* ctx = (cfstore_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + CFSTORE_ASSERT(ctx != NULL); + /* FlashJournal_getInfo() is synchronous */ + status = FlashJournal_getInfo(&ctx->jrnl, &ctx->info); + if(status < JOURNAL_STATUS_OK){ + CFSTORE_TP(CFSTORE_TP_FSM, "%s:Error: failed get journal info (status=%d)\n", __func__, (int) status); + /* move to ready state. cfstore client is expected to Uninitialize() before further calls */ + cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_ready, ctx); + ret = ARM_CFSTORE_DRIVER_ERROR_INTERNAL; + goto out; + + } + if(ctx->info.sizeofJournaledBlob > 0) + { + /* setup the expected blob size for writing + * This is a multiple of program unit so the write doesnt fail due to unaligned log */ + ctx->expected_blob_size = ctx->info.sizeofJournaledBlob; + if(ctx->expected_blob_size % ctx->info.program_unit > 0){ + ctx->expected_blob_size += (ctx->info.program_unit - (ctx->info.sizeofJournaledBlob % ctx->info.program_unit)); + } + /* grow the area by the size of the stored blob */ + ptr = (uint8_t*) CFSTORE_REALLOC((void*) ctx->area_0_head, ctx->expected_blob_size); + if(ptr == NULL){ + CFSTORE_ERRLOG("%s:Error: unable to allocate memory (size=%lu)\n", __func__, (long unsigned int) ctx->info.sizeofJournaledBlob); + cfstore_ctx_reset(ctx); + ret = ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY; + /* move to ready state. cfstore client is expected to Uninitialize() before further calls */ + cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_ready, ctx); + goto out; + } + memset(ptr, 0, ctx->expected_blob_size); + if(ptr != ctx->area_0_head){ + CFSTORE_TP(CFSTORE_TP_FSM, "%s:cfstore_ctx_g.area_0_head pointer changed (cfstore_ctx_g.area_0_head=%p, ptr=%p)\n", __func__, ctx->area_0_head, ptr); + ctx->area_0_head = ptr; + ctx->area_0_tail = ctx->area_0_head + ctx->info.sizeofJournaledBlob; + } + ret = FlashJournal_read(&ctx->jrnl, (void*) ctx->area_0_head, ctx->info.sizeofJournaledBlob); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to initialize flash journaling layer (ret=%d)\n", __func__, (int) ret); + /* move to ready state. cfstore client is expected to Uninitialize() before further calls */ + cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_ready, ctx); + goto out; + } else if(ret > 0){ + /* read has completed synchronously*/ + CFSTORE_TP(CFSTORE_TP_FSM, "%s:debug:ret > 0: (ret=%d)\n", __func__, (int) ret); + cfstore_flash_journal_callback(ret, FLASH_JOURNAL_OPCODE_READ_BLOB); + ret = ctx->status; + goto out; + } + /* keep lock and wait for async callback */ + } else { + /* there is no blob, move to next state. need a +ve status value to indicate async completion + * to the fsm reading state handler. use CFSTORE_FLASH_AREA_SIZE_MIN for this value */ + ctx->expected_blob_size = CFSTORE_FLASH_AREA_SIZE_MIN; + status = (FlashJournal_Status_t) CFSTORE_FLASH_AREA_SIZE_MIN; + cfstore_flash_journal_callback(status, FLASH_JOURNAL_OPCODE_READ_BLOB); + ret = ctx->status; + goto out; + } +out: + return ret; +} + + +/* @brief fsm handler when in reading state */ +static int32_t cfstore_fsm_reading(void* context) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_ctx_t* ctx = (cfstore_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + CFSTORE_ASSERT(ctx->fsm.state == cfstore_fsm_state_reading); + CFSTORE_ASSERT(ctx->cmd_code == FLASH_JOURNAL_OPCODE_READ_BLOB); + if(ctx->status > 0) + { + if(ctx->status > (int32_t) CFSTORE_FLASH_AREA_SIZE_MIN) + { + CFSTORE_TP(CFSTORE_TP_FSM, "%s:debug:ctx->status > (int32_t) CFSTORE_FLASH_AREA_SIZE_MIN:\n", __func__); + /* check the correct amount of data was read, which is the status code */ + if(ctx->status == (int32_t) ctx->expected_blob_size) + { + /* now have to allow for the fact that there may have been some padding + * at the end of the last _log() to flash, so the read back area may have + * padding at the end, and the tail_pointer needs to not point to the + * end where the padding is located, but to the end of the last KV. + */ + ret = cfstore_flash_set_tail(); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: cfstore_flash_set_tail() failed (ret=%" PRId32 ")\n", __func__, ret); + /* move to ready state. cfstore client is expected to Uninitialize() before further calls */ + cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_ready, ctx); + memset(&ctx->info, 0, sizeof(ctx->info)); + goto out; + } + /* clear info data */ + memset(&ctx->info, 0, sizeof(ctx->info)); + ret = cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_ready, ctx); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: cfstore_fsm_state_set() failed (ret=%" PRId32 ")\n", __func__, ret); + goto out; + } + ret = ctx->status; + } + else + { + CFSTORE_ERRLOG("%s:Error: read bytes (%d) does not equal requested read size (%d)\n", __func__, (int) ctx->status, (int) ctx->expected_blob_size); + ret = cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_ready, ctx); + if(ret < ARM_DRIVER_OK){ + /* move to ready state. cfstore client is expected to Uninitialize() before further calls */ + CFSTORE_ERRLOG("%s:Error: cfstore_fsm_state_set() failed (ret=%" PRId32 ")\n", __func__, ret); + goto out; + } + ret = ctx->status; + } + } + else + { + CFSTORE_TP(CFSTORE_TP_FSM, "%s:debug:ctx->status <= (int32_t) CFSTORE_FLASH_AREA_SIZE_MIN:\n", __func__); + ret = cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_ready, ctx); + if(ret < ARM_DRIVER_OK){ + /* move to ready state. cfstore client is expected to Uninitialize() before further calls */ + CFSTORE_ERRLOG("%s:Error: cfstore_fsm_state_set() failed (ret=%" PRId32 ")\n", __func__, ret); + goto out; + } + ret = ctx->status; + } + } + else if(ctx->status < 0) + { + CFSTORE_TP(CFSTORE_TP_FSM, "%s:debug:ctx->status < 0:\n", __func__); + ret = ctx->status; + } +out: + return ret; +} + + +static int32_t cfstore_fsm_read_on_exit(void* context) +{ + cfstore_ctx_t* ctx = (cfstore_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered:\n", __func__); + /* notify client of initialisation status */ + cfstore_client_notify_data_init(&ctx->client_notify_data, CFSTORE_OPCODE_INITIALIZE, ctx->status, NULL); + ctx->client_callback_notify_flag = true; + return ARM_DRIVER_OK; +} + +/* int32_t cfstore_fsm_log_on_entry(void* context){ (void) context;} */ + +/* @brief on entry to writing state, update value */ +int32_t cfstore_fsm_log_on_entry(void* context) +{ + int32_t ret = 0; + cfstore_ctx_t* ctx = (cfstore_ctx_t*) context; + FlashJournal_Info_t info; + FlashJournal_Status_t status = JOURNAL_STATUS_ERROR; + + CFSTORE_FENTRYLOG("%s:entered:\n", __func__); + memset(&info, 0, sizeof(info)); + + status = FlashJournal_getInfo(&ctx->jrnl, &info); + if(status < JOURNAL_STATUS_OK){ + CFSTORE_ERRLOG("%s:Error: failed get journal info (status=%d)\n", __func__, (int) status); + /* move to ready state. cfstore client is expected to Uninitialize() before further calls */ + cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_ready, ctx); + return cfstore_flash_map_error(status); + } + /* compute the expected_blob_size = area_size plus the padding at the end of the area to align with program_unit*/ + ctx->expected_blob_size = cfstore_ctx_get_area_len(); + if(ctx->expected_blob_size % info.program_unit > 0){ + ctx->expected_blob_size += (info.program_unit - (ctx->expected_blob_size % info.program_unit)); + } + if(ctx->area_0_head && ctx->area_dirty_flag == true) + { + ret = FlashJournal_log(&ctx->jrnl, (const void*) ctx->area_0_head, ctx->expected_blob_size); + if(ret < JOURNAL_STATUS_OK){ + CFSTORE_ERRLOG("%s:Error: FlashJournal_commit() failed (ret=%d)\n", __func__, (int) ret); + ret = cfstore_flash_map_error(status); + /* move to ready state. cfstore client is expected to Uninitialize() before further calls */ + cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_ready, ctx); + goto out0; + } else if(ret > 0){ + /* read has completed synchronously*/ + cfstore_flash_journal_callback(ret, FLASH_JOURNAL_OPCODE_LOG_BLOB); + ret = ctx->status; + } + /* wait for async completion handler*/ + } + else + { + /* nothing to be logged so move back to ready state indicating success*/ + cfstore_flash_journal_callback(ctx->expected_blob_size, FLASH_JOURNAL_OPCODE_LOG_BLOB); + } +out0: + return ret; +} + +/* @brief fsm handler when in reading state */ +static int32_t cfstore_fsm_logging(void* context) +{ + cfstore_ctx_t* ctx = (cfstore_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered:ctx->status=%ld\n", __func__, ctx->status); + /* check the correct amount of data was written */ + if(ctx->status < JOURNAL_STATUS_OK){ + CFSTORE_ERRLOG("%s:Error: FlashJournal_log() failed (ret=%d)\n", __func__, (int) ctx->status); + /* move to ready state. cfstore client is expected to Uninitialize() before further calls */ + cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_ready, ctx); + ctx->status = cfstore_flash_map_error(ctx->status); + } + else + { /* ctx->status >= 0 (status == 0 when everything is deleted) */ + if(ctx->status == (int32_t)ctx->expected_blob_size){ + /* move to the committing state to commit to flash*/ + ctx->status = cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_committing, ctx); + } else { + CFSTORE_ERRLOG("%s:Error: FlashJournal_log() failed to log the expected number of bytes (ctx->expected_blob_size=%d, committed=%d)\n", __func__, (int) ctx->expected_blob_size, (int) ctx->status); + ctx->status = ARM_DRIVER_ERROR; + } + } + return ctx->status; +} + + +static int32_t cfstore_fsm_log_on_exit(void* context) +{ + (void) context; + CFSTORE_FENTRYLOG("%s:entered:\n", __func__); + return ARM_DRIVER_OK; +} + + +/* @brief fsm handler when entering committing state + * @note + * Its unnecessary to provide CS protection for the flashJouranl_commit() as the all the + * _log() operations affecting the commit have been performed, and no more _log() operations + * can happen until we're back in the ready state + */ +static int32_t cfstore_fsm_commit_on_entry(void* context) +{ + int32_t ret = JOURNAL_STATUS_OK; + cfstore_ctx_t* ctx = (cfstore_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered:\n", __func__); + if(ctx->area_0_head && ctx->area_dirty_flag == true) + { + ret = FlashJournal_commit(&ctx->jrnl); + CFSTORE_TP(CFSTORE_TP_FSM, "%s:debug: FlashJournal_commit() (ret=%d)\n", __func__, (int) ret); + if(ret < JOURNAL_STATUS_OK){ + CFSTORE_ERRLOG("%s:Error: FlashJournal_commit() failed (ret=%d)\n", __func__, (int) ret); + /* move to ready state. cfstore client is expected to Uninitialize() before further calls */ + cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_ready, ctx); + } else if(ret > 0){ + /* read has completed synchronously*/ + cfstore_flash_journal_callback(ret, FLASH_JOURNAL_OPCODE_COMMIT); + ret = ctx->status; + } + } + else + { + /* a commit should not be made because there have been no flashJournal_log() calls since the last commit. + * If a _commit() call was made without any _log() calls then it would result in the flash being erased + * because flash journal essentially contains a mirror image of the configuration store sram area, which + * has to be *** FULLY*** repopulated before each _commit(). */ + cfstore_flash_journal_callback(ARM_DRIVER_OK_DONE, FLASH_JOURNAL_OPCODE_COMMIT); + ret = ctx->status; + } + /* wait for async callback */ + CFSTORE_FENTRYLOG("%s:exiting: FlashJournal_commit() (ret=%d)\n", __func__, (int) ret); + return ret; +} + + +/* @brief fsm handler when in committing state + * @note + * Its unnecessary to provide CS protection for the flashJouranl_commit() as the all the + * _log() operations affecting the commit have been performed, and no more _log() operations + * can happen until we're back in the ready state + */ +static int32_t cfstore_fsm_committing(void* context) +{ + cfstore_ctx_t* ctx = (cfstore_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + CFSTORE_ASSERT(ctx->fsm.state == cfstore_fsm_state_committing); + CFSTORE_ASSERT(ctx->cmd_code == FLASH_JOURNAL_OPCODE_COMMIT); + + /* check the correct amount of data was written */ + if(ctx->status < JOURNAL_STATUS_OK){ + CFSTORE_ERRLOG("%s:Error: FlashJournal_commit() failed (ret=%d)\n", __func__, (int) ctx->status); + /* move to ready state. cfstore client is expected to Uninitialize() before further calls */ + cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_ready, ctx); + ctx->status = cfstore_flash_map_error(ctx->status); + } + else if(ctx->status == JOURNAL_STATUS_OK) + { + ctx->status = cfstore_flash_map_error(ctx->status); + } + else + { /* ctx->status > 0. for flash-journal-strategy-sequential version >0.4.0, commit() return no longer reports size of commit block */ + ctx->status = cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_ready, ctx); + } + return ctx->status; +} + +static int32_t cfstore_fsm_commit_on_exit(void* context) +{ + cfstore_ctx_t* ctx = (cfstore_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered:\n", __func__); + ctx->area_dirty_flag = false; + /* notify client of commit status */ + cfstore_client_notify_data_init(&ctx->client_notify_data, CFSTORE_OPCODE_FLUSH, ctx->status, NULL); + ctx->client_callback_notify_flag = true; + return ARM_DRIVER_OK; +} + +/* int32_t cfstore_fsm_reset_on_entry(void* context){ (void) context;} */ +/* int32_t cfstore_fsm_resetting(void* context){ (void) context;} */ +/* int32_t cfstore_fsm_reset_on_exit(void* context){ (void) context;} */ + +static int32_t cfstore_fsm_ready_on_commit_req(void* context) +{ + cfstore_ctx_t* ctx = (cfstore_ctx_t*) context; + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_logging, ctx); +} + +/* int32_t cfstore_fsm_ready_on_entry(void* context){ (void) context;} */ +/* int32_t cfstore_fsm_ready(void* context){ (void) context;} */ +/* int32_t cfstore_fsm_ready_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 }, +}; + +/* handler functions for entering the state*/ +cfstore_fsm_handler cfstore_fsm_on_entry[cfstore_fsm_state_max] = +{ + cfstore_fsm_stop_on_entry, + cfstore_fsm_init_on_entry, + cfstore_fsm_read_on_entry, + 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 */ +}; + +/* handler functions for exiting state, currently none used */ +cfstore_fsm_handler cfstore_fsm_on_exit[cfstore_fsm_state_max] = +{ + cfstore_fsm_null, /* cfstore_fsm_stop_on_exit */ + cfstore_fsm_null, /* cfstore_fsm_init_on_exit */ + cfstore_fsm_read_on_exit, + 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 */ +}; + + +/* @brief inject event into fsm */ +static int32_t cfstore_fsm_state_handle_event(cfstore_fsm_t* fsm, cfstore_fsm_event_t event, void* context) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_ctx_t* ctx = (cfstore_ctx_t*) context; + + CFSTORE_FENTRYLOG("%s:entered: fsm=%p, fsm->state=%d, event=%d (%s), ctx=%p\n", __func__, fsm, fsm->state, event, cfstore_flash_event_str[event], ctx); + CFSTORE_ASSERT(event < cfstore_fsm_event_max); + fsm->event = event; + if(cfstore_flash_fsm[fsm->state][fsm->event] != NULL){ + ret = cfstore_flash_fsm[fsm->state][fsm->event](ctx); + if(ret < ARM_DRIVER_OK){ + #ifdef CFSTORE_DEBUG + CFSTORE_ERRLOG("%s:FSM:EVT:Error: cfstore_flash_fsm[%s][%s] failed\n", __func__, (char*) cfstore_flash_state_str[fsm->state], (char*) cfstore_flash_event_str[fsm->event]); + #endif + return ret; + } + } + + /* do not clear context data set by caller as it may be used later + * fsm->event = cfstore_fsm_event_max; + * ctx->status = 0; + * ctx->cmd_code = (FlashJournal_OpCode_t)((int) FLASH_JOURNAL_OPCODE_RESET+1); + */ + return ret; +} + + +/* @brief get the current state of the fsm */ +static cfstore_fsm_state_t cfstore_fsm_state_get(cfstore_fsm_t* fsm) +{ + return fsm->state; +} + +/* @brief function to move to new fsm state, calling state exit function for old state and entry function for new state */ +static int32_t cfstore_fsm_state_set(cfstore_fsm_t* fsm, cfstore_fsm_state_t new_state, void* ctx) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_ctx_t* context = (cfstore_ctx_t*) ctx; + #ifdef CFSTORE_DEBUG + cfstore_fsm_state_t old_state = fsm->state; + #endif + + CFSTORE_FENTRYLOG("%s:entered: fsm=%p, ctx=%p\n", __func__, fsm, ctx); + #ifdef CFSTORE_DEBUG + CFSTORE_TP(CFSTORE_TP_FSM, "%s:FSM:REQ RX: fsm->state=%d (%s): new_state=%d (%s)\n", __func__, (int) fsm->state, cfstore_flash_state_str[fsm->state], (int) new_state, cfstore_flash_state_str[new_state]); + #endif + CFSTORE_ASSERT(fsm != NULL); + CFSTORE_ASSERT(new_state < cfstore_fsm_state_max); + CFSTORE_ASSERT(ctx != NULL); + CFSTORE_ASSERT(fsm->state < cfstore_fsm_state_max); + + if(cfstore_fsm_on_exit[fsm->state] != NULL){ + ret = cfstore_fsm_on_exit[fsm->state](ctx); + if(ret < ARM_DRIVER_OK){ + #ifdef CFSTORE_DEBUG + CFSTORE_ERRLOG("%s:FSM:REQ RX:%s:%s:Error: cfstore_fsm_on_exit() failed\n", __func__, cfstore_flash_state_str[fsm->state], cfstore_flash_state_str[new_state]); + #endif + /* handling of the error is done in the on_exit() method, which best knows how the state to move to */ + return ret; + } + } + fsm->state = new_state; + if(cfstore_fsm_on_entry[new_state] != NULL){ + ret = cfstore_fsm_on_entry[new_state](ctx); + if(ret < ARM_DRIVER_OK){ + #ifdef CFSTORE_DEBUG + CFSTORE_TP(CFSTORE_TP_FSM, "%s:FSM:REQ RX: fsm->state=%d (%s): new_state=%d (%s): Error: cfstore_fsm_on_entry() failed (ret=%" PRId32 ")\n", __func__, (int) fsm->state, cfstore_flash_state_str[fsm->state], (int) new_state, cfstore_flash_state_str[new_state], ret); + #endif + /* handling of the error is done in the on_entry() method, which best knows how the state to move to */ + return ret; + } + } + if(context->client_callback_notify_flag == true) + { + cfstore_client_notify_data_t notify_data; + + CFSTORE_TP(CFSTORE_TP_FSM, "%s:doing client callback\n", __func__); + + /* only one set of client notify data is required as there can only be 1 outstanding flash journal async notificaion + * at one time. */ + context->client_callback_notify_flag = false; /* prevents re-calling callback if this function gets called again */ + memcpy(¬ify_data, &context->client_notify_data, sizeof(cfstore_client_notify_data_t)); + /* clear context state before initiating call */ + cfstore_client_notify_data_init(&context->client_notify_data, CFSTORE_OPCODE_MAX, ARM_DRIVER_ERROR, NULL); + cfstore_ctx_client_notify(ctx, ¬ify_data); + } + CFSTORE_TP(CFSTORE_TP_FSM, "%s:FSM:REQ DONE: fsm->state=%d (%s): new_state=%d (%s)\n", __func__, (int) old_state, cfstore_flash_state_str[old_state], (int) new_state, cfstore_flash_state_str[new_state]); + return ret; +} + +static bool cfstore_flash_journal_is_async_op_pending(cfstore_ctx_t* ctx) +{ + CFSTORE_FENTRYLOG("%s:entered: fsm->state=%s\n", __func__, (char*) cfstore_flash_state_str[cfstore_fsm_state_get(&ctx->fsm)]); + if(cfstore_fsm_state_get(&ctx->fsm) != cfstore_fsm_state_ready) + { + /* flash journal async operation is in progress */ + return true; + } + return false; +} + +static int32_t cfstore_flash_init(void) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + CFSTORE_FENTRYLOG("%s:entered: \n", __func__); + CFSTORE_FENTRYLOG("%s:: CFSTORE_CONFIG_BACKEND_FLASH_ENABLED=%d\n", __func__, (int) CFSTORE_CONFIG_BACKEND_FLASH_ENABLED); + ctx->cmd_code = (FlashJournal_OpCode_t)((int) FLASH_JOURNAL_OPCODE_RESET+1); + ctx->expected_blob_size = 0; + ctx->fsm.event = cfstore_fsm_event_max; + ctx->fsm.state = cfstore_fsm_state_stopped; + memset(&ctx->info, 0, sizeof(ctx->info)); + ret = cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_initing, ctx); + if(ret < 0){ + CFSTORE_DBGLOG("%s:Error: cfstore_fsm_state_set() failed\n", __func__); + return ret; + } + return ret; +} + + +/* @brief de-initialise the flash journal */ +static int32_t cfstore_flash_deinit(void) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + CFSTORE_FENTRYLOG("%s:entered: fsm->state=%s\n", __func__, (char*) cfstore_flash_state_str[cfstore_fsm_state_get(&ctx->fsm)]); + ret = cfstore_fsm_state_set(&ctx->fsm, cfstore_fsm_state_stopped, ctx); + if(ret < 0){ + CFSTORE_TP(CFSTORE_TP_INIT, "%s:Error: cfstore_fsm_state_set() failed\n", __func__); + } + return ret; +} + +/* +static int32_t cfstore_flash_reset(void) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + ret = FlashJournal_reset(&ctx->jrnl); + if(ret != JOURNAL_STATUS_OK){ + CFSTORE_ERRLOG("%s:Error: failed to reset flash journal (ret=%" PRId32 ")\n", __func__, ret); + goto out0; + } +out0: + return ret; +} +*/ + +static int32_t cfstore_flash_flush(cfstore_ctx_t* ctx) +{ + int32_t ret = ARM_DRIVER_OK; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + /* put the async completion code state variables into a known state */ + ctx->status = ARM_DRIVER_OK; + ctx->cmd_code = (FlashJournal_OpCode_t)((int) FLASH_JOURNAL_OPCODE_RESET+1); + + /* cfstore_fsm_state_handle_event() is called at intr context via + * cfstore_flash_journal_callback(), and hence calls from app context are + * protected with CSs */ + cfstore_critical_section_lock(&ctx->rw_area0_lock, __func__); + ret = cfstore_fsm_state_handle_event(&ctx->fsm, cfstore_fsm_event_commit_req, (void*) ctx); + cfstore_critical_section_unlock(&ctx->rw_area0_lock, __func__); + return ret; +} + +#else /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ + +static bool cfstore_flash_journal_is_async_op_pending(cfstore_ctx_t* ctx) { CFSTORE_FENTRYLOG("%s:SRAM:entered:\n", __func__); (void) ctx; return false; } + +/* @brief generate the CFSTORE_OPCODE_INITIALIZE callback notification */ +static int32_t cfstore_flash_init(void) +{ + cfstore_client_notify_data_t notify_data; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + CFSTORE_FENTRYLOG("%s:SRAM:entered:\n", __func__); + cfstore_client_notify_data_init(¬ify_data, CFSTORE_OPCODE_INITIALIZE, ARM_DRIVER_OK, NULL); + cfstore_ctx_client_notify(ctx, ¬ify_data); + return ARM_DRIVER_OK; +} + +static int32_t cfstore_flash_deinit(void){ CFSTORE_FENTRYLOG("%s:SRAM:entered:\n", __func__); return ARM_DRIVER_OK; } +/* static int32_t cfstore_flash_reset(void) { CFSTORE_FENTRYLOG("%s:SRAM:entered:\n", __func__); return ARM_DRIVER_OK; }*/ +static int32_t cfstore_flash_flush(cfstore_ctx_t* ctx) +{ + cfstore_client_notify_data_t notify_data; + + CFSTORE_FENTRYLOG("%s:SRAM:entered:\n", __func__); + cfstore_client_notify_data_init(¬ify_data, CFSTORE_OPCODE_FLUSH, ARM_DRIVER_OK, NULL); + cfstore_ctx_client_notify(ctx, ¬ify_data); + return ARM_DRIVER_OK; +} + +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ + +/** @brief internal delete helper function. + * @note must be called within critical section. + */ +static int32_t cfstore_delete_ex(cfstore_area_hkvt_t* hkvt) +{ + uint8_t* ptr = NULL; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE kv_size = 0; + ARM_CFSTORE_SIZE area_size = 0; + ARM_CFSTORE_SIZE realloc_size = 0; /* size aligned to flash program_unit size */ + cfstore_ctx_t* ctx = cfstore_ctx_get(); + cfstore_file_t* file; + cfstore_list_node_t* node; + cfstore_list_node_t* file_list = &ctx->file_list; + + CFSTORE_FENTRYLOG("%s:entered:(ctx->area_0_head=%p, ctx->area_0_tail=%p)\n", __func__, ctx->area_0_head, ctx->area_0_tail); + kv_size = cfstore_hkvt_get_size(hkvt); + area_size = cfstore_ctx_get_area_len(); + memmove(hkvt->head, hkvt->tail, ctx->area_0_tail - hkvt->tail); + /* zero the deleted KV memory */ + memset(ctx->area_0_tail-kv_size, 0, kv_size); + + /* In the general case the new ((area_size - kv_size) % program_unit > 0). The new area_size is + * aligned to a program_unit boundary to facilitate r/w to flash and so the memory realloc size + * is calculated to align, as follows */ + /* setup the reallocation memory size. */ + realloc_size = area_size - kv_size; + if(realloc_size % cfstore_ctx_get_program_unit(ctx) > 0){ + realloc_size += (cfstore_ctx_get_program_unit(ctx) - (realloc_size % cfstore_ctx_get_program_unit(ctx))); + } + /* realloc() can return non-zero ptr for size = 0 when the last KV is deleted */ + if(realloc_size > 0) + { + ptr = (uint8_t*) CFSTORE_REALLOC((void*) ctx->area_0_head, realloc_size); + /* realloc can return NULL when the last KV is deleted. + * It also appears that realloc can return non-zero ptr even when realloc_size = 0 */ + if(ptr == NULL){ + CFSTORE_ERRLOG("%s:Error:realloc failed\n", __func__); + cfstore_ctx_reset(ctx); + return ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY; + } + /* check realloc() hasnt move area in memory from cfstore_ctx_g.area_0_head */ + if(ptr != ctx->area_0_head){ + CFSTORE_TP(CFSTORE_TP_DELETE, "%s: cfstore_ctx_g.area_0_head pointer changed (cfstore_ctx_g.area_0_head=%p, cfstore_ctx_g.area_0_tail=%p)\n", __func__, ctx->area_0_head, ptr); + /* realloc() has moved the area in memory */ + ctx->area_0_head = ptr; + } + /* set tail to be the end of the new area, which will be updated by cfstore_flash_set_tail */ + ctx->area_0_tail = ptr + area_size - kv_size; + ret = cfstore_flash_set_tail(); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: cfstore_flash_set_tail() failed (ret=%" PRId32 ")\n", __func__, ret); + goto out0; + } + + /* now have to walk the file list updating head pointers for the KVs that remain*/ + node = file_list->next; + while(node != file_list){ + /* Any KV positioned later in the area than the deleted KV will require file head pointers updating. + * If file's head pointer is beyond the deleted KV tail then the file->head needs to be updated + * to reflect the memove */ + file = (cfstore_file_t*) node; + if(file->head >= hkvt->head){ + file->head -= kv_size; + } + node = node->next; + } + } + else + { + /* realloc_size = 0 */ + CFSTORE_FREE((void*) ctx->area_0_head); + ctx->area_0_head = NULL; + ctx->area_0_tail = NULL; + } + ret = ARM_DRIVER_OK; +out0: + return ret; +} + + +/* + * File operations + */ + +static cfstore_file_t* cfstore_file_get(ARM_CFSTORE_HANDLE hkey) +{ + return (cfstore_file_t*) hkey; +} + +static cfstore_file_t* cfstore_file_create(cfstore_area_hkvt_t* hkvt, ARM_CFSTORE_FMODE flags, ARM_CFSTORE_HANDLE hkey, cfstore_list_node_t *list_head) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_file_t* file = (cfstore_file_t*) hkey; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + if(file != NULL){ + memset(file, 0, sizeof(cfstore_file_t)); + CFSTORE_INIT_LIST_HEAD(&file->node); + ret = cfstore_hkvt_refcount_inc(hkvt, NULL); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: cfstore_hkvt_refcount_inc() failed (ret=%" PRId32 ")\n", __func__, ret); + return NULL; + } + file->head = hkvt->head; + file->flags.read = flags.read; + file->flags.write = flags.write; + if(list_head != NULL){ + cfstore_listAdd(list_head, &file->node, list_head); + } + } + return file; +} + +/* @brief required to be in critical section when called. */ +static int32_t cfstore_file_destroy(cfstore_file_t* file) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_area_hkvt_t hkvt; + uint8_t refcount = 0; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + if(file) { + hkvt = cfstore_get_hkvt_from_head_ptr(file->head); + CFSTORE_ASSERT(cfstore_hkvt_is_valid(&hkvt, cfstore_ctx_get()->area_0_tail) == true); + ret = ARM_DRIVER_OK; + cfstore_hkvt_refcount_dec(&hkvt, &refcount); + CFSTORE_TP(CFSTORE_TP_FILE, "%s:refcount =%d\n", __func__, (int)refcount); + if(refcount == 0){ + /* check for delete */ + CFSTORE_TP(CFSTORE_TP_FILE, "%s:checking delete flag\n", __func__); + if(cfstore_hkvt_get_flags_delete(&hkvt)){ + ret = cfstore_delete_ex(&hkvt); + } + /* reset client buffer to empty ready for reuse */ + /* delete the file even if not deleting the KV*/ + cfstore_listDel(&file->node); + memset(file, 0, sizeof(cfstore_file_t)); + } + } + return ret; +} + + +/** + * @brief check whether this is an valid buffer + * + * @param hkey + * IN: The key handle to be validated + * + * ctx + * IN: cfstore context block + */ +static bool cfstore_file_is_valid(ARM_CFSTORE_HANDLE hkey, cfstore_ctx_t* ctx) +{ + cfstore_file_t* file = cfstore_file_get(hkey); + + if(ctx->area_0_head != NULL && ctx->area_0_tail != NULL){ + if(file->head < ctx->area_0_head || file->head > ctx->area_0_tail){ + return 0; + } + return true; + } + return false; +} + +/** + * @brief check whether this is an empty buffer, or whether it + * has valid data + * + * @param hkey + * IN: The key handle to be validated + * + * ctx + * IN: cfstore context block + */ +static bool cfstore_file_is_empty(ARM_CFSTORE_HANDLE hkey) +{ + ARM_CFSTORE_HANDLE_INIT(zero); + if(hkey != NULL){ + return !memcmp(hkey, zero, CFSTORE_HANDLE_BUFSIZE); + } + return 0; +} + + +/* @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 */ + return cfstore_caps_g; +} + + +/* @brief check the flags argument are supported */ +static int32_t cfstore_validate_fmode_flags(ARM_CFSTORE_FMODE flags) +{ + if(flags.continuous){ + CFSTORE_ERRLOG("%s:Error:Continuous flag not supported.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED; + } + if(flags.lazy_flush){ + CFSTORE_ERRLOG("%s:Error:Lazy flush flag not supported.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED; + } + if(flags.flush_on_close){ + CFSTORE_ERRLOG("%s:Error:Flush on close flag not supported.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED; + } + if(flags.storage_detect){ + CFSTORE_ERRLOG("%s:Error:Storage detect flag not supported.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED; + } + return ARM_DRIVER_OK; +} + + +/* @brief validate the client supplied opaque handle */ +static CFSTORE_INLINE int32_t cfstore_validate_handle(ARM_CFSTORE_HANDLE hkey) +{ + if(hkey == NULL){ + return ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE; + } + return ARM_DRIVER_OK; +} + +/* @brief check the flash security features are valid (internal use only) */ +static int32_t cfstore_validate_flash_security_features(const ARM_STORAGE_SECURITY_FEATURES *security) +{ + CFSTORE_ASSERT(security != NULL); + + if(security->acls){ + CFSTORE_ERRLOG("%s:Error: flash security features acls flag not supported.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED; + } + if(security->internal_flash){ + CFSTORE_ERRLOG("%s:Error: flash security features internal_flash flag not supported.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED; + } + if(security->rollback_protection){ + CFSTORE_ERRLOG("%s:Error: flash security features rollback_protection flag not supported.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED; + } + if(security->tamper_proof){ + CFSTORE_ERRLOG("%s:Error: flash security features tamper_proof flag not supported.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED; + } + if(security->board_level_attacks){ + CFSTORE_ERRLOG("%s:Error: flash security features board level attacks flag not supported.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED; + } + if(security->software_attacks){ + CFSTORE_ERRLOG("%s:Error: flash security features device_software flag not supported.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED; + } + if(security->chip_level_attacks){ + CFSTORE_ERRLOG("%s:Error: flash security features chip level attacks flag not supported.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED; + } + if(security->side_channel_attacks){ + CFSTORE_ERRLOG("%s:Error: flash security features side channel attacks flag not supported.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED; + } + return ARM_DRIVER_OK; +} + +/* @brief check the key descriptor are valid (internal use only) */ +static int32_t cfstore_validate_flash_data_retention_level(const uint8_t drl) +{ + int32_t ret = ARM_DRIVER_ERROR; + + switch(drl) + { + case ARM_RETENTION_WHILE_DEVICE_ACTIVE : + case ARM_RETENTION_ACROSS_SLEEP : + case ARM_RETENTION_ACROSS_DEEP_SLEEP : + case ARM_RETENTION_BATTERY_BACKED : + case ARM_RETENTION_NVM : + ret = ARM_DRIVER_OK; + break; + default: + CFSTORE_ERRLOG("%s:Error: data retention level (%d) not supported.\n", __func__, drl); + ret = ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED; + break; + + } + return ret; +} + +/* @brief check the access control list is valid (internal use only) */ +static int32_t cfstore_validate_access_control_list(const ARM_CFSTORE_ACCESS_CONTROL_LIST acl) +{ + if(acl.perm_owner_execute) + { + CFSTORE_ERRLOG("%s:Error: Access control list with permission owner execute set is not supported.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED; + } + if(acl.perm_other_execute) + { + CFSTORE_ERRLOG("%s:Error: Access control list with permission other execute set is not supported.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_NOT_SUPPORTED; + } + return ARM_DRIVER_OK; +} + +/* @brief check the key descriptor is valid */ +static int32_t cfstore_validate_key_desc(const ARM_CFSTORE_KEYDESC *kdesc) +{ + int32_t ret = ARM_DRIVER_ERROR; + + if(kdesc == NULL){ + return ARM_CFSTORE_DRIVER_ERROR_INVALID_KEY_DESCRIPTOR; + } + ret = cfstore_validate_access_control_list(kdesc->acl); + if(ret < ARM_DRIVER_OK){ + return ret; + } + ret = cfstore_validate_flash_data_retention_level(kdesc->drl); + if(ret < ARM_DRIVER_OK){ + return ret; + } + ret = cfstore_validate_flash_security_features(&kdesc->security); + if(ret < ARM_DRIVER_OK){ + return ret; + } + ret = cfstore_validate_fmode_flags(kdesc->flags); + if(ret < ARM_DRIVER_OK){ + return ret; + } + return ARM_DRIVER_OK; +} + +/** + * @brief check the key_len pointer is valid + * + * @param hkey + * IN: The key handle to be validated + */ +static CFSTORE_INLINE int32_t cfstore_validate_len_ptr(ARM_CFSTORE_SIZE *len) +{ + if(len == NULL){ + return ARM_CFSTORE_DRIVER_ERROR_INVALID_KEY_LEN; + } + return ARM_DRIVER_OK; +} + +/* @brief return a pointer to the next { or }, or NULL if not present */ +static inline char* cfstore_validate_pos_next_brace(const char* pos) +{ + char* pos_open = strchr(pos, '{'); + char* pos_close = strchr(pos, '}'); + if(pos_open != NULL) { + if(pos_close != NULL){ + return pos_open < pos_close ? pos_open : pos_close; + } + return pos_open; + } + return pos_close; +} + + +static int32_t cfstore_validate_key_name_ex(const char* key_name, const char* permissible) +{ + char* pos = NULL; + int brace_count = 0; + ARM_CFSTORE_SIZE len = 0; + ARM_CFSTORE_SIZE valid_len = 0; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + if(key_name != NULL){ + /* check the key_name is terminated by a 0 */ + pos = (char*) memchr(key_name, '\0', CFSTORE_KEY_NAME_MAX_LENGTH+1); + if(pos == NULL){ + CFSTORE_ERRLOG("%s:key_name does not have terminating null.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_INVALID_KEY_NAME; + } + /* check for zero length key_name*/ + if(strlen(key_name) == 0){ + CFSTORE_ERRLOG("%s:Error: invalid key_name.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_INVALID_KEY_NAME; + } + /* check the key_name len is less than the max length (220) */ + len = strlen(key_name); + if(len > CFSTORE_KEY_NAME_MAX_LENGTH){ + CFSTORE_ERRLOG("%s:key_name string is longer (%d) than the supported maximum (%d).\n", __func__, (int) len, (int) CFSTORE_KEY_NAME_MAX_LENGTH); + return ARM_CFSTORE_DRIVER_ERROR_INVALID_KEY_NAME; + } + /* check the key_name only contains permissible characters */ + valid_len = strspn(key_name, permissible); + if(valid_len != len){ + CFSTORE_ERRLOG("%s:Invalid character (%c) found in key_name (key_name=%s).\n", __func__, key_name[valid_len], key_name); + return ARM_CFSTORE_DRIVER_ERROR_INVALID_KEY_NAME; + } + + /*check there isnt a leading '.' on the kv name */ + if(key_name[0] == '.'){ + CFSTORE_ERRLOG("%s:Leading (.) character found in key_name (key_name=%s) is not allowed.\n", __func__, key_name); + return ARM_CFSTORE_DRIVER_ERROR_INVALID_KEY_NAME; + } + + /* - check for matching '{' for each '}' present + * - only check a string if either { or } are present + * i.e. dont process string without + * checking for existence of single brace, and checking for either { or } so + * that the case where } is the first brace is convered. + * - start loop at first { or } char, both {} covers case where } is the first brace + * - (brace_count >=0 && brace_count <= 1) must always be true + * - brace_count must == 0 at end of string + */ + pos = cfstore_validate_pos_next_brace(key_name); + while(pos != NULL && brace_count >= 0 && brace_count <= 1) + { + switch(*pos) + { + case '{': + brace_count++; + break; + case '}': + brace_count--; + break; + default: + break; + } + pos++; + pos = cfstore_validate_pos_next_brace(pos); + } + if(brace_count != 0){ + CFSTORE_ERRLOG("%s: Unmatched brace found in key_name (count=%" PRId32 ".\n", __func__, brace_count); + return ARM_CFSTORE_DRIVER_ERROR_INVALID_KEY_NAME; + } + } + return ARM_DRIVER_OK; +} + + +/* @brief check the key name is valid */ +static int32_t cfstore_validate_key_name(const char* key_name) +{ + int32_t ret = ARM_DRIVER_ERROR; + + ret = cfstore_uvisor_security_context_prefix_check(key_name); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed uvisor security context check.\n", __func__); + return ret; + } + return cfstore_validate_key_name_ex(key_name, CFSTORE_KEY_NAME_CHARS_ACCEPTABLE); +} + +/* @brief check the key name query is valid */ +static int32_t cfstore_validate_key_name_query(const char* key_name_query) +{ + return cfstore_validate_key_name_ex(key_name_query, CFSTORE_KEY_NAME_QUERY_CHARS_ACCEPTABLE); +} + + +/** + * @brief check the value length field is valid + * + * @param key_name + * IN: The key name string to be validated + * @note This will be replaced with the actual uvisor call, when available. + */ +static CFSTORE_INLINE int32_t cfstore_validate_value_len(ARM_CFSTORE_SIZE value_len) +{ + if(value_len <= CFSTORE_VALUE_SIZE_MAX) { + return ARM_DRIVER_OK; + } + return ARM_CFSTORE_DRIVER_ERROR_VALUE_SIZE_TOO_LARGE; +} + + +/* @brief See definition in configuration_store.h for description. */ +static int32_t cfstore_get_key_name_ex(cfstore_area_hkvt_t *hkvt, char* key_name, uint8_t *key_name_len) +{ + int32_t ret = ARM_DRIVER_OK; + int32_t max_len = 0; + + max_len = cfstore_hkvt_get_key_len(hkvt) + 1; + max_len = max_len <= *key_name_len ? max_len : *key_name_len; + memcpy(key_name, (const char*) hkvt->key, max_len-1); + key_name[max_len-1] = '\0'; + *key_name_len = max_len; + return ret; +} + + +/* @brief See definition in configuration_store.h for description. */ +static int32_t cfstore_get_key_name(ARM_CFSTORE_HANDLE hkey, char* key_name, uint8_t *key_name_len) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_area_hkvt_t hkvt; + cfstore_client_notify_data_t notify_data; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + CFSTORE_ASSERT(key_name != NULL); + CFSTORE_ASSERT(key_name_len != NULL); + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + if(!cfstore_ctx_is_initialised(ctx)) { + CFSTORE_ERRLOG("%s:Error: CFSTORE is not initialised.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_UNINITIALISED; + goto out0; + } + /* getting a keyname doesnt change the sram area so this can happen independently of + * an oustanding async operation. its unnecessary to check the fsm state */ + ret = cfstore_validate_handle(hkey); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid handle.\n", __func__); + goto out0; + } + if(key_name == NULL){ + CFSTORE_ERRLOG("%s:Error: invalid handle.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_INVALID_KEY_NAME; + goto out0; + } + ret = cfstore_validate_len_ptr((ARM_CFSTORE_SIZE*)key_name_len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid key_name_len argument.\n", __func__); + goto out0; + } + memset(&hkvt, 0, sizeof(hkvt)); + hkvt = cfstore_get_hkvt(hkey); + if(!cfstore_hkvt_is_valid(&hkvt, ctx->area_0_tail)){ + CFSTORE_ERRLOG("%s:ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE; + goto out0; + } + ret = cfstore_get_key_name_ex(&hkvt, key_name, key_name_len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: cfstore_get_key_name_ex() returned error.\n", __func__); + goto out0; + } + ret = *key_name_len; +out0: + /* GetKeyName() always completes synchronously irrespective of flash mode, so indicate to caller */ + cfstore_client_notify_data_init(¬ify_data, CFSTORE_OPCODE_GET_KEY_NAME, ret, hkey); + cfstore_ctx_client_notify(ctx, ¬ify_data); + return ret; +} + +/* @brief See definition in configuration_store.h for description. */ +static ARM_CFSTORE_STATUS cfstore_get_status(void) +{ + ARM_CFSTORE_STATUS status; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + memset(&status, 0, sizeof(status)); + if(!cfstore_ctx_is_initialised(ctx)) { + CFSTORE_ERRLOG("%s:Error: CFSTORE is not initialised.\n", __func__); + status.error = true; + } + /* getting status doesnt change the sram area so this can happen independently of + * an oustanding async operation. */ + if(cfstore_flash_journal_is_async_op_pending(ctx)) + { + status.in_progress = true; + } + else + { + status.in_progress = false; + } + return status; +} + +/* @brief See definition in configuration_store.h for description. */ +static int32_t cfstore_get_value_len(ARM_CFSTORE_HANDLE hkey, ARM_CFSTORE_SIZE *value_len) +{ + int32_t ret = ARM_CFSTORE_DRIVER_ERROR_UNINITIALISED; + cfstore_area_hkvt_t hkvt; + cfstore_client_notify_data_t notify_data; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + CFSTORE_ASSERT(hkey != NULL); + CFSTORE_ASSERT(value_len != NULL); + + if(!cfstore_ctx_is_initialised(ctx)) { + CFSTORE_ERRLOG("%s:Error: CFSTORE is not initialised.\n", __func__); + goto out0; + } + /* getting a value len doesnt change the sram area so this can happen independently of + * an oustanding async operation. its unnecessary to check the fsm state */ + ret = cfstore_validate_handle(hkey); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid handle.\n", __func__); + goto out0; + } + ret = cfstore_validate_len_ptr(value_len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid value len argument.\n", __func__); + goto out0; + } + hkvt = cfstore_get_hkvt(hkey); + if(!cfstore_hkvt_is_valid(&hkvt, ctx->area_0_tail)){ + CFSTORE_ERRLOG("%s:ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE; + goto out0; + } + *value_len = cfstore_hkvt_get_value_len(&hkvt); + ret = (int32_t) *value_len; +out0: + /* GetValueLen() always completes synchronously irrespective of flash mode, so indicate to caller */ + cfstore_client_notify_data_init(¬ify_data, CFSTORE_OPCODE_GET_VALUE_LEN, ret, hkey); + cfstore_ctx_client_notify(ctx, ¬ify_data); + return ret; +} + +#ifdef CFSTORE_DEBUG + +/* @brief debug trace a struct cfstore_area_hkvt_t, providing values for key field. */ +static CFSTORE_INLINE void cfstore_hkvt_dump(cfstore_area_hkvt_t* hkvt, const char* tag) +{ +/* #define noSymbol */ +#ifdef noSymbol + char kname[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + char value[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + uint32_t klen = 0; + uint32_t vlen = 0; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + memset(kname, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + memset(value, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + klen = cfstore_hkvt_get_key_len(hkvt); + vlen = cfstore_hkvt_get_value_len(hkvt); + memcpy((void*)kname, (void*) hkvt->key, klen); + memcpy((void*)value, (void*) hkvt->value, vlen); + kname[klen] = '\0'; + value[vlen] = '\0'; + + /* table column description + * col 1: tag, descriptive string supplied by client to identify context of table dump + * col 2: hkvt struct member that is to be reported i.e. head, key, value, tail + * col 3: the value of the pointer described in col 2. + * col 4: the value of the pointer described in col 3 as an offset from the start of the sram area + * col 5: field specified data e.g. for header, the extracted key length, value_length. + */ + CFSTORE_TP(CFSTORE_TP_VERBOSE3, "%s:hkvt->head:%8p:%8p:klen=%08d:vlen=%08d:\n", tag, hkvt->head, (void*)(hkvt->head - ctx->area_0_head), (int) klen, (int) vlen); + CFSTORE_TP(CFSTORE_TP_VERBOSE3, "%s:hkvt->key :%8p:%8p:%s\n", tag, hkvt->key, (void*)(hkvt->key - ctx->area_0_head), kname); + CFSTORE_TP(CFSTORE_TP_VERBOSE3, "%s:hkvt->val :%8p:%8p:%s\n", tag, hkvt->value, (void*)(hkvt->value - ctx->area_0_head), value); + CFSTORE_TP(CFSTORE_TP_VERBOSE3, "%s:hkvt->tail:%8p:%8p:\n", tag, hkvt->tail, (void*)(hkvt->tail - ctx->area_0_head)); + return; +#else + (void) hkvt; + (void) tag; + +#endif /* noSymbol */ +} + +static CFSTORE_INLINE void cfstore_flags_dump(ARM_CFSTORE_FMODE flag, const char* tag) +{ + int pos = 0; + char flags[9]; + + pos += snprintf(&flags[pos], 9, "%c", flag.continuous ? 'C' : 'c'); + pos += snprintf(&flags[pos], 9, "%c", flag.lazy_flush ? 'L' : 'l'); + pos += snprintf(&flags[pos], 9, "%c", flag.flush_on_close ? 'F' : 'f'); + pos += snprintf(&flags[pos], 9, "%c", flag.read ? 'R' : 'r'); + pos += snprintf(&flags[pos], 9, "%c", flag.write ? 'W' : 'w'); + pos += snprintf(&flags[pos], 9, "%c", flag.storage_detect ? 'S' : 's'); + pos += snprintf(&flags[pos], 9, "--"); + + CFSTORE_TP(CFSTORE_TP_VERBOSE3, "%s:flags :%s:(C=>continuous set, L=>lazy flush, F=>flush on close, R=>read, W=>write, S=>storage detect)\n", tag, flags); + return; +} + +static CFSTORE_INLINE void cfstore_file_dump(cfstore_file_t* file, const char* tag) +{ +#ifdef noSymbol + cfstore_area_hkvt_t hkvt; + + CFSTORE_TP(CFSTORE_TP_VERBOSE3, "%s:*** Dumping File Contents : Start ***\n", tag); + CFSTORE_TP(CFSTORE_TP_VERBOSE3, "%s:file==hkey:%p\n", tag, file); + CFSTORE_TP(CFSTORE_TP_VERBOSE3, "%s:rloc/wloc :%08u/%08u:\n", tag, (unsigned int) file->rlocation, (unsigned int) file->wlocation); + cfstore_flags_dump(file->flags, tag); + hkvt = cfstore_get_hkvt((ARM_CFSTORE_HANDLE)file); + cfstore_hkvt_dump(&hkvt, tag); + CFSTORE_TP(CFSTORE_TP_VERBOSE3, "%s:*** Dumping File Contents : End ***\n", tag); + return; +#else + (void) file; + (void) tag; + +#endif /* noSymbol */ +} + +/* dump sram contents of cfstore in a useful manner for debugging */ +static CFSTORE_INLINE void cfstore_dump_contents(const char* tag) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_area_hkvt_t hkvt; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + CFSTORE_TP(CFSTORE_TP_VERBOSE3, "%s:*** Dumping CFSTORE Contents : Start ***\n", tag); + CFSTORE_TP(CFSTORE_TP_VERBOSE3, "%s:cfstore_ctx_g.area_0_head=%8p\n", tag, ctx->area_0_head); + CFSTORE_TP(CFSTORE_TP_VERBOSE3, "%s:cfstore_ctx_g.area_0_tail=%8p\n", tag, ctx->area_0_tail); + ret = cfstore_get_head_hkvt(&hkvt); + if(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND){ + CFSTORE_TP(CFSTORE_TP_VERBOSE1, "%s:CFSTORE has no KVs\n", tag); + goto out0; + } else if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: could not get head of list.\n", tag); + goto out0; + } + while(cfstore_get_next_hkvt(&hkvt, &hkvt) != ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND) + { + cfstore_hkvt_dump(&hkvt, tag); + } +out0: + CFSTORE_TP(CFSTORE_TP_VERBOSE3, "%s:*** Dumping CFSTORE Contents : End ***\n", tag); + return; +} + + + +#else +static CFSTORE_INLINE void cfstore_hkvt_dump(cfstore_area_hkvt_t* hkvt, const char* tag){ (void) hkvt; (void) tag; return; } +static CFSTORE_INLINE void cfstore_file_dump(cfstore_file_t* file, const char* tag){ (void) file; (void) tag; return; } +static CFSTORE_INLINE void cfstore_dump_contents(const char* tag){ (void) tag; return; } +static CFSTORE_INLINE void cfstore_flags_dump(ARM_CFSTORE_FMODE flag, const char* tag){ (void) flag; (void) tag; return; } +#endif /*CFSTORE_DEBUG*/ + +/* + * CS operations + */ + +/* @brief See definition in configuration_store.h for description. */ +ARM_DRIVER_VERSION cfstore_get_version(void) +{ + /* getting version info doesnt change the sram area so this can happen independently of + * an oustanding async operation. its unnecessary to check the fsm state */ + return cfstore_driver_version_g; +} + + +/* + * CS API Key-Value operations + */ + +/* @brief See definition in configuration_store.h for description. */ +static int32_t cfstore_delete(ARM_CFSTORE_HANDLE hkey) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_area_hkvt_t hkvt; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + cfstore_client_notify_data_t notify_data; + + CFSTORE_TP((CFSTORE_TP_DELETE|CFSTORE_TP_FENTRY), "%s:entered\n", __func__); + if(!cfstore_ctx_is_initialised(ctx)) { + CFSTORE_TP(CFSTORE_TP_DELETE, "%s:Error: CFSTORE is not initialised.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_UNINITIALISED; + goto out0; + } + /* deleting a key will change the sram area while a logging/flushing operation is pending, which + * should not happen while an async operation is outstanding */ + if(cfstore_flash_journal_is_async_op_pending(ctx)) { + CFSTORE_TP(CFSTORE_TP_DELETE, "%s:Debug: flash journal operation pending (awaiting asynchronous notification).\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_OPERATION_PENDING; + goto out0; + } + ret = cfstore_validate_handle(hkey); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid handle.\n", __func__); + goto out0; + } + if(!cfstore_is_kv_client_deletable((cfstore_file_t*) hkey)){ + CFSTORE_ERRLOG("%s:Error: client is not permitted to delete KV.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_NO_PERMISSIONS; + goto out0; + } + hkvt = cfstore_get_hkvt(hkey); + /* check its a valid hkvt */ + if(!cfstore_hkvt_is_valid(&hkvt, ctx->area_0_tail)){ + CFSTORE_ERRLOG("%s:ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE; + goto out0; + } + /* set the delete flag so the delete occurs when the file is closed + * no further handles will be returned to this key */ + cfstore_hkvt_set_flags_delete(&hkvt, true); +out0: + /* Delete() always completes synchronously irrespective of flash mode, so indicate to caller */ + cfstore_client_notify_data_init(¬ify_data, CFSTORE_OPCODE_DELETE, ret, NULL); + cfstore_ctx_client_notify(ctx, ¬ify_data); + return ret; +} + + +/** @brief Internal find function using hkvt's. + * + * @note + * Not the following: + * - Any required locks should be taken before this function is called. + * This function does not affect refcount for underlying KVs. + * - The function assumes the arguments have been validated before calling this function + * - No acl policy is enforced by the function. + * + * @return return_value + * On success (finding a KV matching the query) ARM_DRIVER_OK is + * returned. If a KV is not found matching the description then + * ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND is returned. + */ +static int32_t cfstore_find_ex(const char* key_name_query, cfstore_area_hkvt_t *prev, cfstore_area_hkvt_t *next) +{ + int32_t ret = ARM_DRIVER_ERROR; + uint8_t next_key_len; + char key_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + CFSTORE_TP((CFSTORE_TP_FIND|CFSTORE_TP_FENTRY), "%s:entered: key_name_query=\"%s\", prev=%p, next=%p\n", __func__, key_name_query, prev, next); + if(prev == NULL){ + ret = cfstore_get_head_hkvt(next); + /* CFSTORE_TP(CFSTORE_TP_FIND, "%s:next->head=%p, next->key=%p, next->value=%p, next->tail=%p, \n", __func__, next->head, next->key, next->value, next->tail); */ + if(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND){ + CFSTORE_TP(CFSTORE_TP_FIND, "%s:CFSTORE has no KVs\n", __func__); + return ret; + } else if(ret < ARM_DRIVER_OK) { + CFSTORE_TP(CFSTORE_TP_FIND, "%s:failed to find the first KV in area\n", __func__); + return ret; + } + + /* check for no KVs in the store => hkvt is not valid */ + if(!cfstore_hkvt_is_valid(next, ctx->area_0_tail)){ + /* no KVs in store */ + CFSTORE_TP(CFSTORE_TP_FIND, "%s:hkvt is not valid\n", __func__); + return ARM_DRIVER_OK; + } + + } else { + /* CFSTORE_TP(CFSTORE_TP_FIND, "%s:getting hkvt from prev\n", __func__);*/ + ret = cfstore_get_next_hkvt(prev, next); + if(ret < ARM_DRIVER_OK){ + /* no more matching entries or error. + * either way, return*/ + return ret; + } + } + if(next->head == NULL){ + /* no entry*/ + CFSTORE_TP(CFSTORE_TP_FIND, "%s:No more entries found\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND; + } + /* CFSTORE_TP(CFSTORE_TP_FIND, "%s:cfstore_ctx_g.area_0_head=%p, cfstore_ctx_g.area_0_tail=%p\n", __func__, cfstore_ctx_g.area_0_head, cfstore_ctx_g.area_0_tail);*/ + cfstore_hkvt_dump(next, __func__); + while(cfstore_hkvt_is_valid(next, ctx->area_0_tail)) + { + /* CFSTORE_TP(CFSTORE_TP_FIND, "%s:next->head=%p, next->key=%p, next->value=%p, next->tail=%p, \n", __func__, next->head, next->key, next->value, next->tail); */ + cfstore_hkvt_dump(next, __func__); + + /* if this KV is deleting then proceed to the next item */ + // Note: It is probably better not to enforce policy in this function + // but in the client + if(cfstore_hkvt_get_flags_delete(next)){ + ret = cfstore_get_next_hkvt(next, next); + if(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND) { + CFSTORE_TP(CFSTORE_TP_FIND, "%s:No more KVs found\n", __func__); + break; + } + continue; + } + /* if this KV is not readable by the client then proceed to the next item */ + // Note: It is probably better not to enforce policy in this function + // but in the client + if(!cfstore_is_kv_client_readable(next)){ + ret = cfstore_get_next_hkvt(next, next); + if(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND) { + CFSTORE_TP(CFSTORE_TP_FIND, "%s:No more KVs found\n", __func__); + break; + } + continue; + } + /* check if this key_name matches the query */ + next_key_len = cfstore_hkvt_get_key_len(next); + next_key_len++; + cfstore_get_key_name_ex(next, key_name, &next_key_len); + ret = fnmatch(key_name_query, key_name, 0); + if(ret == 0){ + /* found the entry in the store. return handle */ + CFSTORE_TP(CFSTORE_TP_FIND, "%s:Found matching key (key_name_query = \"%s\", next->key = \"%s\"),next_key_len=%d\n", __func__, key_name_query, key_name, (int) next_key_len); + cfstore_hkvt_dump(next, __func__); + return ARM_DRIVER_OK; + } else if(ret != FNM_NOMATCH){ + CFSTORE_ERRLOG("%s:Error: fnmatch() error (ret=%" PRId32 ").\n", __func__, ret); + return ARM_DRIVER_ERROR; + } + /* FNM_NOMATCH => get the next hkvt if any */ + ret = cfstore_get_next_hkvt(next, next); + if(ret == ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND) { + CFSTORE_TP(CFSTORE_TP_FIND, "%s:No more KVs found\n", __func__); + break; + } + } + return ARM_DRIVER_OK; +} + + +/* @brief See definition in configuration_store.h for description. */ +static int32_t cfstore_find(const char* key_name_query, const ARM_CFSTORE_HANDLE previous, ARM_CFSTORE_HANDLE next) +{ + char key_name[CFSTORE_KEY_NAME_MAX_LENGTH+1]; + uint8_t key_len = 0; + cfstore_area_hkvt_t hkvt_next; + cfstore_area_hkvt_t hkvt_previous; + cfstore_area_hkvt_t *phkvt_previous = NULL; + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_FMODE fmode; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + cfstore_client_notify_data_t notify_data; + + CFSTORE_ASSERT(next != NULL); + CFSTORE_FENTRYLOG("%s:entered: key_name_query=\"%s\", previous=%p, next=%p\n", __func__, key_name_query, previous, next); + if(!cfstore_ctx_is_initialised(ctx)) { + CFSTORE_ERRLOG("%s:Error: CFSTORE is not initialised.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_UNINITIALISED; + goto out1; + } + /* finding a key doesnt change the sram area so this can happen independently of + * an oustanding async operation. its unnecessary to check the fsm state */ + ret = cfstore_validate_key_name_query(key_name_query); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid key_name.\n", __func__); + goto out1; + } + ret = cfstore_validate_handle(next); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid next argument.\n", __func__); + goto out1; + } + /* note previous can be NULL if this is the first call the find */ + memset(&hkvt_next, 0, sizeof(hkvt_next)); + memset(&fmode, 0, sizeof(fmode)); + if(previous != NULL && cfstore_file_is_valid(previous, ctx)){ + ret = cfstore_validate_handle(previous); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid handle.\n", __func__); + goto out1; + } + phkvt_previous = &hkvt_previous; + memset(phkvt_previous, 0, sizeof(hkvt_previous)); + hkvt_previous = cfstore_get_hkvt(previous); + cfstore_hkvt_dump(&hkvt_previous, __func__); + if(!cfstore_hkvt_is_valid(phkvt_previous, ctx->area_0_tail)){ + ret = ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE; + goto out1; + } + } else if(!cfstore_file_is_empty(previous)){ + ret = ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE_BUF; + goto out1; + } + ret = cfstore_find_ex(key_name_query, phkvt_previous, &hkvt_next); + if(ret < ARM_DRIVER_OK){ + /* either no more entries or error but either way, return */ + CFSTORE_TP(CFSTORE_TP_FIND, "%s:No more KVs found.\n", __func__); + goto out2; + } + + if(!cfstore_hkvt_is_valid(&hkvt_next, ctx->area_0_tail)){ + CFSTORE_TP(CFSTORE_TP_FIND, "%s:Did not find any matching KVs.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND; + goto out2; + } + + /* Have a valid kv. cfstore_find_ex() checked the client has + * permission to read the KV, so dont have to perform this check again here. */ + + /* return handle to client */ + cfstore_file_create(&hkvt_next, fmode, next, &ctx->file_list); + ret = ARM_DRIVER_OK; +out2: + /* previous handle is being returned to CFSTORE with this call so destroy file struct */ + if(previous != NULL && cfstore_file_is_valid(previous, ctx)) + { + /* do not use ret in this stanza as will loose return state from above */ + /* CFSTORE_TP(CFSTORE_TP_FIND, "%s:about to destroy KV, previous=%p.\n", __func__, previous); */ + cfstore_file_dump((cfstore_file_t*) previous, __func__); + + key_len = CFSTORE_KEY_NAME_MAX_LENGTH+1; + memset(key_name, 0, CFSTORE_KEY_NAME_MAX_LENGTH+1); + if(cfstore_get_key_name_ex(&hkvt_next, key_name, &key_len) < ARM_DRIVER_OK){ + /* either no more entries or error but either way, return */ + CFSTORE_TP(CFSTORE_TP_FIND, "%s:debug: cfstore_get_key_name_ex failed or no more kvs.\n", __func__); + goto out1; + } + cfstore_file_destroy(cfstore_file_get(previous)); + /* now get hkvt_next again based on the name to overcome the fact that the hkvt + * may be invalid due to the possible deletion of the previous KV.x */ + if(cfstore_find_ex(key_name, NULL, &hkvt_next) < ARM_DRIVER_OK){ + /* either no more entries or error but either way, return */ + CFSTORE_TP(CFSTORE_TP_FIND, "%s:find failed key_name=%s ret=%" PRId32 ".\n", __func__, key_name, ret); + goto out1; + } + cfstore_hkvt_dump(&hkvt_next, __func__); + } +out1: + /* Find() always completes synchronously irrespective of flash mode, so indicate to caller */ + cfstore_client_notify_data_init(¬ify_data, CFSTORE_OPCODE_FIND, ret, next); + cfstore_ctx_client_notify(ctx, ¬ify_data); + return ret; +} + + +/* @brief grow/shrink pre-existing KV. + * + * @note rw_lock must be held by the caller of this function rw_area0_lock */ +static int32_t cfstore_recreate(const char* key_name, ARM_CFSTORE_SIZE value_len, ARM_CFSTORE_HANDLE hkey, cfstore_area_hkvt_t* hkvt) +{ + uint8_t* ptr = NULL; + int32_t kv_size_diff = 0; + ARM_CFSTORE_SIZE area_size = 0; + ARM_CFSTORE_FMODE flags; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + CFSTORE_FENTRYLOG("%s:entered: key_name=\"%s\", value_len=%d\n", __func__, key_name, (int) value_len); + cfstore_dump_contents(__func__); + memset(&flags, 0, sizeof(flags)); + flags.read = true; + flags.write = true; + kv_size_diff = value_len - cfstore_hkvt_get_value_len(hkvt); + if(kv_size_diff == 0){ + /* nothing more to do*/ + CFSTORE_TP(CFSTORE_TP_CREATE, "%s:new value length the same as the old\n", __func__); + return ARM_DRIVER_OK; + } + + CFSTORE_TP(CFSTORE_TP_CREATE, "%s:cfstore_ctx_g.area_0_head=%p, cfstore_ctx_g.area_0_tail=%p\n", __func__, ctx->area_0_head, ctx->area_0_tail); + CFSTORE_TP(CFSTORE_TP_CREATE, "%s:sizeof(header)=%d, sizeof(key)=%d, sizeof(value)=%d, kv_size_diff=%d, area_size=%d\n", __func__, (int) sizeof(cfstore_area_header_t), (int)(strlen(key_name)), (int)value_len, (int) kv_size_diff, (int) area_size); + + /* grow the area by the size of the new KV */ + area_size = cfstore_ctx_get_area_len(); + if (kv_size_diff < 0){ + /* value blob size shrinking => do memmove() before realloc() which will free memory */ + memmove(hkvt->tail + kv_size_diff, hkvt->tail, ctx->area_0_tail - hkvt->tail); + //todo: wip: do we have to update file pointers for KVs after the one thats changed size? + } + ptr = (uint8_t*) CFSTORE_REALLOC((void*) ctx->area_0_head, area_size + kv_size_diff); + if(ptr == NULL){ + CFSTORE_ERRLOG("%s:realloc failed for key_name=%s\n", __func__, key_name); + cfstore_ctx_reset(ctx); + return ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY; + } + if(ptr != ctx->area_0_head){ + CFSTORE_TP(CFSTORE_TP_CREATE, "%s:cfstore_ctx_g.area_0_head pointer changed (cfstore_ctx_g.area_0_head=%p, cfstore_ctx_g.area_0_tail=%p)\n", __func__, ctx->area_0_head, ptr); + /* This covers the cases where CFSTORE_REALLOC() has moved the area in memory + * As realloc() has caused the memory to move, hkvt needs re-initialising */ + hkvt->head += ptr - ctx->area_0_head; + hkvt->key += ptr - ctx->area_0_head; + hkvt->value += ptr - ctx->area_0_head; + hkvt->tail += ptr - ctx->area_0_head; + /* Set head and tail to old relative position in new area */ + ctx->area_0_head = ptr; + ctx->area_0_tail = ctx->area_0_head + area_size; + //todo: wip: do we have to update file pointers for KVs after the memory has moved? + } + + if(kv_size_diff > 0) { + /* value blob size growing requires memmove() after realloc() */ + memset(ctx->area_0_tail, 0, kv_size_diff); + memmove(hkvt->tail+kv_size_diff, hkvt->tail, ctx->area_0_tail - hkvt->tail); + //todo: wip: do we have to update file pointers for KVs after the one thats changed size? + } + /* hkvt->head, hkvt->key and hkvt->value remain unchanged but hkvt->tail has moved. Update it.*/ + hkvt->tail = hkvt->tail + kv_size_diff; + + /* set the new value length in the header */ + cfstore_hkvt_set_value_len(hkvt, value_len); + ctx->area_0_tail = ctx->area_0_head + area_size + kv_size_diff; + cfstore_file_create(hkvt, flags, hkey, &ctx->file_list); + ctx->area_dirty_flag = true; + +#ifdef CFSTORE_DEBUG + cfstore_hkvt_dump(hkvt, __func__); + cfstore_dump_contents(__func__); +#endif + return ARM_DRIVER_OK; +} + + +/* @brief See definition in configuration_store.h for description. */ +static int32_t cfstore_create(const char* key_name, ARM_CFSTORE_SIZE value_len, const ARM_CFSTORE_KEYDESC* kdesc, ARM_CFSTORE_HANDLE hkey) +{ + bool b_acl_default = false; + uint8_t* ptr = NULL; + int32_t ret = ARM_DRIVER_ERROR; + int32_t cfstore_uvisor_box_id = 0; + ARM_CFSTORE_SIZE area_size = 0; + ARM_CFSTORE_SIZE kv_size = 0; + ARM_CFSTORE_SIZE realloc_size = 0; + cfstore_area_header_t* hdr; + cfstore_area_hkvt_t hkvt; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + ARM_CFSTORE_FMODE flags; + cfstore_client_notify_data_t notify_data; + + CFSTORE_FENTRYLOG("%s:entered: key_name=\"%s\", value_len=%d, kdesc=%p\n", __func__, key_name, (int)value_len, kdesc); + CFSTORE_ASSERT(kdesc != NULL); + CFSTORE_ASSERT(hkey != NULL); + + memset(&flags, 0, sizeof(flags)); + if(!cfstore_ctx_is_initialised(ctx)) { + CFSTORE_ERRLOG("%s:Error: CFSTORE is not initialised.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_UNINITIALISED; + } + /* creating a key cannot happen while a flashJournal_log() is pending as it would change the sram area being logged*/ + if(cfstore_flash_journal_is_async_op_pending(ctx)) { + CFSTORE_TP(CFSTORE_TP_CREATE, "%s:Debug: flash journal operation pending (awaiting asynchronous notification).\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_OPERATION_PENDING; + } + ret = cfstore_validate_key_name(key_name); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid key_name (%s).\n", __func__, key_name); + goto out0; + } + ret = cfstore_validate_handle(hkey); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid handle.\n", __func__); + goto out0; + } + ret = cfstore_validate_value_len(value_len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid key_name.\n", __func__); + goto out0; + } + /* check uvisor security */ + if(cfstore_is_client_kv_owner(key_name, &cfstore_uvisor_box_id) != ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: Client has insufficient permissions to create KV.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_NO_PERMISSIONS; + goto out0; + } + /* the cfstore (uvisor) client is the owner of the KV and therefore is permitted to created it */ + /* A null kdesc is permitted if client is growing/shrinking pre-existing key. + * Hence, find if key_name pre-exists before validating kdesc */ + ret = cfstore_find_ex(key_name, NULL, &hkvt); + if(ret < ARM_DRIVER_OK && ret != ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND){ + CFSTORE_ERRLOG("%s:CFSTORE find() returned error (%" PRId32 ")\n", __func__, ret); + goto out1; + } + + if(ret != ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND && cfstore_hkvt_is_valid(&hkvt, ctx->area_0_tail)){ + /* found pre-existing entry; */ + if(cfstore_hkvt_get_flags_delete(&hkvt)){ + CFSTORE_ERRLOG("%s:CFSTORE pre-existing KV with key_name=\"%s\" deleting\n", __func__, key_name); + ret = ARM_CFSTORE_DRIVER_ERROR_PREEXISTING_KEY_DELETING; + goto out1; + } + if(kdesc != NULL) { + CFSTORE_ERRLOG("%s:CFSTORE contains pre-existing KV with key_name=\"%s\". Cannot create a new KV with the same name\n", __func__, key_name); + ret = ARM_CFSTORE_DRIVER_ERROR_PREEXISTING_KEY; + goto out1; + } + + /* client is requesting to grow/shrink pre-existing key */ + ret = cfstore_recreate(key_name, value_len, hkey, &hkvt); + goto out1; + } + /* not a valid hkvt implying the key_name wasnt found */ + + /* create new key */ + ret = cfstore_validate_key_desc(kdesc); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid key descriptor.\n", __func__); + goto out1; + } + /* insert the KV into the area */ + kv_size = strlen(key_name); + kv_size += value_len; + kv_size += sizeof(cfstore_area_header_t); + + /* grow the area by the size of the new KV + * In the general case the new ((area_size + kv_size) % program_unit > 0). The new area_size is + * aligned to a program_unit boundary to facilitate r/w to flash and so the memory realloc size + * is calculated to align, as follows */ + area_size = cfstore_ctx_get_area_len(); + // moved to flash_init() and program_unit stored in ctx + /* + status = FlashJournal_getInfo(&ctx->jrnl, &info); + if(status < JOURNAL_STATUS_OK){ + CFSTORE_TP(CFSTORE_TP_CREATE, "%s:Error: failed get journal info (status=%d)\n", __func__, (int) status); + ret = cfstore_flash_map_error(status); + goto out1; + } + */ + /* setup the reallocation memory size. */ + realloc_size = area_size + kv_size; + if(realloc_size % cfstore_ctx_get_program_unit(ctx) > 0){ + realloc_size += (cfstore_ctx_get_program_unit(ctx) - (realloc_size % cfstore_ctx_get_program_unit(ctx))); + } + ptr = (uint8_t*) CFSTORE_REALLOC((void*) ctx->area_0_head, realloc_size); + if(ptr == NULL){ + CFSTORE_ERRLOG("%s:realloc failed for key_name=%s\n", __func__, key_name); + cfstore_ctx_reset(ctx); + ret = ARM_CFSTORE_DRIVER_ERROR_OUT_OF_MEMORY; + goto out1; + } + if(ptr != ctx->area_0_head){ + CFSTORE_TP(CFSTORE_TP_CREATE, "%s:cfstore_ctx_g.area_0_head pointer changed (cfstore_ctx_g.area_0_head=%p, cfstore_ctx_g.area_0_tail=%p)\n", __func__, ctx->area_0_head, ptr); + /* this covers the following cases: + * - this is the first KV insertion in the area, which is special as both area head/tail pointers need setting. + * - realloc() has move the area in memory */ + ctx->area_0_head = ptr; + ctx->area_0_tail = ctx->area_0_head + area_size; + } + + /* check realloc() hasnt move area in memory from cfstore_ctx_g.area_0_head*/ + CFSTORE_TP(CFSTORE_TP_CREATE, "%s:cfstore_ctx_g.area_0_head=%p, ptr=%p\n", __func__, ctx->area_0_head, ptr); + + /* determine if should adopt a default behavior for acl permission setting */ + if(cfstore_acl_is_default(kdesc->acl)){ + /* set as read-write by default default */ + CFSTORE_TP(CFSTORE_TP_CREATE, "%s:Note: No ACL bits set. Adopting default permissions of owner read and write.\n", __func__); + b_acl_default = true; + } + /* set the header up, then copy key_name into header */ + memset(ctx->area_0_tail, 0, kv_size); + hdr = (cfstore_area_header_t*) ctx->area_0_tail; + hdr->klength = (uint8_t) strlen(key_name); + hdr->vlength = value_len; + hdr->perm_owner_read = b_acl_default ? true : kdesc->acl.perm_owner_read; + hdr->perm_owner_write = b_acl_default ? true : kdesc->acl.perm_owner_write; + hdr->perm_owner_execute = kdesc->acl.perm_owner_execute; + hdr->perm_other_read = kdesc->acl.perm_other_read; + hdr->perm_other_write = kdesc->acl.perm_other_write; + hdr->perm_other_execute = kdesc->acl.perm_other_execute; + strncpy((char*)hdr + sizeof(cfstore_area_header_t), key_name, strlen(key_name)); + /* Updating the area_0_tail pointer reveals the inserted KV to other operations. See [NOTE1] for details.*/ + ctx->area_0_tail = ctx->area_0_head + area_size + kv_size; + hkvt = cfstore_get_hkvt_from_head_ptr((uint8_t*) hdr); + if(cfstore_flags_is_default(kdesc->flags)){ + /* set as read-only by default default */ + flags.read = true; + flags.write = true; + } else { + flags.read = kdesc->flags.read; + flags.write = kdesc->flags.write; + } + cfstore_file_create(&hkvt, flags, hkey, &ctx->file_list); + ctx->area_dirty_flag = true; + ret = ARM_DRIVER_OK; +out1: + cfstore_hkvt_dump(&hkvt, __func__); +out0: + cfstore_dump_contents(__func__); + /* Create() always completes synchronously irrespective of flash mode, so indicate to caller */ + cfstore_client_notify_data_init(¬ify_data, CFSTORE_OPCODE_CREATE, ret, hkey); + cfstore_ctx_client_notify(ctx, ¬ify_data); + return ret; +} + + +/* @brief See definition in configuration_store.h for description. */ +static int32_t cfstore_open(const char* key_name, ARM_CFSTORE_FMODE flags, ARM_CFSTORE_HANDLE hkey) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_area_hkvt_t hkvt; + cfstore_file_t *file = NULL; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + cfstore_client_notify_data_t notify_data; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + cfstore_flags_dump(flags, __func__); + CFSTORE_ASSERT(key_name != NULL); + CFSTORE_ASSERT(hkey != NULL); + if(!cfstore_ctx_is_initialised(ctx)) { + CFSTORE_ERRLOG("%s:Error: CFSTORE is not initialised.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_UNINITIALISED; + goto out1; + } + ret = cfstore_validate_key_name(key_name); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid key_name.\n", __func__); + goto out1; + } + ret = cfstore_validate_fmode_flags(flags); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid flags.\n", __func__); + goto out1; + } + if(flags.write){ + /* opening a pre-existing key for writing can result in the sram area being changed, which + * cannot happen while a flashJournal_xxx() async completion notification is outstanding */ + if(cfstore_flash_journal_is_async_op_pending(ctx)) { + CFSTORE_TP(CFSTORE_TP_OPEN, "%s:Debug: flash journal operation pending (awaiting asynchronous notification).\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_OPERATION_PENDING; + goto out1; + } + } + ret = cfstore_validate_handle(hkey); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid handle.\n", __func__); + goto out1; + } + /* find the KV and return a handle */ + cfstore_hkvt_init(&hkvt); + ret = cfstore_find_ex(key_name, NULL, &hkvt); + if(ret < ARM_DRIVER_OK){ + /* either no more entries or error but either way, return */ + CFSTORE_TP(CFSTORE_TP_OPEN, "%s:debug: find failed or no more kvs.\n", __func__); + goto out1; + } + if(!cfstore_hkvt_is_valid(&hkvt, ctx->area_0_tail)) + { + CFSTORE_ERRLOG("%s:Error: Could not find pre-existing key to open with key_name=(%s).\n", __func__, key_name); + ret = ARM_CFSTORE_DRIVER_ERROR_KEY_NOT_FOUND; + goto out1; + } + /* if this KV is deleting then do not allow item to be opened */ + if(cfstore_hkvt_get_flags_delete(&hkvt)){ + CFSTORE_ERRLOG("%s:Error: Pre-existing key key_name=(%s) is deleting.\n", __func__, key_name); + ret = ARM_CFSTORE_DRIVER_ERROR_PREEXISTING_KEY_DELETING; + goto out1; + } + /* key found, check permissions */ + if(cfstore_flags_is_default(flags)){ + /* set as read-only by default default */ + flags.read = true; + } + if(flags.read == true && !cfstore_is_kv_client_readable(&hkvt)){ + CFSTORE_ERRLOG("%s:Error: Client has no read access to KV (key_name=%s).\n", __func__, key_name); + ret = ARM_CFSTORE_DRIVER_ERROR_PERM_NO_READ_ACCESS; + goto out1; + } + if(flags.write == true && !cfstore_is_kv_client_writable(&hkvt)){ + CFSTORE_ERRLOG("%s:Error: Client has no write access to KV (key_name=%s).\n", __func__, key_name); + ret = ARM_CFSTORE_DRIVER_ERROR_PERM_NO_WRITE_ACCESS; + goto out1; + } + if(flags.execute == true && !cfstore_is_kv_client_executable(&hkvt)){ + CFSTORE_ERRLOG("%s:Error: Client has no execute access to KV (key_name=%s).\n", __func__, key_name); + ret = ARM_CFSTORE_DRIVER_ERROR_PERM_NO_EXECUTE_ACCESS; + goto out1; + } + /* return handle to client */ + file = cfstore_file_create(&hkvt, flags, hkey, &ctx->file_list); + if(file) { + cfstore_file_dump(file, __func__); + } else { + CFSTORE_ERRLOG("%s:Error: failed to create file (key_name=%s).\n", __func__, key_name); + } +out1: + /* Open() always completes synchronously irrespective of flash mode, so indicate to caller */ + cfstore_client_notify_data_init(¬ify_data, CFSTORE_OPCODE_OPEN, ret, hkey); + cfstore_ctx_client_notify(ctx, ¬ify_data); + return ret; +} + + +/* @brief See definition in configuration_store.h for description. */ +static int32_t cfstore_close(ARM_CFSTORE_HANDLE hkey) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + cfstore_client_notify_data_t notify_data; + cfstore_area_hkvt_t hkvt; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + if(!cfstore_ctx_is_initialised(ctx)) { + CFSTORE_ERRLOG("%s:Error: CFSTORE is not initialised.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_UNINITIALISED; + goto out0; + } + /* closing a key can lead to its deletion, which cannot happening while there are pending + * async operations outstanding */ + if(cfstore_flash_journal_is_async_op_pending(ctx)) { + CFSTORE_TP(CFSTORE_TP_CLOSE, "%s:Debug: flash journal operation pending (awaiting asynchronous notification).\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_OPERATION_PENDING; + goto out0; + } + ret = cfstore_validate_handle(hkey); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid hkey argument.\n", __func__); + goto out0; + } + /* check the hkey is valid */ + hkvt = cfstore_get_hkvt(hkey); + if(!cfstore_hkvt_is_valid(&hkvt, ctx->area_0_tail)){ + CFSTORE_ERRLOG("%s:ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE; + goto out0; + } + if(!cfstore_is_kv_client_closable((cfstore_file_t*) hkey)){ + CFSTORE_ERRLOG("%s:Error: client is not permitted to close KV.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_NO_PERMISSIONS; + goto out0; + } + /* delete the file associated with this open handle */ + CFSTORE_TP(CFSTORE_TP_CLOSE, "%s:about to call cfstore_file_destroy().\n", __func__); + cfstore_file_dump((cfstore_file_t*) hkey, __func__); + ret = cfstore_file_destroy(cfstore_file_get(hkey)); +out0: + /* Close() always completes synchronously irrespective of flash mode, so indicate to caller */ + cfstore_client_notify_data_init(¬ify_data, CFSTORE_OPCODE_CLOSE, ret, NULL); + cfstore_ctx_client_notify(ctx, ¬ify_data); + return ret; +} + + +/* @brief See definition in configuration_store.h for description. */ +static int32_t cfstore_read(ARM_CFSTORE_HANDLE hkey, void* data, ARM_CFSTORE_SIZE* len) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE read_len = 0; + cfstore_area_hkvt_t hkvt; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + cfstore_file_t* file = cfstore_file_get(hkey); + cfstore_client_notify_data_t notify_data; + + CFSTORE_ASSERT(data); + CFSTORE_ASSERT(len); + CFSTORE_FENTRYLOG("%s:entered, hkey=%p\n", __func__, hkey); + if(!cfstore_ctx_is_initialised(ctx)) { + CFSTORE_ERRLOG("%s:Error: CFSTORE is not initialised.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_UNINITIALISED; + goto out0; + } + /* reading KVs doesnt change the sram area so this can happen independently of + * an oustanding async operation. its unnecessary to check the fsm state */ + ret = cfstore_validate_handle(hkey); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid handle.\n", __func__); + goto out0; + } + if(data == NULL){ + CFSTORE_ERRLOG("%s:Error: invalid read data buffer.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_INVALID_READ_BUFFER; + goto out0; + } + ret = cfstore_validate_len_ptr(len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid len argument.\n", __func__); + goto out0; + } + cfstore_hkvt_init(&hkvt); + hkvt = cfstore_get_hkvt(hkey); + /* check the hkey is valid */ + if(!cfstore_hkvt_is_valid(&hkvt, ctx->area_0_tail)){ + CFSTORE_ERRLOG("%s:ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE; + goto out0; + } + + if(!cfstore_is_kv_client_readable(&hkvt)){ + CFSTORE_ERRLOG("%s:Error: client does not have permission to read KV.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_PERM_NO_READ_ACCESS; + goto out0; + } + read_len = *len <= (cfstore_hkvt_get_value_len(&hkvt) - file->rlocation) ? *len : cfstore_hkvt_get_value_len(&hkvt) - file->rlocation; + memcpy(data, hkvt.value + file->rlocation, read_len); + file->rlocation += read_len; + *len = read_len; + ret = read_len; +out0: + /* Read() always completes synchronously irrespective of flash mode, so indicate to caller */ + cfstore_client_notify_data_init(¬ify_data, CFSTORE_OPCODE_READ, ret, hkey); + cfstore_ctx_client_notify(ctx, ¬ify_data); + return ret; +} + + +/* @brief See definition in configuration_store.h for description. */ +static int32_t cfstore_write(ARM_CFSTORE_HANDLE hkey, const char* data, ARM_CFSTORE_SIZE* len) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_CFSTORE_SIZE value_len = 0; + cfstore_area_hkvt_t hkvt; + cfstore_file_t* file = cfstore_file_get(hkey); + cfstore_ctx_t* ctx = cfstore_ctx_get(); + cfstore_client_notify_data_t notify_data; + + CFSTORE_FENTRYLOG("%s:entered, hkey=%p\n", __func__, hkey); + CFSTORE_ASSERT(hkey != NULL); + CFSTORE_ASSERT(len != NULL); + if(!cfstore_ctx_is_initialised(ctx)) { + CFSTORE_ERRLOG("%s:Error: CFSTORE is not initialised.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_UNINITIALISED; + goto out0; + } + /* writing a key cannot happen while a flashJournal_xxx() async operation is pending */ + if(cfstore_flash_journal_is_async_op_pending(ctx)) { + CFSTORE_TP(CFSTORE_TP_WRITE, "%s:Debug: flash journal operation pending (awaiting asynchronous notification).\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_OPERATION_PENDING; + goto out0; + } + ret = cfstore_validate_handle(hkey); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid handle.\n", __func__); + goto out0; + } + if(data == NULL){ + CFSTORE_ERRLOG("%s:Error: invalid write data buffer.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_INVALID_WRITE_BUFFER; + goto out0; + } + ret = cfstore_validate_len_ptr(len); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid len argument.\n", __func__); + goto out0; + } + ret = cfstore_validate_value_len(*len); + if (ret < ARM_DRIVER_OK) { + CFSTORE_ERRLOG("%s:Error: invalid key_name.\n", __func__); + goto out0; + } + /*check file has write permission set */ + if(!file->flags.write){ + CFSTORE_ERRLOG("%s:Error: KV is read-only.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_KEY_READ_ONLY; + goto out0; + } + memset(&hkvt, 0, sizeof(hkvt)); + hkvt = cfstore_get_hkvt(hkey); + if(!cfstore_hkvt_is_valid(&hkvt, ctx->area_0_tail)){ + CFSTORE_ERRLOG("%s:Error: ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE; + goto out0; + } + if(!cfstore_is_kv_client_writable(&hkvt)){ + CFSTORE_ERRLOG("%s:Error: client does not have permission to write KV.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_PERM_NO_WRITE_ACCESS; + goto out0; + } + value_len = (ARM_CFSTORE_SIZE) cfstore_hkvt_get_value_len(&hkvt); + *len = *len < value_len ? *len: value_len; + memcpy(hkvt.value + file->wlocation, data, *len); + file->wlocation += *len; + cfstore_hkvt_dump(&hkvt, __func__); + ctx->area_dirty_flag = true; + ret = *len; +out0: + /* Write() always completes synchronously irrespective of flash mode, so indicate to caller */ + cfstore_client_notify_data_init(¬ify_data, CFSTORE_OPCODE_WRITE, ret, hkey); + cfstore_ctx_client_notify(ctx, ¬ify_data); + return ret; +} + + +/* @brief See definition in configuration_store.h for description. */ +static int32_t cfstore_rseek(ARM_CFSTORE_HANDLE hkey, ARM_CFSTORE_OFFSET offset) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_area_hkvt_t hkvt; + cfstore_file_t* file = cfstore_file_get(hkey); + cfstore_ctx_t* ctx = cfstore_ctx_get(); + cfstore_client_notify_data_t notify_data; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + if(!cfstore_ctx_is_initialised(ctx)) { + CFSTORE_ERRLOG("%s:Error: CFSTORE is not initialised.\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_UNINITIALISED; + } + /* read-seeking KVs doesnt change the sram area so this can happen independently of + * an oustanding async operation. its unnecessary to check the fsm state */ + ret = cfstore_validate_handle(hkey); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: invalid handle.\n", __func__); + return ret; + } + ret = cfstore_validate_value_len(offset); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: offset (%u) greater than maximum value blob size (%u).\n", __func__, (unsigned int) offset, CFSTORE_VALUE_SIZE_MAX); + return ret; + } + if(!file->flags.read){ + CFSTORE_ERRLOG("%s:Error: KV is not readable.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_KEY_UNREADABLE; + goto out0; + + } + cfstore_hkvt_init(&hkvt); + hkvt = cfstore_get_hkvt(hkey); + if(!cfstore_hkvt_is_valid(&hkvt, ctx->area_0_tail)){ + CFSTORE_ERRLOG("%s:Error: ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_INVALID_HANDLE; + goto out0; + } + if(!cfstore_is_kv_client_readable(&hkvt)){ + CFSTORE_ERRLOG("%s:Error: client does not have permission to read KV.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_PERM_NO_READ_ACCESS; + goto out0; + } + /* check offset is in range */ + if(offset > cfstore_hkvt_get_value_len(&hkvt)){ + CFSTORE_ERRLOG("%s:Error: seeking beyond end of value.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_INVALID_SEEK; + goto out0; + + } + file->rlocation = offset; + ret = (int32_t) offset; +out0: + /* Rseek() always completes synchronously irrespective of flash mode, so indicate to caller */ + cfstore_client_notify_data_init(¬ify_data, CFSTORE_OPCODE_RSEEK, ret, hkey); + cfstore_ctx_client_notify(ctx, ¬ify_data); + return ret; +} + + +/* @brief See definition in configuration_store.h for description. */ +static int32_t cfstore_flush(void) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + if(!cfstore_ctx_is_initialised(ctx)) { + CFSTORE_ERRLOG("%s:Error: CFSTORE is not initialised.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_UNINITIALISED; + goto out0; + } + /* only 1 flush operation can be outstanding so check whether one is already in progress */ + if(cfstore_flash_journal_is_async_op_pending(ctx)) { + CFSTORE_TP(CFSTORE_TP_FLUSH, "%s:Debug: flash journal operation pending (awaiting asynchronous notification).\n", __func__); + return ARM_CFSTORE_DRIVER_ERROR_OPERATION_PENDING; + } + ret = cfstore_flash_flush(ctx); + if(ret < ARM_DRIVER_OK) { + CFSTORE_ERRLOG("%s:Error: cfstore_flash_flush() returned error (ret=%" PRId32 ").\n", __func__, ret); + goto out0; + } +out0: + return ret; +} + +/* @brief See definition in configuration_store.h for description. */ +static int32_t cfstore_initialise(ARM_CFSTORE_CALLBACK callback, void* client_context) +{ + int ret = ARM_DRIVER_ERROR; + cfstore_ctx_t* ctx = cfstore_ctx_get(); +#if defined CFSTORE_CONFIG_BACKEND_FLASH_ENABLED && CFSTORE_CONFIG_BACKEND_FLASH_ENABLED == 1 + ARM_STORAGE_CAPABILITIES storage_caps; +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ + + CFSTORE_FENTRYLOG("%s:entered: callback=%p, client_context=%p, ref_count=%d\n", __func__, callback, client_context, (int) ctx->init_ref_count); + /* init cfstore context the first time this method is called + * note ctx->rw_area0_lock has already been initialised */ + + /* CS protection required to get into the fsm into the initing state, without another client g*/ + cfstore_critical_section_lock(&ctx->rw_area0_lock, __func__); + if(ctx->init_ref_count == 0) + { + CFSTORE_TP(CFSTORE_TP_INIT, "%s:debug: first time init\n", __func__); + /* perform first time initialisation */ + ctx->init_ref_count++; + /* initially there is no memory allocated for the area */ + CFSTORE_INIT_LIST_HEAD(&ctx->file_list); + cfstore_critical_section_init(&ctx->rw_area0_lock); + ctx->area_0_head = NULL; + ctx->area_0_tail = NULL; + + CFSTORE_ASSERT(sizeof(cfstore_file_t) == CFSTORE_HANDLE_BUFSIZE); + if(sizeof(cfstore_file_t) != CFSTORE_HANDLE_BUFSIZE){ + CFSTORE_ERRLOG("%s:Error: sizeof(cfstore_file_t)=(%d) != CFSTORE_HANDLE_BUFSIZE (%d)\n", __func__,(int) sizeof(cfstore_file_t), (int) CFSTORE_HANDLE_BUFSIZE); + ret = ARM_CFSTORE_DRIVER_ERROR_INTERNAL; + goto out0; + } + ctx->client_callback = callback; + ctx->client_context = client_context; + ctx->area_dirty_flag = false; + ctx->client_callback_notify_flag = false; + + cfstore_client_notify_data_init(&ctx->client_notify_data, CFSTORE_OPCODE_MAX, ARM_DRIVER_ERROR, NULL); + ctx->power_state = ARM_POWER_FULL; + ctx->status = ARM_DRIVER_OK; + +#if defined CFSTORE_CONFIG_BACKEND_FLASH_ENABLED && CFSTORE_CONFIG_BACKEND_FLASH_ENABLED == 1 + // todo: put in cfstore_flash_init() ? + /* set the cfstore async flag according to the storage driver mode */ + storage_caps = cfstore_storage_drv->GetCapabilities(); + cfstore_caps_g.asynchronous_ops = storage_caps.asynchronous_ops; +#endif /* CFSTORE_CONFIG_BACKEND_FLASH_ENABLED */ + + ret = cfstore_flash_init(); + if(ret < ARM_DRIVER_OK) { + CFSTORE_ERRLOG("%s:Error: failed to initialise flash layer\n", __func__); + goto out0; + } + } + else + { + CFSTORE_TP(CFSTORE_TP_INIT, "%s:debug: n-th time init\n", __func__); + /* initialisation already done so only increment the ref count */ + ctx->init_ref_count++; + ret = ARM_DRIVER_OK; + } + /* if not initialised already, fsm now in the initing state so safe to come out of CS */ + cfstore_critical_section_unlock(&ctx->rw_area0_lock, __func__); +out0: + CFSTORE_FENTRYLOG("%s:exiting: callback=%p, client_context=%p, ref_count=%d\n", __func__, callback, client_context, (int) ctx->init_ref_count); + return ret; +} + + +/* @brief See prototype definition in configuration_store.h for function description. + * + * @note unitialising cfstore results in all entries that have not been flushed being lost + */ +static int32_t cfstore_uninitialise(void) +{ + int32_t ret = ARM_DRIVER_ERROR; + ARM_STORAGE_CAPABILITIES caps; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + memset(&caps, 0, sizeof(caps)); + + if(!cfstore_ctx_is_initialised(ctx)) { + CFSTORE_ERRLOG("%s:Error: CFSTORE is not initialised.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_UNINITIALISED; + goto out; + } + /* only uninitialise when there are no flash journal async operations pending*/ + if(cfstore_flash_journal_is_async_op_pending(ctx)) { + CFSTORE_TP(CFSTORE_TP_INIT, "%s:Debug: flash journal operation pending (awaiting asynchronous notification).\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_OPERATION_PENDING; + goto out; + } + if(ctx->init_ref_count > 0) { + ctx->init_ref_count--; + CFSTORE_TP(CFSTORE_TP_INIT, "%s:Debug: decemented init_ref_count (%" PRId32 ").\n", __func__, ctx->init_ref_count); + } + if(ctx->init_ref_count == 0) + { + CFSTORE_TP(CFSTORE_TP_INIT, "%s:Debug: init_ref_count == 0 (%" PRId32 ") so uninitialising.\n", __func__, ctx->init_ref_count); + /* check file list is empty and if not, free the items */ + if(ctx->file_list.next != ctx->file_list.prev) + { + /* list is not empty. walk the list and free the entries */ + // todo: wip: free items on the file list + } + + ret = cfstore_flash_deinit(); + if(ret < ARM_DRIVER_OK){ + CFSTORE_ERRLOG("%s:Error: failed to uninitialise flash journal layer.\n", __func__); + goto out; + } + if(ctx->area_0_head){ + CFSTORE_FREE(ctx->area_0_head); + ctx->area_0_head = NULL; + ctx->area_0_tail = NULL; + } + } +out: + /* notify client */ + cfstore_client_notify_data_init(&ctx->client_notify_data, CFSTORE_OPCODE_UNINITIALIZE, ret, NULL); + cfstore_ctx_client_notify(ctx, &ctx->client_notify_data); + return ret; +} + + +/* @brief See definition in configuration_store.h for description. */ +static int32_t cfstore_power_control(ARM_POWER_STATE state) +{ + int32_t ret = ARM_DRIVER_ERROR; + cfstore_ctx_t* ctx = cfstore_ctx_get(); + cfstore_client_notify_data_t notify_data; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + if(!cfstore_ctx_is_initialised(ctx)) { + CFSTORE_ERRLOG("%s:Error: CFSTORE is not initialised.\n", __func__); + ret = ARM_CFSTORE_DRIVER_ERROR_UNINITIALISED; + goto out0; + } + /* setting power state doesnt change the sram area so this can happen independently of + * an oustanding async operation. its unnecessary to check the fsm state */ + if(state <= ARM_POWER_FULL){ + ctx->power_state = state; + /* set return to a positive value*/ + ret = (int32_t) state; + } +out0: + /* PowerControl() always completes synchronously irrespective of flash mode, so indicate to caller */ + cfstore_client_notify_data_init(¬ify_data, CFSTORE_OPCODE_POWER_CONTROL, ret, NULL); + cfstore_ctx_client_notify(ctx, ¬ify_data); + return ret; +} + + +#ifdef YOTTA_CFG_CFSTORE_UVISOR + +/* + * uvisor secure gateways for ARM_CFSTORE_DRIVER access methods. + */ + +UVISOR_EXTERN int32_t __cfstore_uvisor_close(ARM_CFSTORE_HANDLE hkey) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return cfstore_close(hkey); +} + +static int32_t cfstore_uvisor_close(ARM_CFSTORE_HANDLE hkey) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return secure_gateway(configuration_store, __cfstore_uvisor_close, hkey); +} + +UVISOR_EXTERN int32_t __cfstore_uvisor_create(const char* key_name, ARM_CFSTORE_SIZE value_len, const ARM_CFSTORE_KEYDESC* kdesc, ARM_CFSTORE_HANDLE hkey) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return cfstore_create(key_name, value_len, kdesc, hkey); +} + +static int32_t cfstore_uvisor_create(const char* key_name, ARM_CFSTORE_SIZE value_len, const ARM_CFSTORE_KEYDESC* kdesc, ARM_CFSTORE_HANDLE hkey) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return secure_gateway(configuration_store, __cfstore_uvisor_create, key_name, value_len, kdesc, hkey); +} + +UVISOR_EXTERN int32_t __cfstore_uvisor_delete(ARM_CFSTORE_HANDLE hkey) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return cfstore_delete(hkey); +} + +static int32_t cfstore_uvisor_delete(ARM_CFSTORE_HANDLE hkey) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return secure_gateway(configuration_store, __cfstore_uvisor_delete, hkey); +} + +UVISOR_EXTERN int32_t __cfstore_uvisor_find(const char* key_name_query, const ARM_CFSTORE_HANDLE previous, ARM_CFSTORE_HANDLE next) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return cfstore_find(key_name_query, previous, next); +} + +static int32_t cfstore_uvisor_find(const char* key_name_query, const ARM_CFSTORE_HANDLE previous, ARM_CFSTORE_HANDLE next) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return secure_gateway(configuration_store, __cfstore_uvisor_find, key_name_query, previous, next); +} + +UVISOR_EXTERN int32_t __cfstore_uvisor_flush(int dummy) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) dummy; + return cfstore_flush(); +} + +static int32_t cfstore_uvisor_flush(void) +{ + int dummy = 0; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return secure_gateway(configuration_store, __cfstore_uvisor_flush, dummy); +} + +UVISOR_EXTERN int32_t __cfstore_uvisor_get_key_name(ARM_CFSTORE_HANDLE hkey, char* key_name, uint8_t *key_name_len) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return cfstore_get_key_name(hkey, key_name, key_name_len); +} + +static int32_t cfstore_uvisor_get_key_name(ARM_CFSTORE_HANDLE hkey, char* key_name, uint8_t *key_name_len) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return secure_gateway(configuration_store, __cfstore_uvisor_get_key_name, hkey, key_name, key_name_len); +} + +UVISOR_EXTERN int32_t __cfstore_uvisor_get_value_len(ARM_CFSTORE_HANDLE hkey, ARM_CFSTORE_SIZE *value_len) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return cfstore_get_value_len(hkey, value_len); +} + +static int32_t cfstore_uvisor_get_value_len(ARM_CFSTORE_HANDLE hkey, ARM_CFSTORE_SIZE *value_len) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return secure_gateway(configuration_store, __cfstore_uvisor_get_value_len, hkey, value_len); +} + +UVISOR_EXTERN int32_t __cfstore_uvisor_initialize(ARM_CFSTORE_CALLBACK callback, void* client_context) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return cfstore_initialise(callback, client_context); +} + +static int32_t cfstore_uvisor_initialise(ARM_CFSTORE_CALLBACK callback, void* client_context) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return secure_gateway(configuration_store, __cfstore_uvisor_initialize, callback, client_context); +} + +/* type to convert between ARM_CFSTORE_FMODE and uint32 for passing flags through secure gw */ +typedef union cfstore_fmode_flags_t +{ + ARM_CFSTORE_FMODE flags; + uint32_t val; +} cfstore_fmode_flags_t; + +UVISOR_EXTERN int32_t __cfstore_uvisor_open(const char* key_name, uint32_t flags, ARM_CFSTORE_HANDLE hkey) +{ + cfstore_fmode_flags_t uflags; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + uflags.val = flags; + return cfstore_open(key_name, uflags.flags, hkey); +} + +static int32_t cfstore_uvisor_open(const char* key_name, ARM_CFSTORE_FMODE flags, ARM_CFSTORE_HANDLE hkey) +{ + cfstore_fmode_flags_t uflags; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + uflags.flags = flags; + return secure_gateway(configuration_store, __cfstore_uvisor_open, key_name, uflags.val, hkey); +} + +UVISOR_EXTERN int32_t __cfstore_uvisor_read(ARM_CFSTORE_HANDLE hkey, void* data, ARM_CFSTORE_SIZE* len) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return cfstore_read(hkey, data, len); +} + +static int32_t cfstore_uvisor_read(ARM_CFSTORE_HANDLE hkey, void* data, ARM_CFSTORE_SIZE* len) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return secure_gateway(configuration_store, __cfstore_uvisor_read, hkey, data, len); +} + +UVISOR_EXTERN int32_t __cfstore_uvisor_rseek(ARM_CFSTORE_HANDLE hkey, ARM_CFSTORE_OFFSET offset) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return cfstore_rseek(hkey, offset); +} + +static int32_t cfstore_uvisor_rseek(ARM_CFSTORE_HANDLE hkey, ARM_CFSTORE_OFFSET offset) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return secure_gateway(configuration_store, __cfstore_uvisor_rseek, hkey, offset); +} + +UVISOR_EXTERN int32_t __cfstore_uvisor_uninitialise(int dummy) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + (void) dummy; + return cfstore_uninitialise(); +} + +static int32_t cfstore_uvisor_uninitialize(void) +{ + int dummy = 0; + + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return secure_gateway(configuration_store, __cfstore_uvisor_uninitialise, dummy); +} + +UVISOR_EXTERN int32_t __cfstore_uvisor_write(ARM_CFSTORE_HANDLE hkey, const char* data, ARM_CFSTORE_SIZE* len) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return cfstore_write(hkey, data, len); +} + +static int32_t cfstore_uvisor_write(ARM_CFSTORE_HANDLE hkey, const char* data, ARM_CFSTORE_SIZE* len) +{ + CFSTORE_FENTRYLOG("%s:entered\n", __func__); + return secure_gateway(configuration_store, __cfstore_uvisor_write, hkey, data, len); +} + + +ARM_CFSTORE_DRIVER cfstore_driver = +{ + .Close = cfstore_uvisor_close, + .Create = cfstore_uvisor_create, + .Delete= cfstore_uvisor_delete, + .Find = cfstore_uvisor_find, + .Flush = cfstore_uvisor_flush, + .GetCapabilities = cfstore_get_capabilities, + .GetKeyName = cfstore_uvisor_get_key_name, + .GetStatus = cfstore_get_status, + .GetValueLen = cfstore_uvisor_get_value_len, + .GetVersion = cfstore_get_version, + .Initialize = cfstore_uvisor_initialise, + .Open = cfstore_uvisor_open, + .PowerControl = cfstore_power_control, + .Read = cfstore_uvisor_read, + .Rseek = cfstore_uvisor_rseek, + .Uninitialize = cfstore_uvisor_uninitialize, + .Write = cfstore_uvisor_write, +}; + +#else + +/* non-uvisor interface */ +ARM_CFSTORE_DRIVER cfstore_driver = +{ + .Close = cfstore_close, + .Create = cfstore_create, + .Delete= cfstore_delete, + .Find = cfstore_find, + .Flush = cfstore_flush, + .GetCapabilities = cfstore_get_capabilities, + .GetKeyName = cfstore_get_key_name, + .GetStatus = cfstore_get_status, + .GetValueLen = cfstore_get_value_len, + .GetVersion = cfstore_get_version, + .Initialize = cfstore_initialise, + .Open = cfstore_open, + .PowerControl = cfstore_power_control, + .Read = cfstore_read, + .Rseek = cfstore_rseek, + .Uninitialize = cfstore_uninitialise, + .Write = cfstore_write, +}; + +#endif /* YOTTA_CFG_CFSTORE_UVISOR */ diff --git a/storage/cfstore/tatmanjants.txt b/storage/cfstore/tatmanjants.txt new file mode 100644 index 0000000000..192bae13d5 --- /dev/null +++ b/storage/cfstore/tatmanjants.txt @@ -0,0 +1,30 @@ +/* In order to support ARM toolchain and simplify the number of newlib posix files used, + * this have been copied from collate.c, and the license for this code has been included at the + * here: + * + * Copyright (c) 1995 Alex Tatmanjants + * at Electronni Visti IA, Kiev, Ukraine. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ \ No newline at end of file