From ad88218a35d213f40f79d4849ae5938b1a29a2c1 Mon Sep 17 00:00:00 2001 From: Senthil Ramakrishnan Date: Mon, 15 Oct 2018 13:59:36 -0500 Subject: [PATCH 01/45] Kernel.h doxygen update --- rtos/Kernel.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rtos/Kernel.h b/rtos/Kernel.h index bd123def7f3..a80003bb8e9 100644 --- a/rtos/Kernel.h +++ b/rtos/Kernel.h @@ -47,14 +47,14 @@ uint64_t get_ms_count(); /** Attach a function to be called by the RTOS idle task @param fptr pointer to the function to be called - @note You may call this function from ISR context. + @note You can call this function from ISR context. */ void attach_idle_hook(void (*fptr)(void)); -/** Attach a function to be called when a task is killed +/** Attach a function to be called when a task is terminated @param fptr pointer to the function to be called - @note You may call this function from ISR context. + @note You can call this function from ISR context. */ void attach_thread_terminate_hook(void (*fptr)(osThreadId_t id)); From 4cdcdc10104995591f68d6623115d7eba3d10d20 Mon Sep 17 00:00:00 2001 From: Senthil Ramakrishnan Date: Mon, 29 Oct 2018 13:53:19 -0500 Subject: [PATCH 02/45] Updated attach_thread_terminate_hook doxygen comments --- rtos/Kernel.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rtos/Kernel.h b/rtos/Kernel.h index a80003bb8e9..f538ed3855a 100644 --- a/rtos/Kernel.h +++ b/rtos/Kernel.h @@ -47,14 +47,14 @@ uint64_t get_ms_count(); /** Attach a function to be called by the RTOS idle task @param fptr pointer to the function to be called - @note You can call this function from ISR context. + @note You may call this function from ISR context. */ void attach_idle_hook(void (*fptr)(void)); -/** Attach a function to be called when a task is terminated +/** Attach a function to be called when a thread terminates. @param fptr pointer to the function to be called - @note You can call this function from ISR context. + @note You may call this function from ISR context. */ void attach_thread_terminate_hook(void (*fptr)(osThreadId_t id)); From 2418d9c0ae22161c9c9ddb0c544475981fe6166d Mon Sep 17 00:00:00 2001 From: Amanda Butler Date: Tue, 30 Oct 2018 10:25:25 -0500 Subject: [PATCH 03/45] Edit Kernel.h Make minor copy edits for active voice, branding and deletion of extra spaces. --- rtos/Kernel.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/rtos/Kernel.h b/rtos/Kernel.h index f538ed3855a..851dfb688ed 100644 --- a/rtos/Kernel.h +++ b/rtos/Kernel.h @@ -33,26 +33,26 @@ namespace rtos { namespace Kernel { /** Read the current RTOS kernel millisecond tick count. - The tick count corresponds to the tick count used by the RTOS for timing - purposes. It increments monotonically from 0 at boot, hence effectively + The tick count corresponds to the tick count the RTOS uses for timing + purposes. It increments monotonically from 0 at boot, so it effectively never wraps. If the underlying RTOS only provides a 32-bit tick count, this method expands it to 64 bits. @return RTOS kernel current tick count - @note mbed OS always uses millisecond RTOS ticks, and this could only wrap - after half a billion years + @note Mbed OS always uses millisecond RTOS ticks, and this could only wrap + after half a billion years. @note You cannot call this function from ISR context. */ uint64_t get_ms_count(); -/** Attach a function to be called by the RTOS idle task - @param fptr pointer to the function to be called +/** Attach a function to be called by the RTOS idle task. + @param fptr pointer to the function to be called @note You may call this function from ISR context. */ void attach_idle_hook(void (*fptr)(void)); /** Attach a function to be called when a thread terminates. - @param fptr pointer to the function to be called + @param fptr pointer to the function to be called @note You may call this function from ISR context. */ From b01c8570e556a83516e8f70e8c427987d0c8752a Mon Sep 17 00:00:00 2001 From: Qinghao Shi Date: Wed, 31 Oct 2018 14:49:21 +0000 Subject: [PATCH 04/45] exclude vendor's EMAC driver from doxy --- doxyfile_options | 1 + 1 file changed, 1 insertion(+) diff --git a/doxyfile_options b/doxyfile_options index b8c8b170650..c865e127c2f 100644 --- a/doxyfile_options +++ b/doxyfile_options @@ -849,6 +849,7 @@ EXCLUDE_PATTERNS = */tools/* \ */features/lwipstack/* \ */features/nanostack/sal-stack-nanostack/* \ */features/nanostack/coap-service/* \ + */features/netsocket/emac-drivers/* \ */mbed-trace/* \ */mbed-coap/* \ */nanostack-libservice/* \ From 60cc0c295d9006a87752d21770d23836e44a5a93 Mon Sep 17 00:00:00 2001 From: Qinghao Shi Date: Wed, 31 Oct 2018 14:50:59 +0000 Subject: [PATCH 05/45] fixed NetworkInterface Class missing in doxy --- features/netsocket/NetworkInterface.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/netsocket/NetworkInterface.h b/features/netsocket/NetworkInterface.h index 7d1087a2d03..6338dbaa62f 100644 --- a/features/netsocket/NetworkInterface.h +++ b/features/netsocket/NetworkInterface.h @@ -33,7 +33,8 @@ class EMACInterface; /** Common interface that is shared between network devices. * - * @addtogroup netsocket + * @\addtogroup netsocket + * @{ */ class NetworkInterface: public DNS { public: @@ -331,5 +332,5 @@ class NetworkInterface: public DNS { #endif //!defined(DOXYGEN_ONLY) }; - +/** @}*/ #endif From e3623b91fa8e47ad34a75f047c122939f7fc65fc Mon Sep 17 00:00:00 2001 From: Seppo Takalo Date: Wed, 31 Oct 2018 17:14:05 +0200 Subject: [PATCH 06/45] Increase EMAC test timeout to 1400 seconds --- TESTS/network/emac/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TESTS/network/emac/main.cpp b/TESTS/network/emac/main.cpp index ac2bdb14033..49ee3cf909a 100644 --- a/TESTS/network/emac/main.cpp +++ b/TESTS/network/emac/main.cpp @@ -53,7 +53,7 @@ using namespace utest::v1; utest::v1::status_t test_setup(const size_t number_of_cases) { #if !MBED_CONF_APP_ECHO_SERVER - GREENTEA_SETUP(1200, "default_auto"); + GREENTEA_SETUP(1400, "default_auto"); #endif return verbose_test_setup_handler(number_of_cases); } From 74f72597a8b7dc4c82c2cd4991064a66170e2a8e Mon Sep 17 00:00:00 2001 From: William Kelly III Date: Wed, 31 Oct 2018 12:28:53 -0500 Subject: [PATCH 07/45] Fix off-by-one-error in BusIn/Out --- drivers/BusIn.cpp | 2 +- drivers/BusInOut.cpp | 2 +- drivers/BusOut.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/BusIn.cpp b/drivers/BusIn.cpp index f6cc933e7ea..c14351f3fe5 100644 --- a/drivers/BusIn.cpp +++ b/drivers/BusIn.cpp @@ -97,7 +97,7 @@ BusIn::operator int() DigitalIn &BusIn::operator[](int index) { // No lock needed since _pin is not modified outside the constructor - MBED_ASSERT(index >= 0 && index <= 16); + MBED_ASSERT(index >= 0 && index < 16); MBED_ASSERT(_pin[index]); return *_pin[index]; } diff --git a/drivers/BusInOut.cpp b/drivers/BusInOut.cpp index 950cbb5d58f..170e035b962 100644 --- a/drivers/BusInOut.cpp +++ b/drivers/BusInOut.cpp @@ -128,7 +128,7 @@ BusInOut &BusInOut::operator= (BusInOut &rhs) DigitalInOut &BusInOut::operator[](int index) { // No lock needed since _pin is not modified outside the constructor - MBED_ASSERT(index >= 0 && index <= 16); + MBED_ASSERT(index >= 0 && index < 16); MBED_ASSERT(_pin[index]); return *_pin[index]; } diff --git a/drivers/BusOut.cpp b/drivers/BusOut.cpp index 913ba197505..769d2e68919 100644 --- a/drivers/BusOut.cpp +++ b/drivers/BusOut.cpp @@ -95,7 +95,7 @@ BusOut &BusOut::operator= (BusOut &rhs) DigitalOut &BusOut::operator[](int index) { // No lock needed since _pin is not modified outside the constructor - MBED_ASSERT(index >= 0 && index <= 16); + MBED_ASSERT(index >= 0 && index < 16); MBED_ASSERT(_pin[index]); return *_pin[index]; } From a7c777d5c1d50e22edd0fec18e7b6c4b8e7a80a3 Mon Sep 17 00:00:00 2001 From: adbridge Date: Wed, 31 Oct 2018 17:40:15 +0000 Subject: [PATCH 08/45] Make examples commands return a failure Currently the following commands in examples.py, do_import() do_deploy() do_versionning() do_clone() all return a success status (ie 0) irrespective of any errors originating from their sub-functions. This PR fixes this. Now these commands will return one of: 0 - success 1 - general failure x - failure returned by a subprocess.call function --- tools/test/examples/examples.py | 12 ++++-------- tools/test/examples/examples_lib.py | 23 +++++++++++++++++++---- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/tools/test/examples/examples.py b/tools/test/examples/examples.py index fcc3485baf2..3fbeaa3acc6 100644 --- a/tools/test/examples/examples.py +++ b/tools/test/examples/examples.py @@ -97,20 +97,17 @@ def do_export(args, config, examples): def do_import(_, config, examples): """Do the import step of this process""" - lib.source_repos(config, examples) - return 0 + return lib.source_repos(config, examples) def do_clone(_, config, examples): """Do the clone step of this process""" - lib.clone_repos(config, examples) - return 0 + return lib.clone_repos(config, examples) def do_deploy(_, config, examples): """Do the deploy step of this process""" - lib.deploy_repos(config, examples) - return 0 + return lib.deploy_repos(config, examples) def do_compile(args, config, examples): @@ -125,8 +122,7 @@ def do_compile(args, config, examples): def do_versionning(args, config, examples): """ Test update the mbed-os to the version specified by the tag """ - lib.update_mbedos_version(config, args.tag, examples) - return 0 + return lib.update_mbedos_version(config, args.tag, examples) if __name__ == "__main__": diff --git a/tools/test/examples/examples_lib.py b/tools/test/examples/examples_lib.py index 51cf970223f..18d3e4d8810 100644 --- a/tools/test/examples/examples_lib.py +++ b/tools/test/examples/examples_lib.py @@ -169,7 +169,11 @@ def source_repos(config, examples): print("'%s' example directory already exists. Deleting..." % name) rmtree(name) - subprocess.call(["mbed-cli", "import", repo_info['repo']]) + result = subprocess.call(["mbed-cli", "import", repo_info['repo']]) + if result: + return result + + return 0 def clone_repos(config, examples , retry = 3): """ Clones each of the repos associated with the specific examples name from the @@ -192,6 +196,8 @@ def clone_repos(config, examples , retry = 3): break else: print("ERROR : unable to clone the repo {}".format(name)) + return 1 + return 0 def deploy_repos(config, examples): """ If the example directory exists as provided by the json config file, @@ -207,11 +213,15 @@ def deploy_repos(config, examples): if name in examples: if os.path.exists(name): os.chdir(name) - subprocess.call(["mbed-cli", "deploy"]) + result = subprocess.call(["mbed-cli", "deploy"]) os.chdir("..") + if result: + print("mbed-cli deploy command failed for '%s'" % name) + return result else: print("'%s' example directory doesn't exist. Skipping..." % name) - + return 1 + return 0 def get_num_failures(results, export=False): """ Returns the number of failed compilations from the results summary @@ -405,5 +415,10 @@ def update_mbedos_version(config, tag, examples): update_dir = basename(repo_info['repo']) + "/mbed-os" print("\nChanging dir to %s\n" % update_dir) os.chdir(update_dir) - subprocess.call(["mbed-cli", "update", tag, "--clean"]) + result = subprocess.call(["mbed-cli", "update", tag, "--clean"]) os.chdir("../..") + if result: + return result: + + return 0 + \ No newline at end of file From ebef79a05cd4deb15c6b063cd4ac16fad961e043 Mon Sep 17 00:00:00 2001 From: Jaakko Korhonen Date: Wed, 24 Oct 2018 14:30:42 +0300 Subject: [PATCH 09/45] Add generic BlockDevice test for contiguous erase/write/read. --- .../blockdevice/general_block_device/main.cpp | 138 +++++++++++++++++- 1 file changed, 137 insertions(+), 1 deletion(-) diff --git a/features/storage/TESTS/blockdevice/general_block_device/main.cpp b/features/storage/TESTS/blockdevice/general_block_device/main.cpp index ec4675cc753..8d08659c5c7 100644 --- a/features/storage/TESTS/blockdevice/general_block_device/main.cpp +++ b/features/storage/TESTS/blockdevice/general_block_device/main.cpp @@ -13,12 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS //Required for PRIu64 +#endif #include "mbed.h" #include "greentea-client/test_env.h" #include "unity.h" #include "utest.h" #include "mbed_trace.h" +#include #include using namespace utest::v1; @@ -209,6 +213,137 @@ void test_multi_threads() TEST_ASSERT_EQUAL(0, err); } +void test_contiguous_erase_write_read() +{ + utest_printf("\nTest Contiguous Erase/Program/Read Starts..\n"); + + // Test flow: + // 1. Erase whole test area + // - Tests contiguous erase + // 2. Write smaller memory area + // - Tests contiguous sector writes + // 3. Rerun step 2 for whole erase region + + BlockDevice *block_device = BlockDevice::get_default_instance(); + TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "\nno block device found.\n"); + + // Initialize BlockDevice + int err = block_device->init(); + TEST_ASSERT_EQUAL(0, err); + + // Test parameters + bd_size_t erase_size = block_device->get_erase_size(); + TEST_ASSERT(erase_size > 0); + bd_size_t program_size = block_device->get_program_size(); + TEST_ASSERT(program_size > 0); + utest_printf("\nerase_size=%d", erase_size); + utest_printf("\nprogram_size=%d", program_size); + utest_printf("\nblock_device->size()=%" PRId64, block_device->size()); + + // Determine write/read buffer size + // start write_read_buf_size from 1% block_device->size() + bd_size_t write_read_buf_size = block_device->size() / 100; // 1%, 10k=100, 100k=1k, 1MB=10k, 32MB=32k + // try to limit write_read_buf_size to 10k. If program_size*2 is larger than 10k, that will be used instead. + if (write_read_buf_size > 10000) { + write_read_buf_size = 10000; + } + // 2 program_size blocks is minimum for contiguous write/read test + if (write_read_buf_size < program_size*2) { + write_read_buf_size = program_size*2; // going over 10k + } + bd_size_t contiguous_write_read_blocks_per_region = write_read_buf_size / program_size; // 2 is minimum to test contiguous write + write_read_buf_size = contiguous_write_read_blocks_per_region * program_size; + utest_printf("\ncontiguous_write_read_blocks_per_region=%" PRIu64, contiguous_write_read_blocks_per_region); + utest_printf("\nwrite_read_buf_size=%" PRIu64, write_read_buf_size); + + // Determine test region count + int contiguous_write_read_regions = TEST_BLOCK_COUNT; + utest_printf("\ncontiguous_write_read_regions=%d", contiguous_write_read_regions); + + // Determine whole erase size + bd_size_t contiguous_erase_size = write_read_buf_size * contiguous_write_read_regions; + contiguous_erase_size -= contiguous_erase_size % erase_size; // aligned to erase_size + contiguous_erase_size += erase_size; // but larger than write/read size * regions + utest_printf("\ncontiguous_erase_size=%" PRIu64, contiguous_erase_size); + + // Determine starting address + bd_addr_t start_address = rand(); // low 32 bytes + start_address += (uint64_t)rand() << 32; // high 32 bytes + start_address %= block_device->size() - contiguous_erase_size - erase_size; // fit all data + alignment reserve + start_address += erase_size; // add alignment reserve + start_address -= start_address % erase_size; // align with erase_block + bd_addr_t stop_address = start_address + write_read_buf_size * contiguous_write_read_regions; + utest_printf("\nstart_address=0x%016" PRIx64, start_address); + utest_printf("\nstop_address=0x%016" PRIx64, stop_address); + + // Allocate write/read buffer + uint8_t *write_read_buf = (uint8_t*)malloc(write_read_buf_size); + if (write_read_buf == NULL) { + block_device->deinit(); + TEST_SKIP_MESSAGE("\nnot enough memory for test"); + } + utest_printf("\nwrite_read_buf_size=%" PRIu64 "", (uint64_t)write_read_buf_size); + + // Pre-fill the to-be-erased region. By pre-filling the region, + // we can be sure the test will not pass if the erase doesn't work. + for (bd_size_t offset=0; start_address+offset < stop_address; offset+=write_read_buf_size) { + for (size_t i=0; iprogram((const void*)write_read_buf, start_address+offset, write_read_buf_size); + TEST_ASSERT_EQUAL(0, err); + } + + // Erase the whole region first + utest_printf("\nerasing memory, from 0x%" PRIx64 " of size 0x%" PRIx64, start_address, contiguous_erase_size); + err = block_device->erase(start_address, contiguous_erase_size); + TEST_ASSERT_EQUAL(0, err); + + // Loop through all write/read regions + int region = 0; + for (; start_address < stop_address; start_address+=write_read_buf_size) { + utest_printf("\n\nregion #%d start_address=0x%016" PRIx64, region++, start_address); + + // Generate test data + unsigned int seed = rand(); + utest_printf("\ngenerating test data, seed=%u", seed); + srand(seed); + for (size_t i=0; iprogram((const void*)write_read_buf, start_address, write_read_buf_size); + TEST_ASSERT_EQUAL(0, err); + + // Read test data + memset(write_read_buf, 0, (size_t)write_read_buf_size); + utest_printf("\nreading test data"); + err = block_device->read(write_read_buf, start_address, write_read_buf_size); + TEST_ASSERT_EQUAL(0, err); + + // Verify read data + utest_printf("\nverifying test data"); + srand(seed); + for (size_t i=0; ideinit(); + TEST_ASSERT_EQUAL(0, err); +} // Test setup utest::v1::status_t test_setup(const size_t number_of_cases) @@ -219,7 +354,8 @@ utest::v1::status_t test_setup(const size_t number_of_cases) Case cases[] = { Case("Testing read write random blocks", test_random_program_read_erase), - Case("Testing Multi Threads Erase Program Read", test_multi_threads) + Case("Testing Multi Threads Erase Program Read", test_multi_threads), + Case("Testing contiguous erase, write and read", test_contiguous_erase_write_read) }; Specification specification(test_setup, cases); From c3e0e3e75dd37a678c9e81a666d16001f0cc603c Mon Sep 17 00:00:00 2001 From: Jaakko Korhonen Date: Wed, 24 Oct 2018 14:31:25 +0300 Subject: [PATCH 10/45] Add generic BlockDevice test for get_erase_value(). --- .../blockdevice/general_block_device/main.cpp | 78 ++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/features/storage/TESTS/blockdevice/general_block_device/main.cpp b/features/storage/TESTS/blockdevice/general_block_device/main.cpp index 8d08659c5c7..c102a388888 100644 --- a/features/storage/TESTS/blockdevice/general_block_device/main.cpp +++ b/features/storage/TESTS/blockdevice/general_block_device/main.cpp @@ -213,6 +213,81 @@ void test_multi_threads() TEST_ASSERT_EQUAL(0, err); } +void test_get_erase_value() +{ + utest_printf("\nTest BlockDevice::get_erase_value()..\n"); + + // Test flow: + // 1. Write data to selected region + // - Known starting point + // 2. Erase selected region + // 3. Read erased region and compare with get_erase_value() + + BlockDevice *block_device = BlockDevice::get_default_instance(); + TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "\nno block device found.\n"); + + int err = block_device->init(); + TEST_ASSERT_EQUAL(0, err); + + // Check erase value + int erase_value_int = block_device->get_erase_value(); + utest_printf("\nblock_device->get_erase_value()=%d", erase_value_int); + TEST_SKIP_UNLESS_MESSAGE(erase_value_int >= 0, "\nerase value is negative which means the erase value is unknown\n"); + + // Assuming that get_erase_value() returns byte value as documentation mentions + // "If get_erase_value() returns a non-negative byte value" for unknown case. + TEST_ASSERT(erase_value_int <= 255); + uint8_t erase_value = (uint8_t)erase_value_int; + + // Determine data_buf_size + bd_size_t erase_size = block_device->get_erase_size(); + TEST_ASSERT(erase_size > 0); + bd_size_t data_buf_size = erase_size; + + // Determine start_address + bd_addr_t start_address = rand(); // low 32 bytes + start_address += (uint64_t)rand() << 32; // high 32 bytes + start_address %= block_device->size() - data_buf_size - erase_size; // fit all data + alignment reserve + start_address += erase_size; // add alignment reserve + start_address -= start_address % erase_size; // align with erase_block + utest_printf("\nstart_address=0x%016" PRIx64, start_address); + + // Allocate buffer for read test data + uint8_t *data_buf = (uint8_t*)malloc(data_buf_size); + TEST_ASSERT_NOT_NULL(data_buf); + + // Write random data to selected region to make sure data is not accidentally set to "erased" value. + // With this pre-write, the test case will fail even if block_device->erase() is broken. + for (bd_size_t i=0; iprogram((const void*)data_buf, start_address, data_buf_size); + TEST_ASSERT_EQUAL(0, err); + + // Erase given memory region + utest_printf("\nerasing given memory region"); + err = block_device->erase(start_address, data_buf_size); + TEST_ASSERT_EQUAL(0, err); + + // Read erased memory region + utest_printf("\nreading erased memory region"); + err = block_device->read((void*)data_buf, start_address, data_buf_size); + TEST_ASSERT_EQUAL(0, err); + + // Verify erased memory region + utest_printf("\nverifying erased memory region"); + for (bd_size_t i=0; ideinit(); + TEST_ASSERT_EQUAL(0, err); +} + void test_contiguous_erase_write_read() { utest_printf("\nTest Contiguous Erase/Program/Read Starts..\n"); @@ -355,7 +430,8 @@ utest::v1::status_t test_setup(const size_t number_of_cases) Case cases[] = { Case("Testing read write random blocks", test_random_program_read_erase), Case("Testing Multi Threads Erase Program Read", test_multi_threads), - Case("Testing contiguous erase, write and read", test_contiguous_erase_write_read) + Case("Testing contiguous erase, write and read", test_contiguous_erase_write_read), + Case("Test BlockDevice::get_erase_value()", test_get_erase_value) }; Specification specification(test_setup, cases); From 761405592bb0abd971f077314f5e906422a7fe08 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Mon, 29 Oct 2018 16:58:07 -0500 Subject: [PATCH 11/45] Added mutex to DataFlash for thread safety --- .../DataFlashBlockDevice.cpp | 31 +++++++++++++++---- .../DataFlashBlockDevice.h | 3 ++ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/components/storage/blockdevice/COMPONENT_DATAFLASH/DataFlashBlockDevice.cpp b/components/storage/blockdevice/COMPONENT_DATAFLASH/DataFlashBlockDevice.cpp index c71a3c3054b..4dc5542c585 100644 --- a/components/storage/blockdevice/COMPONENT_DATAFLASH/DataFlashBlockDevice.cpp +++ b/components/storage/blockdevice/COMPONENT_DATAFLASH/DataFlashBlockDevice.cpp @@ -164,6 +164,7 @@ DataFlashBlockDevice::DataFlashBlockDevice(PinName mosi, int DataFlashBlockDevice::init() { + _mutex.lock(); DEBUG_PRINTF("init\r\n"); if (!_is_initialized) { @@ -173,6 +174,7 @@ int DataFlashBlockDevice::init() uint32_t val = core_util_atomic_incr_u32(&_init_ref_count, 1); if (val != 1) { + _mutex.unlock(); return BD_ERROR_OK; } @@ -281,33 +283,40 @@ int DataFlashBlockDevice::init() _is_initialized = true; } + _mutex.unlock(); return result; } int DataFlashBlockDevice::deinit() { + _mutex.lock(); DEBUG_PRINTF("deinit\r\n"); if (!_is_initialized) { _init_ref_count = 0; + _mutex.unlock(); return BD_ERROR_OK; } uint32_t val = core_util_atomic_decr_u32(&_init_ref_count, 1); if (val) { + _mutex.unlock(); return BD_ERROR_OK; } _is_initialized = false; + _mutex.unlock(); return BD_ERROR_OK; } int DataFlashBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) { + _mutex.lock(); DEBUG_PRINTF("read: %p %" PRIX64 " %" PRIX64 "\r\n", buffer, addr, size); if (!_is_initialized) { + _mutex.unlock(); return BD_ERROR_DEVICE_ERROR; } @@ -350,6 +359,7 @@ int DataFlashBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) int DataFlashBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size) { + _mutex.lock(); DEBUG_PRINTF("program: %p %" PRIX64 " %" PRIX64 "\r\n", buffer, addr, size); if (!_is_initialized) { @@ -416,6 +426,7 @@ int DataFlashBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t int DataFlashBlockDevice::erase(bd_addr_t addr, bd_size_t size) { + _mutex.lock(); DEBUG_PRINTF("erase: %" PRIX64 " %" PRIX64 "\r\n", addr, size); if (!_is_initialized) { @@ -484,23 +495,29 @@ bd_size_t DataFlashBlockDevice::get_program_size() const bd_size_t DataFlashBlockDevice::get_erase_size() const { + _mutex.lock(); DEBUG_PRINTF("erase size: %" PRIX16 "\r\n", _block_size); - - return _block_size; + bd_size_t block_size = _block_size; + _mutex.unlock(); + return block_size; } bd_size_t DataFlashBlockDevice::get_erase_size(bd_addr_t addr) const { + _mutex.lock(); DEBUG_PRINTF("erase size: %" PRIX16 "\r\n", _block_size); - - return _block_size; + bd_size_t block_size = _block_size; + _mutex.unlock(); + return block_size; } bd_size_t DataFlashBlockDevice::size() const { + _mutex.lock(); DEBUG_PRINTF("device size: %" PRIX32 "\r\n", _device_size); - - return _device_size; + bd_size_t device_size = _device_size; + _mutex.unlock(); + return device_size; } /** @@ -512,6 +529,7 @@ bd_size_t DataFlashBlockDevice::size() const */ uint16_t DataFlashBlockDevice::_get_register(uint8_t opcode) { + _mutex.lock(); DEBUG_PRINTF("_get_register: %" PRIX8 "\r\n", opcode); /* activate device */ @@ -527,6 +545,7 @@ uint16_t DataFlashBlockDevice::_get_register(uint8_t opcode) /* deactivate device */ _cs = 1; + _mutex.unlock(); return status; } diff --git a/components/storage/blockdevice/COMPONENT_DATAFLASH/DataFlashBlockDevice.h b/components/storage/blockdevice/COMPONENT_DATAFLASH/DataFlashBlockDevice.h index 52e2600e09d..bd447e7439c 100644 --- a/components/storage/blockdevice/COMPONENT_DATAFLASH/DataFlashBlockDevice.h +++ b/components/storage/blockdevice/COMPONENT_DATAFLASH/DataFlashBlockDevice.h @@ -173,6 +173,9 @@ class DataFlashBlockDevice : public BlockDevice { int _sync(void); int _write_page(const uint8_t *buffer, uint32_t addr, uint32_t offset, uint32_t size); uint32_t _translate_address(bd_addr_t addr); + + // Mutex for thread safety + mutable PlatformMutex _mutex; }; From 0d04f03882b6478b07781cb8a061009b74bfd929 Mon Sep 17 00:00:00 2001 From: Jaakko Korhonen Date: Tue, 30 Oct 2018 10:22:59 +0200 Subject: [PATCH 12/45] Added missing mutex to DataFlash for thread safety. --- .../blockdevice/COMPONENT_DATAFLASH/DataFlashBlockDevice.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/storage/blockdevice/COMPONENT_DATAFLASH/DataFlashBlockDevice.cpp b/components/storage/blockdevice/COMPONENT_DATAFLASH/DataFlashBlockDevice.cpp index 4dc5542c585..635e4fe0165 100644 --- a/components/storage/blockdevice/COMPONENT_DATAFLASH/DataFlashBlockDevice.cpp +++ b/components/storage/blockdevice/COMPONENT_DATAFLASH/DataFlashBlockDevice.cpp @@ -354,6 +354,7 @@ int DataFlashBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) result = BD_ERROR_OK; } + _mutex.unlock(); return result; } @@ -363,6 +364,7 @@ int DataFlashBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t DEBUG_PRINTF("program: %p %" PRIX64 " %" PRIX64 "\r\n", buffer, addr, size); if (!_is_initialized) { + _mutex.unlock(); return BD_ERROR_DEVICE_ERROR; } @@ -421,6 +423,7 @@ int DataFlashBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t _write_enable(false); } + _mutex.unlock(); return result; } @@ -430,6 +433,7 @@ int DataFlashBlockDevice::erase(bd_addr_t addr, bd_size_t size) DEBUG_PRINTF("erase: %" PRIX64 " %" PRIX64 "\r\n", addr, size); if (!_is_initialized) { + _mutex.unlock(); return BD_ERROR_DEVICE_ERROR; } @@ -476,6 +480,7 @@ int DataFlashBlockDevice::erase(bd_addr_t addr, bd_size_t size) _write_enable(false); } + _mutex.unlock(); return result; } From 56a12ce7d6d9478b3965e21f2a4cef2233e337c0 Mon Sep 17 00:00:00 2001 From: Jeroen de Bruijn Date: Thu, 1 Nov 2018 13:55:44 +0100 Subject: [PATCH 13/45] feat: Name cellular FSM queue thread --- features/cellular/easy_cellular/CellularConnectionFSM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/cellular/easy_cellular/CellularConnectionFSM.cpp b/features/cellular/easy_cellular/CellularConnectionFSM.cpp index 39c76736ab2..48647a485a1 100644 --- a/features/cellular/easy_cellular/CellularConnectionFSM.cpp +++ b/features/cellular/easy_cellular/CellularConnectionFSM.cpp @@ -617,7 +617,7 @@ nsapi_error_t CellularConnectionFSM::start_dispatch() { MBED_ASSERT(!_queue_thread); - _queue_thread = new rtos::Thread(osPriorityNormal, 2048); + _queue_thread = new rtos::Thread(osPriorityNormal, 2048, NULL, "fsm_queue_thread"); if (!_queue_thread) { stop(); return NSAPI_ERROR_NO_MEMORY; From 1f4f62333966129ece2a44f1623909c8d13209ae Mon Sep 17 00:00:00 2001 From: Jeroen de Bruijn Date: Thu, 1 Nov 2018 13:57:03 +0100 Subject: [PATCH 14/45] feat: Name PPP thread --- features/lwipstack/ppp_lwip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/lwipstack/ppp_lwip.cpp b/features/lwipstack/ppp_lwip.cpp index cea781badd4..01f09635307 100644 --- a/features/lwipstack/ppp_lwip.cpp +++ b/features/lwipstack/ppp_lwip.cpp @@ -68,7 +68,7 @@ static EventQueue *prepare_event_queue() // Only need to queue 2 events. new blows on failure. event_queue = new EventQueue(2 * EVENTS_EVENT_SIZE, NULL); - event_thread = new Thread(osPriorityNormal, PPP_THREAD_STACK_SIZE); + event_thread = new Thread(osPriorityNormal, PPP_THREAD_STACK_SIZE, NULL, "event_thread"); if (event_thread->start(callback(event_queue, &EventQueue::dispatch_forever)) != osOK) { delete event_thread; From b28d0811dff5b220518d13006beac398181304c9 Mon Sep 17 00:00:00 2001 From: adbridge Date: Thu, 1 Nov 2018 13:07:16 +0000 Subject: [PATCH 15/45] Fix up subprocess calls subprocess.call() does not by default return a status value. Update the commands to add shell=True which forces a return value. Also convert the commands to a single string rather than a list as this plays more nicely with both linux and windows. Also fix a spurious : --- tools/test/examples/examples_lib.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tools/test/examples/examples_lib.py b/tools/test/examples/examples_lib.py index 18d3e4d8810..308fb87c754 100644 --- a/tools/test/examples/examples_lib.py +++ b/tools/test/examples/examples_lib.py @@ -168,8 +168,10 @@ def source_repos(config, examples): if os.path.exists(name): print("'%s' example directory already exists. Deleting..." % name) rmtree(name) + + cmd = "mbed-cli import %s" %repo_info['repo'] + result = subprocess.call(cmd, shell=True) - result = subprocess.call(["mbed-cli", "import", repo_info['repo']]) if result: return result @@ -191,8 +193,9 @@ def clone_repos(config, examples , retry = 3): if os.path.exists(name): print("'%s' example directory already exists. Deleting..." % name) rmtree(name) + cmd = "%s clone %s" %(repo_info['type'], repo_info['repo']) for i in range(0, retry): - if subprocess.call([repo_info['type'], "clone", repo_info['repo']]) == 0: + if not subprocess.call(cmd, shell=True): break else: print("ERROR : unable to clone the repo {}".format(name)) @@ -213,7 +216,7 @@ def deploy_repos(config, examples): if name in examples: if os.path.exists(name): os.chdir(name) - result = subprocess.call(["mbed-cli", "deploy"]) + result = subprocess.call("mbed-cli deploy", shell=True) os.chdir("..") if result: print("mbed-cli deploy command failed for '%s'" % name) @@ -415,10 +418,11 @@ def update_mbedos_version(config, tag, examples): update_dir = basename(repo_info['repo']) + "/mbed-os" print("\nChanging dir to %s\n" % update_dir) os.chdir(update_dir) - result = subprocess.call(["mbed-cli", "update", tag, "--clean"]) + cmd = "mbed-cli update %s --clean" %tag + result = subprocess.call(cmd, shell=True) os.chdir("../..") if result: - return result: + return result return 0 \ No newline at end of file From f92108c0f3c95f6f90634b3342bee4dc10254d47 Mon Sep 17 00:00:00 2001 From: Seppo Takalo Date: Thu, 1 Nov 2018 15:30:27 +0200 Subject: [PATCH 16/45] Make sure that close() is called before the transport is destroyed. Transport is a member of TLSSocket which is derived from TLSSocketWrapper. Make sure that TLSSocketWrapper::close() is called before the transport is destroyed. --- features/netsocket/TLSSocket.cpp | 9 +++++++++ features/netsocket/TLSSocket.h | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/features/netsocket/TLSSocket.cpp b/features/netsocket/TLSSocket.cpp index 56583aaf421..bf53be6d2c4 100644 --- a/features/netsocket/TLSSocket.cpp +++ b/features/netsocket/TLSSocket.cpp @@ -35,4 +35,13 @@ nsapi_error_t TLSSocket::connect(const char *host, uint16_t port) return TLSSocketWrapper::do_handshake(); } +TLSSocket::~TLSSocket() +{ + /* Transport is a member of TLSSocket which is derived from TLSSocketWrapper. + * Make sure that TLSSocketWrapper::close() is called before the transport is + * destroyed. + */ + close(); +} + #endif // MBEDTLS_SSL_CLI_C \ No newline at end of file diff --git a/features/netsocket/TLSSocket.h b/features/netsocket/TLSSocket.h index cbb6bb06904..13ccd787d42 100644 --- a/features/netsocket/TLSSocket.h +++ b/features/netsocket/TLSSocket.h @@ -41,6 +41,10 @@ class TLSSocket : public TLSSocketWrapper { */ TLSSocket() : TLSSocketWrapper(&tcp_socket) {} + /** Destroy the TLSSocket and closes the transport. + */ + virtual ~TLSSocket(); + /** Create a socket on a network interface * * Creates and opens a socket on the network stack of the given From 766359c7c57985bcde54009cc1e6e23e3a927111 Mon Sep 17 00:00:00 2001 From: kegilbert Date: Thu, 1 Nov 2018 11:57:44 -0500 Subject: [PATCH 17/45] Typo fixes in Doxygen for Platform --- platform/CallChain.h | 2 +- platform/SharedPtr.h | 2 +- platform/mbed_error.h | 32 ++++++++++++++++---------------- platform/mbed_mem_trace.h | 6 +++--- platform/mbed_stats.h | 2 +- platform/mbed_toolchain.h | 4 ++-- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/platform/CallChain.h b/platform/CallChain.h index 85682102d35..3fe17231b09 100644 --- a/platform/CallChain.h +++ b/platform/CallChain.h @@ -147,7 +147,7 @@ class CallChain : private NonCopyable { * @param method pointer to the member function to be called * * @returns - * The function object created for 'tptr' and 'mptr' + * The function object created for the object and method pointers * * @deprecated * The add_front function does not support cv-qualifiers. Replaced by diff --git a/platform/SharedPtr.h b/platform/SharedPtr.h index ffa702a45a4..d2735072558 100644 --- a/platform/SharedPtr.h +++ b/platform/SharedPtr.h @@ -29,7 +29,7 @@ namespace mbed { /** Shared pointer class. * * A shared pointer is a "smart" pointer that retains ownership of an object using - * reference counting accross all smart pointers referencing that object. + * reference counting across all smart pointers referencing that object. * * @code * #include "platform/SharedPtr.h" diff --git a/platform/mbed_error.h b/platform/mbed_error.h index 71aa69c5c31..117b14499b8 100644 --- a/platform/mbed_error.h +++ b/platform/mbed_error.h @@ -86,24 +86,24 @@ extern "C" { \endverbatim * * The error status value range for each error type is as follows:\n - * Posix Error Status-es - 0xFFFFFFFF to 0xFFFFFF01(-1 -255) - This corresponds to Posix error codes represented as negative.\n + * POSIX Error Status-es - 0xFFFFFFFF to 0xFFFFFF01(-1 -255) - This corresponds to POSIX error codes represented as negative.\n * System Error Status-es - 0x80XX0100 to 0x80XX0FFF - This corresponds to System error codes range(all values are negative). Bits 23-16 will be module type(marked with XX)\n * Custom Error Status-es - 0xA0XX1000 to 0xA0XXFFFF - This corresponds to Custom error codes range(all values are negative). Bits 23-16 will be module type(marked with XX)\n\n * * The ERROR CODE(values encoded into ERROR CODE bit-field in mbed_error_status_t) value range for each error type is also separated as below:\n - * Posix Error Codes - 1 to 255.\n + * POSIX Error Codes - 1 to 255.\n * System Error Codes - 256 to 4095.\n * Custom Error Codes - 4096 to 65535.\n * - * @note Posix error codes are always encoded as negative of their actual value. For example, EPERM is encoded as -EPERM. - * And, the MODULE TYPE for Posix error codes are always encoded as MBED_MODULE_UNKNOWN.\n - * This is to enable easy injection of Posix error codes into MbedOS error handling system without altering the actual Posix error values.\n - * Accordingly, Posix error codes are represented as -1 to -255 under MbedOS error status representation. + * @note POSIX error codes are always encoded as negative of their actual value. For example, EPERM is encoded as -EPERM. + * And, the MODULE TYPE for POSIX error codes are always encoded as MBED_MODULE_UNKNOWN.\n + * This is to enable easy injection of POSIX error codes into MbedOS error handling system without altering the actual POSIX error values.\n + * Accordingly, POSIX error codes are represented as -1 to -255 under MbedOS error status representation. */ typedef int mbed_error_status_t; /** - * Macro for defining a Posix error status. This macro is mainly used to define Posix error values in mbed_error_code_t enumeration. + * Macro for defining a POSIX error status. This macro is mainly used to define POSIX error values in mbed_error_code_t enumeration. * @param error_name Name of the error without the ERROR_ prefix * @param error_code Error code value to be used, must be between 1 and 255(inclusive). * @@ -201,7 +201,7 @@ typedef int mbed_error_status_t; * See mbed_error_status_t description for more info.\n * MBED_ERROR_TYPE_SYSTEM - Used to indicate that the error status is of System defined Error type.\n * MBED_ERROR_TYPE_CUSTOM - Used to indicate that the error status is of Custom defined Error type.\n - * MBED_ERROR_TYPE_POSIX - Used to indicate that the error status is of Posix error type.\n + * MBED_ERROR_TYPE_POSIX - Used to indicate that the error status is of POSIX error type.\n * */ typedef enum _mbed_error_type_t { @@ -301,23 +301,23 @@ typedef enum _mbed_module_type { /** mbed_error_code_t definition * * mbed_error_code_t enumeration defines the Error codes and Error status values for MBED_MODULE_UNKNOWN.\n - * It defines all of Posix Error Codes/Statuses and Mbed System Error Codes/Statuses.\n\n + * It defines all of POSIX Error Codes/Statuses and Mbed System Error Codes/Statuses.\n\n * * @note - * Posix Error codes are defined using the macro MBED_DEFINE_POSIX_ERROR\n + * POSIX Error codes are defined using the macro MBED_DEFINE_POSIX_ERROR\n * For example MBED_DEFINE_POSIX_ERROR( EPERM, EPERM ). This effectively defines the following values:\n * ERROR_CODE_EPERM = EPERM\n * ERROR_EPERM = -EPERM\n * - * Posix Error codes are defined using the macro MBED_DEFINE_POSIX_ERROR\n + * POSIX Error codes are defined using the macro MBED_DEFINE_POSIX_ERROR\n * For example MBED_DEFINE_POSIX_ERROR( EPERM, EPERM ). This macro defines the following values:\n * ERROR_CODE_EPERM = MBED_POSIX_ERROR_BASE+EPERM\n * ERROR_EPERM = -(MBED_POSIX_ERROR_BASE+EPERM)\n * Its effectively equivalent to:\n * ERROR_CODE_EPERM = 1\n * ERROR_EPERM = -1\n - * All Posix error codes currently supported by MbedOS(defined in mbed_retarget.h) are defined using the MBED_DEFINE_POSIX_ERROR macro.\n\n - * Below are the Posic error codes and the description:\n + * All POSIX error codes currently supported by MbedOS(defined in mbed_retarget.h) are defined using the MBED_DEFINE_POSIX_ERROR macro.\n\n + * Below are the POSIX error codes and the description:\n * \verbatim EPERM 1 Operation not permitted ENOENT 2 No such file or directory @@ -546,9 +546,9 @@ typedef enum _mbed_module_type { * * @note * **Using error codes:** \n - * Posix error codes may be used in modules/functions currently using Posix error codes and switching them to Mbed-OS error codes + * POSIX error codes may be used in modules/functions currently using POSIX error codes and switching them to Mbed-OS error codes * may cause interoperability issues. For example, some of the filesystem, network stack implementations may need to use - * Posix error codes in order to keep them compatible with other modules interfacing with them, and may continue to use Posix error codes. + * POSIX error codes in order to keep them compatible with other modules interfacing with them, and may continue to use POSIX error codes. * * In all other cases, like for any native development of Mbed-OS modules Mbed-OS error codes should be used. * This makes it easy to use Mbed-OS error reporting/logging infrastructure and makes debugging error scenarios @@ -576,7 +576,7 @@ typedef enum _mbed_module_type { typedef enum _mbed_error_code { //Below are POSIX ERROR CODE definitions, which starts at MBED_POSIX_ERROR_BASE(=0) - //POSIX ERROR CODE definitions starts at offset 0(MBED_POSIX_ERROR_BASE) to align them with actual Posix Error Code + //POSIX ERROR CODE definitions starts at offset 0(MBED_POSIX_ERROR_BASE) to align them with actual POSIX Error Code //defintions in mbed_retarget.h // Error Name Error Code MBED_DEFINE_POSIX_ERROR(EPERM, EPERM), /* 1 Operation not permitted */ diff --git a/platform/mbed_mem_trace.h b/platform/mbed_mem_trace.h index 047fef91641..965bf83c80f 100644 --- a/platform/mbed_mem_trace.h +++ b/platform/mbed_mem_trace.h @@ -80,13 +80,13 @@ void mbed_mem_trace_set_callback(mbed_mem_trace_cb_t cb); void mbed_mem_trace_disable(); /** - * Renable the memory trace output with the cb in use when disable was called + * Re-enable the memory trace output with the cb in use when disable was called */ void mbed_mem_trace_enable(); /** * Trace lock. - * @note Locking prevent recursive tracing of malloc/free inside relloc/calloc + * @note Locking prevent recursive tracing of malloc/free inside realloc/calloc */ void mbed_mem_trace_lock(); @@ -141,7 +141,7 @@ void mbed_mem_trace_free(void *ptr, void *caller); * * @param op identifies the memory operation ('m' for 'malloc', 'r' for 'realloc', * 'c' for 'calloc' and 'f' for 'free'). - * @param res (base 16) is the result of the memor operation. This is always NULL + * @param res (base 16) is the result of the memory operation. This is always NULL * for 'free', since 'free' doesn't return anything. * @param caller (base 16) is the caller of the memory operation. Note that the value * of 'caller' might be unreliable. diff --git a/platform/mbed_stats.h b/platform/mbed_stats.h index aa7b5819760..fb975f5f292 100644 --- a/platform/mbed_stats.h +++ b/platform/mbed_stats.h @@ -65,7 +65,7 @@ typedef struct { uint32_t thread_id; /**< Identifier for the thread that owns the stack or 0 if representing accumulated statistics */ uint32_t max_size; /**< Maximum number of bytes used on the stack since the thread was started */ uint32_t reserved_size; /**< Current number of bytes reserved for the stack */ - uint32_t stack_cnt; /**< The number of stacks represented in the accumulated statistics or 1 if repesenting a single stack */ + uint32_t stack_cnt; /**< The number of stacks represented in the accumulated statistics or 1 if representing a single stack */ } mbed_stats_stack_t; /** diff --git a/platform/mbed_toolchain.h b/platform/mbed_toolchain.h index 99e1cc11789..b1fb6dcd4d4 100644 --- a/platform/mbed_toolchain.h +++ b/platform/mbed_toolchain.h @@ -237,8 +237,8 @@ /** MBED_UNREACHABLE * An unreachable statement. If the statement is reached, - * behaviour is undefined. Useful in situations where the compiler - * cannot deduce the unreachability of code. + * behavior is undefined. Useful in situations where the compiler + * cannot deduce if the code is unreachable. * * @code * #include "mbed_toolchain.h" From 07eb6bd1593c5e96c68d418ea23706e27874b7e8 Mon Sep 17 00:00:00 2001 From: Marcus Chang Date: Thu, 1 Nov 2018 13:32:37 -0700 Subject: [PATCH 18/45] Add missing include for TLSSocket TCP and UDP sockets are automatically available when mbed.h is included in an application. This change lets the TLSSocket be used in the same way. --- features/netsocket/nsapi.h | 1 + 1 file changed, 1 insertion(+) diff --git a/features/netsocket/nsapi.h b/features/netsocket/nsapi.h index 21047dad737..a9b41932ec7 100644 --- a/features/netsocket/nsapi.h +++ b/features/netsocket/nsapi.h @@ -40,6 +40,7 @@ #include "netsocket/UDPSocket.h" #include "netsocket/TCPSocket.h" #include "netsocket/TCPServer.h" +#include "netsocket/TLSSocket.h" #endif From ae673d606cbe7a554b3b76ab0097bda6d26257d2 Mon Sep 17 00:00:00 2001 From: kegilbert Date: Thu, 1 Nov 2018 15:39:46 -0500 Subject: [PATCH 19/45] Fix typos in Events doxygen --- events/Event.h | 96 ++++++++++++++++++------------------- events/EventQueue.h | 18 +++---- events/mbed_shared_queues.h | 6 +-- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/events/Event.h b/events/Event.h index 3f66d4f72c0..dd0d8e6cf39 100644 --- a/events/Event.h +++ b/events/Event.h @@ -131,8 +131,8 @@ class Event { * The event is posted to the underlying queue and is executed in the * context of the event queue's dispatch loop. * - * The post function is irq safe and can act as a mechanism for moving - * events out of irq contexts. + * The post function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. * * @return A unique id that represents the posted event and can * be passed to EventQueue::cancel, or an id of 0 if @@ -179,7 +179,7 @@ class Event { * Attempts to cancel the most recently posted event. It is safe to call * cancel after an event has already been dispatched. * - * The cancel function is irq safe. + * The cancel function is IRQ safe. * * If called while the event queue's dispatch loop is active, the cancel * function does not guarantee that the event will not execute after it @@ -235,7 +235,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0 Argument to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0, the * arguments to the underlying callback. */ @@ -249,7 +249,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b1, the * arguments to the underlying callback. */ @@ -263,7 +263,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b2, the * arguments to the underlying callback. */ @@ -277,7 +277,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2,c3 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b3, the * arguments to the underlying callback. */ @@ -291,7 +291,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2,c3,c4 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b4, the * arguments to the underlying callback. */ @@ -583,8 +583,8 @@ class Event { * The event is posted to the underlying queue and is executed in the * context of the event queue's dispatch loop. * - * The post function is irq safe and can act as a mechanism for moving - * events out of irq contexts. + * The post function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. * * @param a0 Argument to pass to the event * @return A unique id that represents the posted event and can @@ -635,7 +635,7 @@ class Event { * Attempts to cancel the most recently posted event. It is safe to call * cancel after an event has already been dispatched. * - * The cancel function is irq safe. + * The cancel function is IRQ safe. * * If called while the event queue's dispatch loop is active, the cancel * function does not guarantee that the event will not execute after it @@ -691,7 +691,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0 Argument to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0, the * arguments to the underlying callback. */ @@ -705,7 +705,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b1, the * arguments to the underlying callback. */ @@ -719,7 +719,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b2, the * arguments to the underlying callback. */ @@ -733,7 +733,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2,c3 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b3, the * arguments to the underlying callback. */ @@ -747,7 +747,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2,c3,c4 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b4, the * arguments to the underlying callback. */ @@ -1039,8 +1039,8 @@ class Event { * The event is posted to the underlying queue and is executed in the * context of the event queue's dispatch loop. * - * The post function is irq safe and can act as a mechanism for moving - * events out of irq contexts. + * The post function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. * * @param a0,a1 Arguments to pass to the event * @return A unique id that represents the posted event and can @@ -1091,7 +1091,7 @@ class Event { * Attempts to cancel the most recently posted event. It is safe to call * cancel after an event has already been dispatched. * - * The cancel function is irq safe. + * The cancel function is IRQ safe. * * If called while the event queue's dispatch loop is active, the cancel * function does not guarantee that the event will not execute after it @@ -1147,7 +1147,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0 Argument to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0, the * arguments to the underlying callback. */ @@ -1161,7 +1161,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b1, the * arguments to the underlying callback. */ @@ -1175,7 +1175,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b2, the * arguments to the underlying callback. */ @@ -1189,7 +1189,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2,c3 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b3, the * arguments to the underlying callback. */ @@ -1203,7 +1203,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2,c3,c4 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b4, the * arguments to the underlying callback. */ @@ -1495,8 +1495,8 @@ class Event { * The event is posted to the underlying queue and is executed in the * context of the event queue's dispatch loop. * - * The post function is irq safe and can act as a mechanism for moving - * events out of irq contexts. + * The post function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. * * @param a0,a1,a2 Arguments to pass to the event * @return A unique id that represents the posted event and can @@ -1547,7 +1547,7 @@ class Event { * Attempts to cancel the most recently posted event. It is safe to call * cancel after an event has already been dispatched. * - * The cancel function is irq safe. + * The cancel function is IRQ safe. * * If called while the event queue's dispatch loop is active, the cancel * function does not guarantee that the event will not execute after it @@ -1603,7 +1603,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0 Argument to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0, the * arguments to the underlying callback. */ @@ -1617,7 +1617,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b1, the * arguments to the underlying callback. */ @@ -1631,7 +1631,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b2, the * arguments to the underlying callback. */ @@ -1645,7 +1645,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2,c3 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b3, the * arguments to the underlying callback. */ @@ -1659,7 +1659,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2,c3,c4 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b4, the * arguments to the underlying callback. */ @@ -1951,8 +1951,8 @@ class Event { * The event is posted to the underlying queue and is executed in the * context of the event queue's dispatch loop. * - * The post function is irq safe and can act as a mechanism for moving - * events out of irq contexts. + * The post function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. * * @param a0,a1,a2,a3 Arguments to pass to the event * @return A unique id that represents the posted event and can @@ -2003,7 +2003,7 @@ class Event { * Attempts to cancel the most recently posted event. It is safe to call * cancel after an event has already been dispatched. * - * The cancel function is irq safe. + * The cancel function is IRQ safe. * * If called while the event queue's dispatch loop is active, the cancel * function does not guarantee that the event will not execute after it @@ -2060,7 +2060,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0 Argument to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0, the * arguments to the underlying callback. */ @@ -2074,7 +2074,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b1, the * arguments to the underlying callback. */ @@ -2088,7 +2088,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b2, the * arguments to the underlying callback. */ @@ -2102,7 +2102,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2,c3 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b3, the * arguments to the underlying callback. */ @@ -2116,7 +2116,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2,c3,c4 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b4, the * arguments to the underlying callback. */ @@ -2408,8 +2408,8 @@ class Event { * The event is posted to the underlying queue and is executed in the * context of the event queue's dispatch loop. * - * The post function is irq safe and can act as a mechanism for moving - * events out of irq contexts. + * The post function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. * * @param a0,a1,a2,a3,a4 Arguments to pass to the event * @return A unique id that represents the posted event and can @@ -2460,7 +2460,7 @@ class Event { * Attempts to cancel the most recently posted event. It is safe to call * cancel after an event has already been dispatched. * - * The cancel function is irq safe. + * The cancel function is IRQ safe. * * If called while the event queue's dispatch loop is active, the cancel * function does not guarantee that the event will not execute after it @@ -2516,7 +2516,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0 Argument to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0, the * arguments to the underlying callback. */ @@ -2530,7 +2530,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b1, the * arguments to the underlying callback. */ @@ -2544,7 +2544,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b2, the * arguments to the underlying callback. */ @@ -2558,7 +2558,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2,c3 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b3, the * arguments to the underlying callback. */ @@ -2572,7 +2572,7 @@ class Event { * @param q Event queue to dispatch on * @param f Function to execute when the event is dispatched * @param c0,c1,c2,c3,c4 Arguments to bind to the callback, these arguments are - * allocated on an irq-safe allocator from the event queue's + * allocated on an IRQ-safe allocator from the event queue's * memory pool. Must be type-compatible with b0..b4, the * arguments to the underlying callback. */ diff --git a/events/EventQueue.h b/events/EventQueue.h index 72149663656..c6930d4c509 100644 --- a/events/EventQueue.h +++ b/events/EventQueue.h @@ -74,7 +74,7 @@ class EventQueue : private mbed::NonCopyable { * * When called with a finite timeout, the dispatch function is guaranteed * to terminate. When called with a timeout of 0, the dispatch function - * does not wait and is irq safe. + * does not wait and is IRQ safe. * * @param ms Time to wait for events in milliseconds, a negative * value will dispatch events indefinitely @@ -119,7 +119,7 @@ class EventQueue : private mbed::NonCopyable { * * id must be valid i.e. event must have not finished executing. * - * The cancel function is irq safe. + * The cancel function is IRQ safe. * * If called while the event queue's dispatch loop is active, the cancel * function does not guarantee that the event will not execute after it @@ -136,7 +136,7 @@ class EventQueue : private mbed::NonCopyable { * * id must be valid i.e. event must have not finished executing. * - * This function is irq safe. + * This function is IRQ safe. * * @param id Unique id of the event * @@ -191,8 +191,8 @@ class EventQueue : private mbed::NonCopyable { * The specified callback will be executed in the context of the event * queue's dispatch loop. * - * The call function is irq safe and can act as a mechanism for moving - * events out of irq contexts. + * The call function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. * * @param f Function to execute in the context of the dispatch loop * @param args Arguments to pass to the callback @@ -892,8 +892,8 @@ class EventQueue : private mbed::NonCopyable { * The specified callback will be executed in the context of the event * queue's dispatch loop. * - * The call_in function is irq safe and can act as a mechanism for moving - * events out of irq contexts. + * The call_in function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. * * @param ms Time to delay in milliseconds * @param f Function to execute in the context of the dispatch loop @@ -1199,8 +1199,8 @@ class EventQueue : private mbed::NonCopyable { * The specified callback will be executed in the context of the event * queue's dispatch loop. * - * The call_every function is irq safe and can act as a mechanism for - * moving events out of irq contexts. + * The call_every function is IRQ safe and can act as a mechanism for + * moving events out of IRQ contexts. * * @param f Function to execute in the context of the dispatch loop * @param ms Period of the event in milliseconds diff --git a/events/mbed_shared_queues.h b/events/mbed_shared_queues.h index 1535c398455..eb8d91e7910 100644 --- a/events/mbed_shared_queues.h +++ b/events/mbed_shared_queues.h @@ -41,9 +41,9 @@ namespace mbed { * If an RTOS is not present or the configuration option * `events.shared-dispatch-from-application` is set to true, then this * does not create a dedicated dispatch thread - instead the application is - * expected to run the EventQueue's dispatch, eg from main. This is necessary - * for the event loop to work without an RTOS, or an RTOS system can can save - * memory by reusing the main stack. + * expected to run the EventQueue's dispatch, for example from main. This is + * necessary for the event loop to work without an RTOS, or an RTOS system can + * save memory by reusing the main stack. * * @note * mbed_event_queue is not itself IRQ safe. To use the mbed_event_queue in From 3008de5cbf83365cea10e62680034f3180731847 Mon Sep 17 00:00:00 2001 From: kegilbert Date: Thu, 1 Nov 2018 15:45:55 -0500 Subject: [PATCH 20/45] Fix typos in RTOS doxygen. --- rtos/Queue.h | 2 +- rtos/RtosTimer.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rtos/Queue.h b/rtos/Queue.h index ecec922f70e..5785c9fcd7f 100644 --- a/rtos/Queue.h +++ b/rtos/Queue.h @@ -37,7 +37,7 @@ namespace rtos { */ /** The Queue class represents a collection of objects that are stored first by - * order of priorty, and then in first-in, first-out (FIFO) order. + * order of priority, and then in first-in, first-out (FIFO) order. * * You can use a queue when you need to store data and then access it in the same * order that it has been stored. The order in which you retrieve the data is in diff --git a/rtos/RtosTimer.h b/rtos/RtosTimer.h index ecf666f0327..eb976cd0bd4 100644 --- a/rtos/RtosTimer.h +++ b/rtos/RtosTimer.h @@ -88,7 +88,7 @@ class RtosTimer : private mbed::NonCopyable { public: /** Create timer. @param func function to be executed by this timer. - @param type osTimerOnce for one-shot or osTimerPeriodic for periodic behaviour. (default: osTimerPeriodic) + @param type osTimerOnce for one-shot or osTimerPeriodic for periodic behavior. (default: osTimerPeriodic) @param argument argument to the timer call back function. (default: NULL) @deprecated Replaced with RtosTimer(Callback, os_timer_type) @deprecated @@ -107,7 +107,7 @@ class RtosTimer : private mbed::NonCopyable { /** Create timer. @param func function to be executed by this timer. - @param type osTimerOnce for one-shot or osTimerPeriodic for periodic behaviour. (default: osTimerPeriodic) + @param type osTimerOnce for one-shot or osTimerPeriodic for periodic behavior. (default: osTimerPeriodic) @deprecated The RtosTimer has been superseded by the EventQueue. See RtosTimer.h for more details @@ -123,7 +123,7 @@ class RtosTimer : private mbed::NonCopyable { /** Create timer. @param obj pointer to the object to call the member function on. @param method member function to be executed by this timer. - @param type osTimerOnce for one-shot or osTimerPeriodic for periodic behaviour. (default: osTimerPeriodic) + @param type osTimerOnce for one-shot or osTimerPeriodic for periodic behavior. (default: osTimerPeriodic) @deprecated The RtosTimer constructor does not support cv-qualifiers. Replaced by RtosTimer(callback(obj, method), os_timer_type). From 3f635ef813d7c54ba478ca178e48a3281b70c366 Mon Sep 17 00:00:00 2001 From: Jeroen de Bruijn Date: Fri, 2 Nov 2018 13:44:24 +0100 Subject: [PATCH 21/45] fix: Update thread names Remove _thread suffix and rename threads. --- features/cellular/easy_cellular/CellularConnectionFSM.cpp | 2 +- features/lwipstack/lwip/src/include/lwip/opt.h | 2 +- features/lwipstack/ppp_lwip.cpp | 2 +- rtos/TARGET_CORTEX/mbed_rtos_rtx.c | 2 +- rtos/TARGET_CORTEX/mbed_rtx_conf.h | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/features/cellular/easy_cellular/CellularConnectionFSM.cpp b/features/cellular/easy_cellular/CellularConnectionFSM.cpp index 48647a485a1..e06d9f0aa78 100644 --- a/features/cellular/easy_cellular/CellularConnectionFSM.cpp +++ b/features/cellular/easy_cellular/CellularConnectionFSM.cpp @@ -617,7 +617,7 @@ nsapi_error_t CellularConnectionFSM::start_dispatch() { MBED_ASSERT(!_queue_thread); - _queue_thread = new rtos::Thread(osPriorityNormal, 2048, NULL, "fsm_queue_thread"); + _queue_thread = new rtos::Thread(osPriorityNormal, 2048, NULL, "cellular_fsm_queue"); if (!_queue_thread) { stop(); return NSAPI_ERROR_NO_MEMORY; diff --git a/features/lwipstack/lwip/src/include/lwip/opt.h b/features/lwipstack/lwip/src/include/lwip/opt.h index cf0a7af6d9e..1caa12d7692 100644 --- a/features/lwipstack/lwip/src/include/lwip/opt.h +++ b/features/lwipstack/lwip/src/include/lwip/opt.h @@ -1536,7 +1536,7 @@ * TCPIP_THREAD_NAME: The name assigned to the main tcpip thread. */ #if !defined TCPIP_THREAD_NAME || defined __DOXYGEN__ -#define TCPIP_THREAD_NAME "tcpip_thread" +#define TCPIP_THREAD_NAME "lwip_tcpip" #endif /** diff --git a/features/lwipstack/ppp_lwip.cpp b/features/lwipstack/ppp_lwip.cpp index 01f09635307..e27e07fc06f 100644 --- a/features/lwipstack/ppp_lwip.cpp +++ b/features/lwipstack/ppp_lwip.cpp @@ -68,7 +68,7 @@ static EventQueue *prepare_event_queue() // Only need to queue 2 events. new blows on failure. event_queue = new EventQueue(2 * EVENTS_EVENT_SIZE, NULL); - event_thread = new Thread(osPriorityNormal, PPP_THREAD_STACK_SIZE, NULL, "event_thread"); + event_thread = new Thread(osPriorityNormal, PPP_THREAD_STACK_SIZE, NULL, "ppp_lwip"); if (event_thread->start(callback(event_queue, &EventQueue::dispatch_forever)) != osOK) { delete event_thread; diff --git a/rtos/TARGET_CORTEX/mbed_rtos_rtx.c b/rtos/TARGET_CORTEX/mbed_rtos_rtx.c index 4485a180bef..55c43b40a94 100644 --- a/rtos/TARGET_CORTEX/mbed_rtos_rtx.c +++ b/rtos/TARGET_CORTEX/mbed_rtos_rtx.c @@ -52,7 +52,7 @@ MBED_NORETURN void mbed_rtos_start() _main_thread_attr.cb_size = sizeof(_main_obj); _main_thread_attr.cb_mem = &_main_obj; _main_thread_attr.priority = osPriorityNormal; - _main_thread_attr.name = "main_thread"; + _main_thread_attr.name = "rtx_main"; /* Allow non-secure main thread to call secure functions */ #if defined(DOMAIN_NS) && (DOMAIN_NS == 1U) diff --git a/rtos/TARGET_CORTEX/mbed_rtx_conf.h b/rtos/TARGET_CORTEX/mbed_rtx_conf.h index f0b2fc39849..daae8e787fb 100644 --- a/rtos/TARGET_CORTEX/mbed_rtx_conf.h +++ b/rtos/TARGET_CORTEX/mbed_rtx_conf.h @@ -70,8 +70,8 @@ // LIBSPACE default value set for ARMCC #define OS_THREAD_LIBSPACE_NUM 4 -#define OS_IDLE_THREAD_NAME "idle_thread" -#define OS_TIMER_THREAD_NAME "timer_thread" +#define OS_IDLE_THREAD_NAME "rtx_idle" +#define OS_TIMER_THREAD_NAME "rtx_timer" /* Enable only the evr events we use in Mbed-OS to save flash space. */ //Following events are used by Mbed-OS, DO NOT disable them From 467a7b63445a785d9a57b1a52caaf6536b3de79e Mon Sep 17 00:00:00 2001 From: Jeroen de Bruijn Date: Fri, 2 Nov 2018 13:52:18 +0100 Subject: [PATCH 22/45] feat: Add name to shared queue threads --- events/mbed_shared_queues.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/events/mbed_shared_queues.cpp b/events/mbed_shared_queues.cpp index 6504bb0c8dd..1fc6a079b9b 100644 --- a/events/mbed_shared_queues.cpp +++ b/events/mbed_shared_queues.cpp @@ -32,13 +32,13 @@ namespace mbed { */ template -EventQueue *do_shared_event_queue_with_thread() +EventQueue *do_shared_event_queue_with_thread(const char *name) { static uint64_t queue_buffer[QueueSize / sizeof(uint64_t)]; static EventQueue queue(sizeof queue_buffer, (unsigned char *) queue_buffer); static uint64_t stack[StackSize / sizeof(uint64_t)]; - static Thread thread(Priority, StackSize, (unsigned char *) stack); + static Thread thread(Priority, StackSize, (unsigned char *) stack, name); Thread::State state = thread.get_state(); if (state == Thread::Inactive || state == Thread::Deleted) { @@ -62,14 +62,14 @@ EventQueue *mbed_event_queue() return &queue; #else - return do_shared_event_queue_with_thread(); + return do_shared_event_queue_with_thread("shared_event_queue"); #endif } #ifdef MBED_CONF_RTOS_PRESENT EventQueue *mbed_highprio_event_queue() { - return do_shared_event_queue_with_thread(); + return do_shared_event_queue_with_thread("shared_highprio_event_queue"); } #endif From 440905a84ee51cdfde920db8741424ba009fafe1 Mon Sep 17 00:00:00 2001 From: Jeroen de Bruijn Date: Fri, 2 Nov 2018 16:23:52 +0100 Subject: [PATCH 23/45] fix: Change cellular FSM thread name --- features/cellular/easy_cellular/CellularConnectionFSM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/cellular/easy_cellular/CellularConnectionFSM.cpp b/features/cellular/easy_cellular/CellularConnectionFSM.cpp index e06d9f0aa78..376eedf7dbf 100644 --- a/features/cellular/easy_cellular/CellularConnectionFSM.cpp +++ b/features/cellular/easy_cellular/CellularConnectionFSM.cpp @@ -617,7 +617,7 @@ nsapi_error_t CellularConnectionFSM::start_dispatch() { MBED_ASSERT(!_queue_thread); - _queue_thread = new rtos::Thread(osPriorityNormal, 2048, NULL, "cellular_fsm_queue"); + _queue_thread = new rtos::Thread(osPriorityNormal, 2048, NULL, "cellular_fsm"); if (!_queue_thread) { stop(); return NSAPI_ERROR_NO_MEMORY; From 2ef82e18f5d74be4e1167b30fe38c1d3cf5f33c0 Mon Sep 17 00:00:00 2001 From: Jeroen de Bruijn Date: Sat, 3 Nov 2018 18:32:23 +0100 Subject: [PATCH 24/45] fix: Remove rtx from main thread name --- rtos/TARGET_CORTEX/mbed_rtos_rtx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtos/TARGET_CORTEX/mbed_rtos_rtx.c b/rtos/TARGET_CORTEX/mbed_rtos_rtx.c index 55c43b40a94..934eb8a5ca9 100644 --- a/rtos/TARGET_CORTEX/mbed_rtos_rtx.c +++ b/rtos/TARGET_CORTEX/mbed_rtos_rtx.c @@ -52,7 +52,7 @@ MBED_NORETURN void mbed_rtos_start() _main_thread_attr.cb_size = sizeof(_main_obj); _main_thread_attr.cb_mem = &_main_obj; _main_thread_attr.priority = osPriorityNormal; - _main_thread_attr.name = "rtx_main"; + _main_thread_attr.name = "main"; /* Allow non-secure main thread to call secure functions */ #if defined(DOMAIN_NS) && (DOMAIN_NS == 1U) From c7afb91f6231c78c912f4a8eb26e3aa0fa079e96 Mon Sep 17 00:00:00 2001 From: Kari Haapalehto Date: Mon, 5 Nov 2018 12:45:53 +0200 Subject: [PATCH 25/45] Fix compile warning about initializing and variable order --- .../nanostack/mbed-mesh-api/source/MeshInterfaceNanostack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/nanostack/mbed-mesh-api/source/MeshInterfaceNanostack.cpp b/features/nanostack/mbed-mesh-api/source/MeshInterfaceNanostack.cpp index 0a500863042..72702dd2bc9 100644 --- a/features/nanostack/mbed-mesh-api/source/MeshInterfaceNanostack.cpp +++ b/features/nanostack/mbed-mesh-api/source/MeshInterfaceNanostack.cpp @@ -69,7 +69,7 @@ void Nanostack::Interface::attach( } Nanostack::Interface::Interface(NanostackPhy &phy) : interface_phy(phy), interface_id(-1), _device_id(-1), - _connect_status(NSAPI_STATUS_DISCONNECTED), _blocking(true), _previous_connection_status(NSAPI_STATUS_DISCONNECTED) + _connect_status(NSAPI_STATUS_DISCONNECTED), _previous_connection_status(NSAPI_STATUS_DISCONNECTED), _blocking(true) { mesh_system_init(); } From 2df7258710756e1ed13468b42fc92c8cc05f7182 Mon Sep 17 00:00:00 2001 From: Offir Kochalsky Date: Mon, 5 Nov 2018 12:47:18 +0200 Subject: [PATCH 26/45] Add get_erase_value() support --- .../blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp | 5 +++++ .../blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp index 5e590d7c904..b4400424967 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp +++ b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp @@ -514,6 +514,11 @@ bd_size_t QSPIFBlockDevice::size() const return _device_size_bytes; } +int QSPIFBlockDevice::get_erase_value() const +{ + return 0xFF; +} + /********************************/ /* Different Device Csel Mgmt */ /********************************/ diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h index aad4e3fb373..c01bd5a8597 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h +++ b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h @@ -191,6 +191,17 @@ class QSPIFBlockDevice : public BlockDevice { */ virtual bd_size_t get_erase_size(bd_addr_t addr); + /** Get the value of storage byte after it was erased + * + * If get_erase_value returns a non-negative byte value, the underlying + * storage is set to that value when erased, and storage containing + * that value can be programmed without another erase. + * + * @return The value of storage when erased, or -1 if you can't + * rely on the value of erased storage + */ + virtual int get_erase_value() const; + /** Get the total size of the underlying device * * @return Size of the underlying device in bytes From 09ee5f914d9b5f5ad0a544211f95662cd3e67fdc Mon Sep 17 00:00:00 2001 From: Martin Kojtal Date: Thu, 25 Oct 2018 13:27:52 +0100 Subject: [PATCH 27/45] readme: add license section --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 778ef478317..7089096edf6 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,10 @@ Mbed OS provides a platform that includes: ## Release notes The [release notes](https://os.mbed.com/releases) detail the current release. You can also find information about previous versions. +## License & Contributions + +The software is provided under ]Apache-2.0 license](LICENSE). Contributions to this project are accepted under the same license. Please see [contributing.md](CONTRIBUTING.md) for more info. + ## Getting started for developers We have a [developer website](https://os.mbed.com) for asking questions, engaging with others, finding information on boards and components, using an online IDE and compiler, reading the documentation and learning about what's new and what's coming next in Mbed OS. @@ -39,3 +43,4 @@ We also have a [contributing and publishing guide](https://os.mbed.com/contribut For more information about Mbed OS, please see [our published documentation](https://os.mbed.com/docs/latest). It includes published Doxygen for our APIs, step-by-step tutorials, porting information and background reference materials about our architecture and tools. To contribute to this documentation, please see the [mbed-os-5-docs repo](https://github.com/ARMmbed/mbed-os-5-docs). + From 8ec69e6857574075c9cb95f78fd1e7af7bc1cfa5 Mon Sep 17 00:00:00 2001 From: Martin Kojtal Date: Thu, 25 Oct 2018 13:35:38 +0100 Subject: [PATCH 28/45] readme: add note about 3rd party code licenses --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7089096edf6..520ffe2a090 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,8 @@ The [release notes](https://os.mbed.com/releases) detail the current release. Yo The software is provided under ]Apache-2.0 license](LICENSE). Contributions to this project are accepted under the same license. Please see [contributing.md](CONTRIBUTING.md) for more info. +This project contains code from other projects. The original license text is included in those source files. + ## Getting started for developers We have a [developer website](https://os.mbed.com) for asking questions, engaging with others, finding information on boards and components, using an online IDE and compiler, reading the documentation and learning about what's new and what's coming next in Mbed OS. From f8fc0b3f3f54ebf49725e9b0afc5fa43b0362de3 Mon Sep 17 00:00:00 2001 From: Martin Kojtal Date: Thu, 25 Oct 2018 13:39:48 +0100 Subject: [PATCH 29/45] contributing: update the text Simplify the document. More information is provided on our documentation portal, link it here --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4dec4e8f598..c3d9cf127a7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,4 +2,4 @@ Mbed OS is an open-source, device software platform for the Internet of Things. Contributions are an important part of the platform, and our goal is to make it as simple as possible to become a contributor. -To encourage productive collaboration, as well as robust, consistent and maintainable code, we have a set of guidelines for contributing to Mbed OS. Please see: [contributing guidelines](https://os.mbed.com/docs/latest/reference/contributing.html). +To encourage productive collaboration, as well as robust, consistent and maintainable code, we have a set of guidelines for contributing to Mbed OS. Please see: [contributing guidelines](https://os.mbed.com/docs/latest/reference/contributing.html). From 97cb833afd3639bd8145902b6911f732d7ca50a0 Mon Sep 17 00:00:00 2001 From: Martin Kojtal Date: Thu, 25 Oct 2018 13:42:01 +0100 Subject: [PATCH 30/45] Readme: add license guide doc page --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 520ffe2a090..6dae3b640ab 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ The [release notes](https://os.mbed.com/releases) detail the current release. Yo The software is provided under ]Apache-2.0 license](LICENSE). Contributions to this project are accepted under the same license. Please see [contributing.md](CONTRIBUTING.md) for more info. -This project contains code from other projects. The original license text is included in those source files. +This project contains code from other projects. The original license text is included in those source files. They must comply with our [license guide](https://os.mbed.com/docs/latest/reference/license.html) ## Getting started for developers From 4aaf013b0e3b65a565601b188cf20d32da0e5019 Mon Sep 17 00:00:00 2001 From: Kari Haapalehto Date: Mon, 5 Nov 2018 13:09:56 +0200 Subject: [PATCH 31/45] Fix memory leak --- .../nanostack/mbed-mesh-api/source/NanostackEMACInterface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/features/nanostack/mbed-mesh-api/source/NanostackEMACInterface.cpp b/features/nanostack/mbed-mesh-api/source/NanostackEMACInterface.cpp index 29c4d7c3e54..37e047a2300 100644 --- a/features/nanostack/mbed-mesh-api/source/NanostackEMACInterface.cpp +++ b/features/nanostack/mbed-mesh-api/source/NanostackEMACInterface.cpp @@ -200,6 +200,7 @@ nsapi_error_t Nanostack::add_ethernet_interface(EMAC &emac, bool default_if, Nan nsapi_error_t err = interface->initialize(); if (err) { + delete interface; return err; } From dfadcdae20df3778611ab330ea06bba6b60a3fdd Mon Sep 17 00:00:00 2001 From: Martin Kojtal Date: Mon, 5 Nov 2018 11:10:41 +0000 Subject: [PATCH 32/45] readme: fix apache license --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6dae3b640ab..96a4e0227c6 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The [release notes](https://os.mbed.com/releases) detail the current release. Yo ## License & Contributions -The software is provided under ]Apache-2.0 license](LICENSE). Contributions to this project are accepted under the same license. Please see [contributing.md](CONTRIBUTING.md) for more info. +The software is provided under [Apache-2.0 license](LICENSE). Contributions to this project are accepted under the same license. Please see [contributing.md](CONTRIBUTING.md) for more info. This project contains code from other projects. The original license text is included in those source files. They must comply with our [license guide](https://os.mbed.com/docs/latest/reference/license.html) From 36f1fa06d624976b269dc6ba32301fc5c34cc2ab Mon Sep 17 00:00:00 2001 From: Jarno Lamsa Date: Tue, 30 Oct 2018 15:25:35 +0200 Subject: [PATCH 33/45] Remove extra _deselect to prevent possible hard fault If read timeout happens, the _deselect will get called twice causing a hard fault happening when mutex is released without being locked. The SDBlockDevice::read is calling the _deselect in every case. --- components/storage/blockdevice/COMPONENT_SD/SDBlockDevice.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/components/storage/blockdevice/COMPONENT_SD/SDBlockDevice.cpp b/components/storage/blockdevice/COMPONENT_SD/SDBlockDevice.cpp index 18759dcc799..24f1f129c59 100644 --- a/components/storage/blockdevice/COMPONENT_SD/SDBlockDevice.cpp +++ b/components/storage/blockdevice/COMPONENT_SD/SDBlockDevice.cpp @@ -892,7 +892,6 @@ int SDBlockDevice::_read(uint8_t *buffer, uint32_t length) // read until start byte (0xFE) if (false == _wait_token(SPI_START_BLOCK)) { debug_if(SD_DBG, "Read timeout\n"); - _deselect(); return SD_BLOCK_DEVICE_ERROR_NO_RESPONSE; } From 1ea28973b27001d32b07b3369d02401d569d0a0e Mon Sep 17 00:00:00 2001 From: jeromecoutant Date: Tue, 16 Oct 2018 16:43:23 +0200 Subject: [PATCH 34/45] TOOLS : Add missing M33 and M33F in python scripts --- tools/targets/__init__.py | 6 +++++- tools/test/toolchains/arm_support_test.py | 2 +- tools/toolchains/__init__.py | 4 ++-- tools/toolchains/arm.py | 7 +++++-- tools/toolchains/gcc.py | 6 ++++-- tools/toolchains/iar.py | 4 ++-- 6 files changed, 19 insertions(+), 10 deletions(-) diff --git a/tools/targets/__init__.py b/tools/targets/__init__.py index 42677cadf0c..1441faee5e0 100644 --- a/tools/targets/__init__.py +++ b/tools/targets/__init__.py @@ -49,7 +49,9 @@ "Cortex-M23": ["M23", "CORTEX_M", "LIKE_CORTEX_M23", "CORTEX"], "Cortex-M23-NS": ["M23", "M23_NS", "CORTEX_M", "LIKE_CORTEX_M23", "CORTEX"], "Cortex-M33": ["M33", "CORTEX_M", "LIKE_CORTEX_M33", "CORTEX"], - "Cortex-M33-NS": ["M33", "M33_NS", "CORTEX_M", "LIKE_CORTEX_M33", "CORTEX"] + "Cortex-M33-NS": ["M33", "M33_NS", "CORTEX_M", "LIKE_CORTEX_M33", "CORTEX"], + "Cortex-M33F": ["M33", "CORTEX_M", "LIKE_CORTEX_M33", "CORTEX"], + "Cortex-M33F-NS": ["M33", "M33_NS", "CORTEX_M", "LIKE_CORTEX_M33", "CORTEX"] } CORE_ARCH = { @@ -66,7 +68,9 @@ "Cortex-M23": 8, "Cortex-M23-NS": 8, "Cortex-M33": 8, + "Cortex-M33F": 8, "Cortex-M33-NS": 8, + "Cortex-M33F-NS": 8, } ################################################################################ diff --git a/tools/test/toolchains/arm_support_test.py b/tools/test/toolchains/arm_support_test.py index f26935a8f7d..b32bd7ab82f 100644 --- a/tools/test/toolchains/arm_support_test.py +++ b/tools/test/toolchains/arm_support_test.py @@ -17,7 +17,7 @@ ARMC5_CORES = ["Cortex-M0", "Cortex-M0+", "Cortex-M3", "Cortex-M4", "Cortex-M4F", "Cortex-M7", "Cortex-M7F", "Cortex-M7FD"] ARMC6_CORES = ARMC5_CORES + ["Cortex-M23", "Cortex-M23-NS", - "Cortex-M33", "CortexM33-NS"] + "Cortex-M33", "Cortex-M33-NS", "Cortex-M33F", "Cortex-M33F-NS"] CORE_SUF_ALPHA = ["MDFNS02347-+"] diff --git a/tools/toolchains/__init__.py b/tools/toolchains/__init__.py index 5c973538a27..dbb177e2f75 100644 --- a/tools/toolchains/__init__.py +++ b/tools/toolchains/__init__.py @@ -75,8 +75,8 @@ class mbedToolchain: "Cortex-M23": ["__CORTEX_M23", "ARM_MATH_ARMV8MBL", "__CMSIS_RTOS", "__MBED_CMSIS_RTOS_CM"], "Cortex-M33-NS": ["__CORTEX_M33", "ARM_MATH_ARMV8MML", "DOMAIN_NS=1", "__CMSIS_RTOS", "__MBED_CMSIS_RTOS_CM"], "Cortex-M33": ["__CORTEX_M33", "ARM_MATH_ARMV8MML", "__CMSIS_RTOS", "__MBED_CMSIS_RTOS_CM"], - "Cortex-M33F-NS": ["__CORTEX_M33", "ARM_MATH_ARMV8MML", "DOMAIN_NS=1", "__FPU_PRESENT", "__CMSIS_RTOS", "__MBED_CMSIS_RTOS_CM"], - "Cortex-M33F": ["__CORTEX_M33", "ARM_MATH_ARMV8MML", "__FPU_PRESENT", "__CMSIS_RTOS", "__MBED_CMSIS_RTOS_CM"], + "Cortex-M33F-NS": ["__CORTEX_M33", "ARM_MATH_ARMV8MML", "DOMAIN_NS=1", "__FPU_PRESENT=1U", "__CMSIS_RTOS", "__MBED_CMSIS_RTOS_CM"], + "Cortex-M33F": ["__CORTEX_M33", "ARM_MATH_ARMV8MML", "__FPU_PRESENT=1U", "__CMSIS_RTOS", "__MBED_CMSIS_RTOS_CM"], } MBED_CONFIG_FILE_NAME="mbed_config.h" diff --git a/tools/toolchains/arm.py b/tools/toolchains/arm.py index 525b7171d13..15b844ddebf 100644 --- a/tools/toolchains/arm.py +++ b/tools/toolchains/arm.py @@ -363,8 +363,8 @@ class ARMC6(ARM_STD): SHEBANG = "#! armclang -E --target=arm-arm-none-eabi -x c" SUPPORTED_CORES = ["Cortex-M0", "Cortex-M0+", "Cortex-M3", "Cortex-M4", "Cortex-M4F", "Cortex-M7", "Cortex-M7F", "Cortex-M7FD", - "Cortex-M23", "Cortex-M23-NS", "Cortex-M33", - "Cortex-M33-NS", "Cortex-A9"] + "Cortex-M23", "Cortex-M23-NS", "Cortex-M33", "Cortex-M33F", + "Cortex-M33-NS", "Cortex-M33F-NS", "Cortex-A9"] ARMCC_RANGE = (LooseVersion("6.10"), LooseVersion("7.0")) @staticmethod @@ -416,6 +416,9 @@ def __init__(self, target, *args, **kwargs): self.flags['common'].append("-mfloat-abi=softfp") elif target.core.startswith("Cortex-M23"): self.flags['common'].append("-march=armv8-m.base") + elif target.core.startswith("Cortex-M33F"): + self.flags['common'].append("-mfpu=fpv5-sp-d16") + self.flags['common'].append("-mfloat-abi=softfp") if target.core == "Cortex-M23" or target.core == "Cortex-M33": self.flags['cxx'].append("-mcmse") diff --git a/tools/toolchains/gcc.py b/tools/toolchains/gcc.py index 1ec8d8146b8..95e55747c25 100644 --- a/tools/toolchains/gcc.py +++ b/tools/toolchains/gcc.py @@ -62,8 +62,10 @@ def __init__(self, target, notify=None, macros=None, build_profile=None, self.cpu = ["-mcpu=cortex-m23"] elif target.core.startswith("Cortex-M33F"): self.cpu = ["-mcpu=cortex-m33"] + self.cpu = ["-mcpu=cortex-m33+nodsp"] elif target.core.startswith("Cortex-M33"): - self.cpu = ["-march=armv8-m.main"] + self.cpu = ["-mcpu=cortex-m33"] + self.cpu = ["-mcpu=cortex-m33+nodsp+nofp"] else: self.cpu = ["-mcpu={}".format(target.core.lower())] @@ -97,7 +99,7 @@ def __init__(self, target, notify=None, macros=None, build_profile=None, "-Wl,--cmse-implib", "-Wl,--out-implib=%s" % join(build_dir, "cmse_lib.o") ]) - elif target.core == "Cortex-M23-NS" or target.core == "Cortex-M33-NS": + elif target.core == "Cortex-M23-NS" or target.core == "Cortex-M33-NS" or target.core == "Cortex-M33F-NS": self.flags["ld"].append("-DDOMAIN_NS=1") self.flags["common"] += self.cpu diff --git a/tools/toolchains/iar.py b/tools/toolchains/iar.py index a56c8ad29c8..c94d5f089d4 100644 --- a/tools/toolchains/iar.py +++ b/tools/toolchains/iar.py @@ -74,12 +74,12 @@ def __init__(self, target, notify=None, macros=None, build_profile=None, elif target.core == "Cortex-M7F": asm_flags_cmd += ["--fpu", "VFPv5_sp"] c_flags_cmd.append("--fpu=VFPv5_sp") - elif target.core == "Cortex-M23" or target.core == "Cortex-M33": + elif target.core == "Cortex-M23" or target.core == "Cortex-M33" or target.core == "Cortex-M33F": self.flags["asm"] += ["--cmse"] self.flags["common"] += ["--cmse"] # Create Secure library - if target.core == "Cortex-M23" or self.target.core == "Cortex-M33": + if target.core == "Cortex-M23" or self.target.core == "Cortex-M33" or self.target.core == "Cortex-M33F": secure_file = join(build_dir, "cmse_lib.o") self.flags["ld"] += ["--import_cmse_lib_out=%s" % secure_file] From ba86a538710ae98134c58b0c91acc2e08795efcc Mon Sep 17 00:00:00 2001 From: Deepika Bhavnani Date: Mon, 5 Nov 2018 17:27:27 +0100 Subject: [PATCH 35/45] Update tools/toolchains/gcc.py --- tools/toolchains/gcc.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/toolchains/gcc.py b/tools/toolchains/gcc.py index 95e55747c25..b8adde954b2 100644 --- a/tools/toolchains/gcc.py +++ b/tools/toolchains/gcc.py @@ -61,10 +61,8 @@ def __init__(self, target, notify=None, macros=None, build_profile=None, elif target.core.startswith("Cortex-M23"): self.cpu = ["-mcpu=cortex-m23"] elif target.core.startswith("Cortex-M33F"): - self.cpu = ["-mcpu=cortex-m33"] self.cpu = ["-mcpu=cortex-m33+nodsp"] elif target.core.startswith("Cortex-M33"): - self.cpu = ["-mcpu=cortex-m33"] self.cpu = ["-mcpu=cortex-m33+nodsp+nofp"] else: self.cpu = ["-mcpu={}".format(target.core.lower())] From 8060a535d81488284775da58043f5cb942f2b17c Mon Sep 17 00:00:00 2001 From: Mel W Date: Tue, 6 Nov 2018 09:52:40 +0200 Subject: [PATCH 36/45] Comma, editorial changes --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c3d9cf127a7..33a04923c74 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,4 +2,4 @@ Mbed OS is an open-source, device software platform for the Internet of Things. Contributions are an important part of the platform, and our goal is to make it as simple as possible to become a contributor. -To encourage productive collaboration, as well as robust, consistent and maintainable code, we have a set of guidelines for contributing to Mbed OS. Please see: [contributing guidelines](https://os.mbed.com/docs/latest/reference/contributing.html). +To encourage productive collaboration, as well as robust, consistent, and maintainable code, we have a set of guidelines for [contributing to Mbed OS](https://os.mbed.com/docs/latest/reference/contributing.html). From 86153ddc426bb85bf0835d3062b03b1aacb51d7c Mon Sep 17 00:00:00 2001 From: Mel W Date: Tue, 6 Nov 2018 09:56:03 +0200 Subject: [PATCH 37/45] Commas and other small editorial changes --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 96a4e0227c6..8fb86424c1d 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,12 @@ [mbed-waffle]: https://waffle.io/ARMmbed/mbed-os [mbed-waffle-svg]: https://badge.waffle.io/ARMmbed/mbed-os.svg?columns=all -Arm Mbed OS is an open source embedded operating system designed specifically for the "things" in the Internet of Things. It includes all the features you need to develop a connected product based on an Arm Cortex-M microcontroller, including security, connectivity, an RTOS and drivers for sensors and I/O devices. +Arm Mbed OS is an open source embedded operating system designed specifically for the "things" in the Internet of Things. It includes all the features you need to develop a connected product based on an Arm Cortex-M microcontroller, including security, connectivity, an RTOS, and drivers for sensors and I/O devices. Mbed OS provides a platform that includes: * Security foundations. * Cloud management services. -* Drivers for sensors, I/O devices and connectivity. +* Drivers for sensors, I/O devices, and connectivity. ## Release notes The [release notes](https://os.mbed.com/releases) detail the current release. You can also find information about previous versions. @@ -42,7 +42,7 @@ We also have a [contributing and publishing guide](https://os.mbed.com/contribut ## Documentation -For more information about Mbed OS, please see [our published documentation](https://os.mbed.com/docs/latest). It includes published Doxygen for our APIs, step-by-step tutorials, porting information and background reference materials about our architecture and tools. +For more information about Mbed OS, please see [our published documentation](https://os.mbed.com/docs/latest). It includes Doxygen for our APIs, step-by-step tutorials, porting information, and background reference materials about our architecture and tools. -To contribute to this documentation, please see the [mbed-os-5-docs repo](https://github.com/ARMmbed/mbed-os-5-docs). +To contribute to this documentation, please see the [mbed-os-5-docs repository](https://github.com/ARMmbed/mbed-os-5-docs). From 64ef110efa8696c9b0601e982ffa1fb0575dea9c Mon Sep 17 00:00:00 2001 From: Amanda Butler Date: Tue, 6 Nov 2018 13:47:11 -0600 Subject: [PATCH 38/45] Edits in README.md Remove unnecessary commas, and standardize heading capitalization. --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8fb86424c1d..59d8fcc7e30 100644 --- a/README.md +++ b/README.md @@ -16,17 +16,18 @@ [mbed-waffle]: https://waffle.io/ARMmbed/mbed-os [mbed-waffle-svg]: https://badge.waffle.io/ARMmbed/mbed-os.svg?columns=all -Arm Mbed OS is an open source embedded operating system designed specifically for the "things" in the Internet of Things. It includes all the features you need to develop a connected product based on an Arm Cortex-M microcontroller, including security, connectivity, an RTOS, and drivers for sensors and I/O devices. +Arm Mbed OS is an open source embedded operating system designed specifically for the "things" in the Internet of Things. It includes all the features you need to develop a connected product based on an Arm Cortex-M microcontroller, including security, connectivity, an RTOS and drivers for sensors and I/O devices. Mbed OS provides a platform that includes: -* Security foundations. -* Cloud management services. -* Drivers for sensors, I/O devices, and connectivity. + +- Security foundations. +- Cloud management services. +- Drivers for sensors, I/O devices and connectivity. ## Release notes The [release notes](https://os.mbed.com/releases) detail the current release. You can also find information about previous versions. -## License & Contributions +## License and contributions The software is provided under [Apache-2.0 license](LICENSE). Contributions to this project are accepted under the same license. Please see [contributing.md](CONTRIBUTING.md) for more info. @@ -42,7 +43,7 @@ We also have a [contributing and publishing guide](https://os.mbed.com/contribut ## Documentation -For more information about Mbed OS, please see [our published documentation](https://os.mbed.com/docs/latest). It includes Doxygen for our APIs, step-by-step tutorials, porting information, and background reference materials about our architecture and tools. +For more information about Mbed OS, please see [our published documentation](https://os.mbed.com/docs/latest). It includes Doxygen for our APIs, step-by-step tutorials, porting information and background reference materials about our architecture and tools. To contribute to this documentation, please see the [mbed-os-5-docs repository](https://github.com/ARMmbed/mbed-os-5-docs). From 819f0029bf0066c34a9265a99adff3b0f0354daa Mon Sep 17 00:00:00 2001 From: Amanda Butler Date: Tue, 6 Nov 2018 13:47:38 -0600 Subject: [PATCH 39/45] Remove comma in CONTRIBUTING.md Remove unnecessary comma. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 33a04923c74..701af04107f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,4 +2,4 @@ Mbed OS is an open-source, device software platform for the Internet of Things. Contributions are an important part of the platform, and our goal is to make it as simple as possible to become a contributor. -To encourage productive collaboration, as well as robust, consistent, and maintainable code, we have a set of guidelines for [contributing to Mbed OS](https://os.mbed.com/docs/latest/reference/contributing.html). +To encourage productive collaboration, as well as robust, consistent and maintainable code, we have a set of guidelines for [contributing to Mbed OS](https://os.mbed.com/docs/latest/reference/contributing.html). From 44aaad486c20dd32c34cb2c9260d7a6cf0b41160 Mon Sep 17 00:00:00 2001 From: Amanda Butler Date: Tue, 6 Nov 2018 13:47:43 -0600 Subject: [PATCH 40/45] Remove comma in CONTRIBUTING.md Remove unnecessary comma. From 4bf4ac1e93ed5a244e35841606c5c1491fec564e Mon Sep 17 00:00:00 2001 From: David Saada Date: Tue, 30 Oct 2018 14:26:12 +0200 Subject: [PATCH 41/45] Support erase value in Flash HAL drivers, FlashIAP and block devices --- TESTS/mbed_drivers/flashiap/main.cpp | 18 ++++++++++++++---- .../COMPONENT_FLASHIAP/FlashIAPBlockDevice.cpp | 14 ++++++++++++++ .../COMPONENT_FLASHIAP/FlashIAPBlockDevice.h | 6 ++++++ drivers/FlashIAP.cpp | 5 +++++ drivers/FlashIAP.h | 7 +++++++ .../flash_common_algo.c | 7 +++++++ hal/flash_api.h | 7 +++++++ .../TARGET_ARM_FM/TARGET_FVP_MPS2/flash_api.c | 7 +++++++ .../TARGET_CM3DS_MPS2/flash_api.c | 7 +++++++ .../TARGET_Cypress/TARGET_PSOC6/flash_api.c | 7 +++++++ .../TARGET_MCUXpresso_MCUS/api/flash_api.c | 7 +++++++ .../TARGET_NRF5x/TARGET_NRF51/flash_api.c | 7 +++++++ .../TARGET_NRF5x/TARGET_NRF52/flash_api.c | 7 +++++++ .../TARGET_NUVOTON/TARGET_M2351/flash_api.c | 8 ++++++++ .../TARGET_LPC176X/device/flash_api.c | 7 +++++++ .../TARGET_LPC/flash_api.c | 7 +++++++ .../TARGET_RENESAS/TARGET_RZ_A1XX/flash_api.c | 8 ++++++++ .../TARGET_MCU_RTL8195A/flash_api.c | 7 +++++++ targets/TARGET_STM/TARGET_STM32F0/flash_api.c | 7 +++++++ targets/TARGET_STM/TARGET_STM32F1/flash_api.c | 7 +++++++ targets/TARGET_STM/TARGET_STM32F2/flash_api.c | 7 +++++++ targets/TARGET_STM/TARGET_STM32F3/flash_api.c | 7 +++++++ targets/TARGET_STM/TARGET_STM32F4/flash_api.c | 7 +++++++ targets/TARGET_STM/TARGET_STM32F7/flash_api.c | 7 +++++++ targets/TARGET_STM/TARGET_STM32L0/flash_api.c | 7 +++++++ targets/TARGET_STM/TARGET_STM32L1/flash_api.c | 7 +++++++ targets/TARGET_STM/TARGET_STM32L4/flash_api.c | 7 +++++++ .../TARGET_EFM32/flash_api.c | 12 ++++++++++++ .../TARGET_TOSHIBA/TARGET_TMPM46B/flash_api.c | 11 +++++++++++ .../TARGET_TOSHIBA/TARGET_TMPM4G9/flash_api.c | 7 +++++++ 30 files changed, 232 insertions(+), 4 deletions(-) diff --git a/TESTS/mbed_drivers/flashiap/main.cpp b/TESTS/mbed_drivers/flashiap/main.cpp index 6fecee4c79a..be46362d55f 100644 --- a/TESTS/mbed_drivers/flashiap/main.cpp +++ b/TESTS/mbed_drivers/flashiap/main.cpp @@ -55,9 +55,6 @@ void flashiap_program_test() TEST_ASSERT_TRUE(sector_size % page_size == 0); uint32_t prog_size = std::max(page_size, (uint32_t)8); uint8_t *data = new uint8_t[prog_size + 2]; - for (uint32_t i = 0; i < prog_size + 2; i++) { - data[i] = i; - } // the one before the last sector in the system uint32_t address = (flash_device.get_flash_start() + flash_device.get_flash_size()) - (sector_size); @@ -68,6 +65,20 @@ void flashiap_program_test() ret = flash_device.erase(address, sector_size); TEST_ASSERT_EQUAL_INT32(0, ret); + uint8_t erase_val = flash_device.get_erase_value(); + memset(data, erase_val, prog_size); + + uint8_t *data_flashed = new uint8_t[prog_size]; + for (uint32_t i = 0; i < sector_size / prog_size; i++) { + uint32_t page_addr = address + i * prog_size; + ret = flash_device.read(data_flashed, page_addr, prog_size); + TEST_ASSERT_EQUAL_INT32(0, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY(data, data_flashed, prog_size); + } + + for (uint32_t i = 0; i < prog_size + 2; i++) { + data[i] = i; + } for (uint32_t i = 0; i < sector_size / prog_size; i++) { uint32_t prog_addr = address + i * prog_size; @@ -75,7 +86,6 @@ void flashiap_program_test() TEST_ASSERT_EQUAL_INT32(0, ret); } - uint8_t *data_flashed = new uint8_t[prog_size]; for (uint32_t i = 0; i < sector_size / prog_size; i++) { uint32_t page_addr = address + i * prog_size; ret = flash_device.read(data_flashed, page_addr, prog_size); diff --git a/components/storage/blockdevice/COMPONENT_FLASHIAP/FlashIAPBlockDevice.cpp b/components/storage/blockdevice/COMPONENT_FLASHIAP/FlashIAPBlockDevice.cpp index 020964ec3f2..c0ce2ce2ba0 100644 --- a/components/storage/blockdevice/COMPONENT_FLASHIAP/FlashIAPBlockDevice.cpp +++ b/components/storage/blockdevice/COMPONENT_FLASHIAP/FlashIAPBlockDevice.cpp @@ -227,6 +227,20 @@ bd_size_t FlashIAPBlockDevice::get_erase_size(bd_addr_t addr) const return erase_size; } +int FlashIAPBlockDevice::get_erase_value() const +{ + if (!_is_initialized) { + return -1; + } + + uint8_t erase_val = _flash.get_erase_value(); + + DEBUG_PRINTF("get_erase_value: %" PRIX8 "\r\n", erase_val); + + return erase_val; +} + + bd_size_t FlashIAPBlockDevice::size() const { DEBUG_PRINTF("size: %" PRIX64 "\r\n", _size); diff --git a/components/storage/blockdevice/COMPONENT_FLASHIAP/FlashIAPBlockDevice.h b/components/storage/blockdevice/COMPONENT_FLASHIAP/FlashIAPBlockDevice.h index 20279658fb3..0c1213295a1 100644 --- a/components/storage/blockdevice/COMPONENT_FLASHIAP/FlashIAPBlockDevice.h +++ b/components/storage/blockdevice/COMPONENT_FLASHIAP/FlashIAPBlockDevice.h @@ -109,6 +109,12 @@ class FlashIAPBlockDevice : public BlockDevice { */ virtual bd_size_t get_erase_size(bd_addr_t addr) const; + /** Get the value of storage when erased + * + * @return The value of storage when erased + */ + virtual int get_erase_value() const; + /** Get the total size of the underlying device * * @return Size of the underlying device in bytes diff --git a/drivers/FlashIAP.cpp b/drivers/FlashIAP.cpp index 879a74f3c89..b6b1c2377da 100644 --- a/drivers/FlashIAP.cpp +++ b/drivers/FlashIAP.cpp @@ -203,6 +203,11 @@ uint32_t FlashIAP::get_flash_size() const return flash_get_size(&_flash); } +uint8_t FlashIAP::get_erase_value() const +{ + return flash_get_erase_value(&_flash); +} + } #endif diff --git a/drivers/FlashIAP.h b/drivers/FlashIAP.h index 1699b692453..a33436c0f04 100644 --- a/drivers/FlashIAP.h +++ b/drivers/FlashIAP.h @@ -131,6 +131,13 @@ class FlashIAP : private NonCopyable { */ uint32_t get_page_size() const; + /** Get the flash erase value + * + * Get the value we read after erase operation + * @return flash erase value + */ + uint8_t get_erase_value() const; + private: /* Check if address and size are aligned to a sector diff --git a/hal/TARGET_FLASH_CMSIS_ALGO/flash_common_algo.c b/hal/TARGET_FLASH_CMSIS_ALGO/flash_common_algo.c index 8fdc6443c67..b91191d9ebe 100644 --- a/hal/TARGET_FLASH_CMSIS_ALGO/flash_common_algo.c +++ b/hal/TARGET_FLASH_CMSIS_ALGO/flash_common_algo.c @@ -260,4 +260,11 @@ MBED_NONSECURE_ENTRY uint32_t flash_get_size(const flash_t *obj) return obj->target_config->flash_size; } +MBED_NONSECURE_ENTRY uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif // #ifndef DOMAIN_NS diff --git a/hal/flash_api.h b/hal/flash_api.h index 4231c083bac..4b59193e459 100644 --- a/hal/flash_api.h +++ b/hal/flash_api.h @@ -117,6 +117,13 @@ uint32_t flash_get_start_address(const flash_t *obj); */ uint32_t flash_get_size(const flash_t *obj); +/** Get the flash erase value + * + * @param obj The flash object + * @return The flash erase value + */ +uint8_t flash_get_erase_value(const flash_t *obj); + /**@}*/ #ifdef __cplusplus diff --git a/targets/TARGET_ARM_FM/TARGET_FVP_MPS2/flash_api.c b/targets/TARGET_ARM_FM/TARGET_FVP_MPS2/flash_api.c index 019388a145b..3fe832baf20 100644 --- a/targets/TARGET_ARM_FM/TARGET_FVP_MPS2/flash_api.c +++ b/targets/TARGET_ARM_FM/TARGET_FVP_MPS2/flash_api.c @@ -101,3 +101,10 @@ uint32_t flash_get_size(const flash_t *obj) return ZBT_SRAM1_SIZE; } + +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} diff --git a/targets/TARGET_ARM_SSG/TARGET_CM3DS_MPS2/flash_api.c b/targets/TARGET_ARM_SSG/TARGET_CM3DS_MPS2/flash_api.c index 2a96377aeb5..a86a95fb9d1 100644 --- a/targets/TARGET_ARM_SSG/TARGET_CM3DS_MPS2/flash_api.c +++ b/targets/TARGET_ARM_SSG/TARGET_CM3DS_MPS2/flash_api.c @@ -101,3 +101,10 @@ uint32_t flash_get_size(const flash_t *obj) return FLASH_SIZE; } + +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/flash_api.c b/targets/TARGET_Cypress/TARGET_PSOC6/flash_api.c index 4215ac72dd8..16452e61d23 100644 --- a/targets/TARGET_Cypress/TARGET_PSOC6/flash_api.c +++ b/targets/TARGET_Cypress/TARGET_PSOC6/flash_api.c @@ -82,4 +82,11 @@ uint32_t flash_get_size(const flash_t *obj) return CY_FLASH_SIZE; } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif // DEVICE_FLASH diff --git a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/flash_api.c b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/flash_api.c index eebb48072ce..e0e413ac1c6 100644 --- a/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/flash_api.c +++ b/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/flash_api.c @@ -148,4 +148,11 @@ uint32_t flash_get_size(const flash_t *obj) #endif } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF51/flash_api.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF51/flash_api.c index 2ab1acc374d..ab1c28d8b7b 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF51/flash_api.c +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF51/flash_api.c @@ -195,6 +195,13 @@ uint32_t flash_get_start_address(const flash_t *obj) return 0; } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif /** @}*/ diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/flash_api.c b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/flash_api.c index 58c028143dc..c5fce46be8c 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/flash_api.c +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/flash_api.c @@ -209,4 +209,11 @@ uint32_t flash_get_start_address(const flash_t *obj) return 0; } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif diff --git a/targets/TARGET_NUVOTON/TARGET_M2351/flash_api.c b/targets/TARGET_NUVOTON/TARGET_M2351/flash_api.c index 09314673e5d..6939db1b4f8 100644 --- a/targets/TARGET_NUVOTON/TARGET_M2351/flash_api.c +++ b/targets/TARGET_NUVOTON/TARGET_M2351/flash_api.c @@ -134,4 +134,12 @@ void flash_set_target_config(flash_t *obj) } #endif // #if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U) + +MBED_NONSECURE_ENTRY uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif // #if DEVICE_FLASH diff --git a/targets/TARGET_NXP/TARGET_LPC176X/device/flash_api.c b/targets/TARGET_NXP/TARGET_LPC176X/device/flash_api.c index bbf5c86aaa7..5693cd10b69 100644 --- a/targets/TARGET_NXP/TARGET_LPC176X/device/flash_api.c +++ b/targets/TARGET_NXP/TARGET_LPC176X/device/flash_api.c @@ -200,4 +200,11 @@ uint32_t flash_get_size(const flash_t *obj) return 0x80000; } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif diff --git a/targets/TARGET_NXP/TARGET_MCUXpresso_MCUS/TARGET_LPC/flash_api.c b/targets/TARGET_NXP/TARGET_MCUXpresso_MCUS/TARGET_LPC/flash_api.c index ccd89b06a6d..db047c712a5 100644 --- a/targets/TARGET_NXP/TARGET_MCUXpresso_MCUS/TARGET_LPC/flash_api.c +++ b/targets/TARGET_NXP/TARGET_MCUXpresso_MCUS/TARGET_LPC/flash_api.c @@ -122,4 +122,11 @@ uint32_t flash_get_size(const flash_t *obj) return FSL_FEATURE_SYSCON_FLASH_SIZE_BYTES; } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif diff --git a/targets/TARGET_RENESAS/TARGET_RZ_A1XX/flash_api.c b/targets/TARGET_RENESAS/TARGET_RZ_A1XX/flash_api.c index 34223653e84..ba24d29d9b7 100644 --- a/targets/TARGET_RENESAS/TARGET_RZ_A1XX/flash_api.c +++ b/targets/TARGET_RENESAS/TARGET_RZ_A1XX/flash_api.c @@ -751,4 +751,12 @@ static void cache_control(void) __DSB(); // ensure completion of the invalidation __ISB(); // ensure instruction fetch path sees new I cache state } + +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif diff --git a/targets/TARGET_Realtek/TARGET_AMEBA/TARGET_MCU_RTL8195A/flash_api.c b/targets/TARGET_Realtek/TARGET_AMEBA/TARGET_MCU_RTL8195A/flash_api.c index c8e3849aaef..813834e412d 100644 --- a/targets/TARGET_Realtek/TARGET_AMEBA/TARGET_MCU_RTL8195A/flash_api.c +++ b/targets/TARGET_Realtek/TARGET_AMEBA/TARGET_MCU_RTL8195A/flash_api.c @@ -69,3 +69,10 @@ uint32_t flash_get_size(const flash_t *obj) return FLASH_SIZE; } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + diff --git a/targets/TARGET_STM/TARGET_STM32F0/flash_api.c b/targets/TARGET_STM/TARGET_STM32F0/flash_api.c index 169128d9946..7b74c559883 100644 --- a/targets/TARGET_STM/TARGET_STM32F0/flash_api.c +++ b/targets/TARGET_STM/TARGET_STM32F0/flash_api.c @@ -172,4 +172,11 @@ uint32_t flash_get_size(const flash_t *obj) return FLASH_SIZE; } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif diff --git a/targets/TARGET_STM/TARGET_STM32F1/flash_api.c b/targets/TARGET_STM/TARGET_STM32F1/flash_api.c index 8c07436265b..2dd57c45281 100644 --- a/targets/TARGET_STM/TARGET_STM32F1/flash_api.c +++ b/targets/TARGET_STM/TARGET_STM32F1/flash_api.c @@ -172,4 +172,11 @@ uint32_t flash_get_size(const flash_t *obj) return FLASH_SIZE; } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif diff --git a/targets/TARGET_STM/TARGET_STM32F2/flash_api.c b/targets/TARGET_STM/TARGET_STM32F2/flash_api.c index 0c76349ad35..b5e05aa782f 100644 --- a/targets/TARGET_STM/TARGET_STM32F2/flash_api.c +++ b/targets/TARGET_STM/TARGET_STM32F2/flash_api.c @@ -215,4 +215,11 @@ static uint32_t GetSectorSize(uint32_t Sector) return sectorsize; } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif diff --git a/targets/TARGET_STM/TARGET_STM32F3/flash_api.c b/targets/TARGET_STM/TARGET_STM32F3/flash_api.c index 169128d9946..7b74c559883 100644 --- a/targets/TARGET_STM/TARGET_STM32F3/flash_api.c +++ b/targets/TARGET_STM/TARGET_STM32F3/flash_api.c @@ -172,4 +172,11 @@ uint32_t flash_get_size(const flash_t *obj) return FLASH_SIZE; } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif diff --git a/targets/TARGET_STM/TARGET_STM32F4/flash_api.c b/targets/TARGET_STM/TARGET_STM32F4/flash_api.c index 8f94d39b467..b9334c32d1d 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/flash_api.c +++ b/targets/TARGET_STM/TARGET_STM32F4/flash_api.c @@ -230,4 +230,11 @@ static uint32_t GetSectorSize(uint32_t Sector) return sectorsize; } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif diff --git a/targets/TARGET_STM/TARGET_STM32F7/flash_api.c b/targets/TARGET_STM/TARGET_STM32F7/flash_api.c index 495a4f97b2d..87d439edf25 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/flash_api.c +++ b/targets/TARGET_STM/TARGET_STM32F7/flash_api.c @@ -265,4 +265,11 @@ static uint32_t GetSectorSize(uint32_t Sector) return sectorsize; } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif diff --git a/targets/TARGET_STM/TARGET_STM32L0/flash_api.c b/targets/TARGET_STM/TARGET_STM32L0/flash_api.c index 8277e728544..77c1fde3077 100644 --- a/targets/TARGET_STM/TARGET_STM32L0/flash_api.c +++ b/targets/TARGET_STM/TARGET_STM32L0/flash_api.c @@ -174,4 +174,11 @@ uint32_t flash_get_size(const flash_t *obj) return FLASH_SIZE; } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif diff --git a/targets/TARGET_STM/TARGET_STM32L1/flash_api.c b/targets/TARGET_STM/TARGET_STM32L1/flash_api.c index 2b6389440d9..f6d64ba3193 100644 --- a/targets/TARGET_STM/TARGET_STM32L1/flash_api.c +++ b/targets/TARGET_STM/TARGET_STM32L1/flash_api.c @@ -171,4 +171,11 @@ uint32_t flash_get_size(const flash_t *obj) return FLASH_SIZE; } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif diff --git a/targets/TARGET_STM/TARGET_STM32L4/flash_api.c b/targets/TARGET_STM/TARGET_STM32L4/flash_api.c index 7361cbcccc8..fe2a44c5023 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/flash_api.c +++ b/targets/TARGET_STM/TARGET_STM32L4/flash_api.c @@ -284,4 +284,11 @@ uint32_t flash_get_size(const flash_t *obj) return FLASH_SIZE; } +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif diff --git a/targets/TARGET_Silicon_Labs/TARGET_EFM32/flash_api.c b/targets/TARGET_Silicon_Labs/TARGET_EFM32/flash_api.c index 85993676069..a741eb3f3f2 100644 --- a/targets/TARGET_Silicon_Labs/TARGET_EFM32/flash_api.c +++ b/targets/TARGET_Silicon_Labs/TARGET_EFM32/flash_api.c @@ -144,4 +144,16 @@ uint32_t flash_get_size(const flash_t *obj) return FLASH_SIZE; } +/** Get the flash erase value + * + * @param obj The flash object + * @return The flash erase value + */ +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + #endif // DEVICE_FLASH diff --git a/targets/TARGET_TOSHIBA/TARGET_TMPM46B/flash_api.c b/targets/TARGET_TOSHIBA/TARGET_TMPM46B/flash_api.c index 7dd16b4a108..0425fd9669a 100644 --- a/targets/TARGET_TOSHIBA/TARGET_TMPM46B/flash_api.c +++ b/targets/TARGET_TOSHIBA/TARGET_TMPM46B/flash_api.c @@ -162,3 +162,14 @@ uint32_t flash_get_size(const flash_t *obj) { return FLASH_CHIP_SIZE; } + +#if defined ( __ICCARM__ ) /* IAR Compiler */ +#pragma location = "FLASH_ROM" +#endif +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} + diff --git a/targets/TARGET_TOSHIBA/TARGET_TMPM4G9/flash_api.c b/targets/TARGET_TOSHIBA/TARGET_TMPM4G9/flash_api.c index 486612771d4..1245690750d 100644 --- a/targets/TARGET_TOSHIBA/TARGET_TMPM4G9/flash_api.c +++ b/targets/TARGET_TOSHIBA/TARGET_TMPM4G9/flash_api.c @@ -121,3 +121,10 @@ static void internal_hosc_enable(void) work = (uint32_t)(TSB_CG->OSCCR & ~CGOSCCR_IHOSC1EN_MASK); TSB_CG->OSCCR = (uint32_t)(work | CGOSCCR_IHOSC1EN_RW_ENABLE); } + +uint8_t flash_get_erase_value(const flash_t *obj) +{ + (void)obj; + + return 0xFF; +} From 9f2bbbc83e9137612ed7671ad3a3d0c4206f5e43 Mon Sep 17 00:00:00 2001 From: David Saada Date: Mon, 5 Nov 2018 15:42:57 +0200 Subject: [PATCH 42/45] Add a Unity macro to assert on platform error code difference --- features/frameworks/unity/unity/unity.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/features/frameworks/unity/unity/unity.h b/features/frameworks/unity/unity/unity.h index 85992fec047..1fe0948b8f7 100644 --- a/features/frameworks/unity/unity/unity.h +++ b/features/frameworks/unity/unity/unity.h @@ -24,6 +24,7 @@ extern "C" #define UNITY_FLOAT_VERBOSE #include "unity_internals.h" +#include "mbed_error.h" void setUp(void); void tearDown(void); @@ -294,6 +295,15 @@ void tearDown(void); #define TEST_ASSERT_DOUBLE_IS_NOT_NAN_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_NAN((actual), __LINE__, (message)) #define TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE_MESSAGE(actual, message) UNITY_TEST_ASSERT_DOUBLE_IS_NOT_DETERMINATE((actual), __LINE__, (message)) + +/*------------------------------------------------------- + * Error code checking + *-------------------------------------------------------*/ +// Use these to check whether error code equals what we expect. +// Only display error code (without other information) +#define TEST_ASSERT_EQUAL_ERROR_CODE(expected, actual) TEST_ASSERT_EQUAL(expected & MBED_ERROR_STATUS_CODE_MASK, actual & MBED_ERROR_STATUS_CODE_MASK) +#define TEST_ASSERT_EQUAL_ERROR_CODE_MESSAGE(expected, actual, message) TEST_ASSERT_EQUAL_MESSAGE(expected & MBED_ERROR_STATUS_CODE_MASK, actual & MBED_ERROR_STATUS_CODE_MASK) + /*------------------------------------------------------- * Test skipping *-------------------------------------------------------*/ From d616a5b9df49333349bf6fa5ed74aaff9934db3d Mon Sep 17 00:00:00 2001 From: David Saada Date: Tue, 9 Oct 2018 19:11:34 +0300 Subject: [PATCH 43/45] KVStore & derived classes: design docs & implementation --- docs/.gitignore | 2 + .../FileSystemStore_class_hierarchy.jpg | Bin 0 -> 12978 bytes .../FileSystemStore_class_hierarchy.xml | 1 + .../FileSystemStore/FileSystemStore_design.md | 381 ++++++++++++++ .../FileSystemStore_record.jpg | Bin 0 -> 14391 bytes .../FileSystemStore_record.xml | 1 + .../storage/KVStore/KVStore_classes.jpg | Bin 0 -> 17883 bytes .../storage/KVStore/KVStore_classes.xml | 1 + .../storage/KVStore/KVStore_design.md | 463 ++++++++++++++++ .../SecureStore_class_hierarchy.jpg | Bin 0 -> 19220 bytes .../SecureStore_class_hierarchy.xml | 1 + .../storage/SecureStore/SecureStore_design.md | 448 ++++++++++++++++ .../SecureStore/SecureStore_layers.jpg | Bin 0 -> 44929 bytes .../SecureStore/SecureStore_layers.xml | 1 + .../SecureStore/SecureStore_record.jpg | Bin 0 -> 20872 bytes .../SecureStore/SecureStore_record.xml | 1 + .../storage/TDBStore/TDBStore_areas.jpg | Bin 0 -> 31484 bytes .../storage/TDBStore/TDBStore_areas.xml | 1 + .../TDBStore/TDBStore_class_hierarchy.jpg | Bin 0 -> 19345 bytes .../TDBStore/TDBStore_class_hierarchy.xml | 1 + .../storage/TDBStore/TDBStore_design.md | 492 ++++++++++++++++++ .../storage/TDBStore/TDBStore_ram_table.jpg | Bin 0 -> 44054 bytes .../storage/TDBStore/TDBStore_ram_table.xml | 1 + .../storage/TDBStore/TDBStore_record.jpg | Bin 0 -> 30678 bytes .../storage/TDBStore/TDBStore_record.xml | 1 + features/storage/kvstore/KVStore.h | 205 ++++++++ platform/mbed_error.h | 5 + 27 files changed, 2006 insertions(+) create mode 100644 docs/.gitignore create mode 100644 docs/design-documents/features/storage/FileSystemStore/FileSystemStore_class_hierarchy.jpg create mode 100644 docs/design-documents/features/storage/FileSystemStore/FileSystemStore_class_hierarchy.xml create mode 100644 docs/design-documents/features/storage/FileSystemStore/FileSystemStore_design.md create mode 100644 docs/design-documents/features/storage/FileSystemStore/FileSystemStore_record.jpg create mode 100644 docs/design-documents/features/storage/FileSystemStore/FileSystemStore_record.xml create mode 100644 docs/design-documents/features/storage/KVStore/KVStore_classes.jpg create mode 100644 docs/design-documents/features/storage/KVStore/KVStore_classes.xml create mode 100644 docs/design-documents/features/storage/KVStore/KVStore_design.md create mode 100644 docs/design-documents/features/storage/SecureStore/SecureStore_class_hierarchy.jpg create mode 100644 docs/design-documents/features/storage/SecureStore/SecureStore_class_hierarchy.xml create mode 100644 docs/design-documents/features/storage/SecureStore/SecureStore_design.md create mode 100644 docs/design-documents/features/storage/SecureStore/SecureStore_layers.jpg create mode 100644 docs/design-documents/features/storage/SecureStore/SecureStore_layers.xml create mode 100644 docs/design-documents/features/storage/SecureStore/SecureStore_record.jpg create mode 100644 docs/design-documents/features/storage/SecureStore/SecureStore_record.xml create mode 100644 docs/design-documents/features/storage/TDBStore/TDBStore_areas.jpg create mode 100644 docs/design-documents/features/storage/TDBStore/TDBStore_areas.xml create mode 100644 docs/design-documents/features/storage/TDBStore/TDBStore_class_hierarchy.jpg create mode 100644 docs/design-documents/features/storage/TDBStore/TDBStore_class_hierarchy.xml create mode 100644 docs/design-documents/features/storage/TDBStore/TDBStore_design.md create mode 100644 docs/design-documents/features/storage/TDBStore/TDBStore_ram_table.jpg create mode 100644 docs/design-documents/features/storage/TDBStore/TDBStore_ram_table.xml create mode 100644 docs/design-documents/features/storage/TDBStore/TDBStore_record.jpg create mode 100644 docs/design-documents/features/storage/TDBStore/TDBStore_record.xml create mode 100644 features/storage/kvstore/KVStore.h diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000000..e560daa3f6c --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +*.md.html + diff --git a/docs/design-documents/features/storage/FileSystemStore/FileSystemStore_class_hierarchy.jpg b/docs/design-documents/features/storage/FileSystemStore/FileSystemStore_class_hierarchy.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ca9f62a31e4b542e631997a15ed955b15d97e9f7 GIT binary patch literal 12978 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 LlO73IBcuNxmjjXk literal 0 HcmV?d00001 diff --git a/docs/design-documents/features/storage/FileSystemStore/FileSystemStore_class_hierarchy.xml b/docs/design-documents/features/storage/FileSystemStore/FileSystemStore_class_hierarchy.xml new file mode 100644 index 00000000000..173fdab83b4 --- /dev/null +++ b/docs/design-documents/features/storage/FileSystemStore/FileSystemStore_class_hierarchy.xml @@ -0,0 +1 @@ +7VfbbtswDP0aP3bwJU7zmjRNCwwDBgS7PSo2bQuVLU9WEmdfP8qm41u8ZsDWbkDzEFuHFEUe8ZJY3l1aPiiWJx9kCMJy7bC0vLXluo7vz/BhkFON3C4IiBUPSakFtvwHEGgTuuchFD1FLaXQPO+DgcwyCHQPY0rJY18tkqJ/as5iGAHbgIkx+oWHOqnRhW+3+CPwOGlOdmyS7FjwFCu5z+g8y/Wi6lOLU9bYIv0iYaE8diDv3vLulJS6fkvLOxCG24a2et9mQnr2W0Gmr9pAfhyY2EPj8lzg3lVu3NMnomT+fW98WqVMxTyzvCVK7bzEbwSryAx+o2Vey2YdmYZS3zDBY9oXoHOgWpv4FtOzOnnXABsuYHsqNKRbLRU0cgxnN9yDWD7EEmVCaJKrOc2ZPjjkh4tBJ3TXxnnXBDZlAL2obDSo2zPnHkBpjlm2rLlYV2ytiJm1gMjskqgViSopIo6X5a0imWmqEcel9YalXJjqegRxAGPV+KlTYZTOZ3cTgXLD+ABlB6LEeACZglYnVCGp61FyUBG7M1of25JwGizplMOCMEZVGJ9Nt5mIL5SME4npjBLzUwEjRiELl6becSVzQBZXiHS5ClmRQEikXMMPhL3WMGanG/2cIlUgmOaHfve4FD6Z+yg5Hntm2rsdMD1ksJB7FQDt6pbzwJBvP2NIY5mCHhmqbuMc43UX5P4fneNXTWOn3trIX28jo5x80TYy+5ez9P3nt7n2+nNt8ZL56F/Ix+mZthMyeBpOMZRvKgLX9njedahChtTpq1F75zfLb8/RWA+IfvG8ykic6hq/OxJn84Eh70+NRFy2P9Rr9fbfkHf/Ew== \ No newline at end of file diff --git a/docs/design-documents/features/storage/FileSystemStore/FileSystemStore_design.md b/docs/design-documents/features/storage/FileSystemStore/FileSystemStore_design.md new file mode 100644 index 00000000000..bb7077d952b --- /dev/null +++ b/docs/design-documents/features/storage/FileSystemStore/FileSystemStore_design.md @@ -0,0 +1,381 @@ +# FileSystemStore in Mbed OS + +- [FileSystemStore in Mbed OS](#filesystemstore-in-mbed-os) + + [Revision history](#revision-history) +- [Introduction](#introduction) + + [Overview and background](#overview-and-background) + + [Requirements and assumptions](#requirements-and-assumptions) +- [System architecture and high-level design](#system-architecture-and-high-level-design) + * [Design basics](#design-basics) +- [Detailed design](#detailed-design) + + [Class header](#class-header) + + [Important data structures](#important-data-structures) + + [Initialization and reset](#initialization-and-reset) + + [Core APIs](#core-apis) + + [Incremental set APIs](#incremental-set-apis) + + [Key iterator APIs](#key-iterator-apis) +- [Usage scenarios and examples](#usage-scenarios-and-examples) + + [Standard usage of the class](#standard-usage-of-the-class) +- [Other information](#other-information) + + [Open issues](#open-issues) + + +### Revision history + +| Revision | Date | Authors | Mbed OS version | Comments | +|---------- |---------------- |-------------------------------------------------------- |----------------- |------------------ | +| 1.0 | 20 September 2018 | David Saada ([@davidsaada](https://github.com/davidsaada/)) | 5.11+ | Initial revision | + +# Introduction + +### Overview and background + +FileSystemStore is a lightweight implementation of the [KVStore](../KVStore/KVStore_design.md) interface over file systems. + +### Requirements and assumptions + +FileSystemStore assumes the underlying file system qualities for resilience and file validation. This means that if the underlying file system has no protection against power failures, then neither would FileSystemStore have. +When initializing this class, it is assumed that the underlying FileSystem is initialized and mounted. + +# System architecture and high-level design + +## Design basics + +FileSystemStore implements the get/set interface using files, where each key is represented by a single file. Key is represented by the file name and value is stored as file data. Therefore, FileSystemStore imitates the get/set actions using simple file operations. set is achieved using open-write-close, get using open-read-close and so on. +All files are concentrated under a single directory, whose name is hard coded. So actions such as "reset" are mapped to the deletion of all files under this directory, while iteration actions use file system APIs to traverse the directory. + +### Data layout + +When storing the data, it is stored with a preceding 16-byte metadata header. Metadata includes flags and other parameters for basic validity checks. + +![FileSystemStore Record](./FileSystemStore_record.jpg) + +Fields are: +- Magic: A constant value, for quick validity checking +- Metadata size: Size of metadata header +- Revision: FileSystemStore revision (currently 1) +- User flags: Flags received from user. Currently only write once is dealt with (others are ignored) + +# Detailed design + +FileSystemStore fully implements the KVStore interface over a file system. As such, it uses the FileSystem class interface for file operations. + +![FileSystemStore Class Hierarchy](./FileSystemStore_class_hierarchy.jpg) + +Functionality, as defined by KVStore, includes the following: +- Initialization & reset +- Core actions: get, set & remove +- Incremental set actions +- Iterator actions + +### Class header + +FileSystemStore has the following header: + +```C++ +class FileSystemStore : KVStore { + +public: + FileSystemStore(FileSystem *fs); + virtual ~FileSystemStore(); + + // Initialization and reset + virtual int init(); + virtual int deinit(); + virtual int reset(); + + // Core API + virtual int set(const char *key, const void *buffer, size_t size, uint32_t create_flags); + virtual int get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size = NULL, size_t offset = 0); + virtual int get_info(const char *key, info_t *info); + virtual int remove(const char *key); + + // Incremental set API + virtual int set_start(set_handle_t *handle, const char *key, size_t final_data_size, uint32_t create_flags); + virtual int set_add_data(set_handle_t handle, const void *value_data, size_t data_size); + virtual int set_finalize(set_handle_t handle); + + // Key iterator + virtual int iterator_open(iterator_t *it, const char *prefix = NULL); + virtual int iterator_next(iterator_t it, char *key, size_t key_size); + virtual int iterator_close(iterator_t it); + +private: + Mutex _mutex; + FileSystem *_fs; + bool _is_initialized; +} +``` + +### Important data structures + +```C++ +// Key metadata +typedef struct { + uint32_t magic; + uint16_t metadata_size; + uint16_t revision; + uint32_t flags; +} key_metadata_t; + +// incremental set handle +typedef struct { + char *key; + uint32_t create_flags; +} inc_set_handle_t; + +// iterator handle +typedef struct { + void *dir_handle; + char *prefix; +} key_iterator_handle_t; +``` + + +### Initialization and reset + +**init function** + +Header: +`virtual int init();` + +Pseudo code: +- if `_is_initialized` return OK +- Create and take `_mutex` +- Create the FileSystemStore directory if it doesn't exist +- Set `_is_initialized` to true +- Release `_mutex` + +**deinit function** + +Header: +`virtual int deinit();` + +Pseudo code: +- if not `_is_initialized` return OK +- Take `_mutex` +- Set `_is_initialized` to false +- Release `_mutex` + +**reset function** + +Header: +`virtual int reset();` + +Pseudo code: +- Take `_mutex` +- Delete all files under the FileSystemStore directory +- Set `_num_keys` to 0 +- Release `_mutex` + +### Core APIs + +**set function** + +Header: +`virtual int set(const char *key, const void *buffer, size_t size, uint32_t create_flags);` + +Pseudo code: +- if not `_is_initialized` return "not initialized" error +- Call `set_start` with all fields and a local `set_handle_t` variable +- Call `set_add_data` with `buffer` and `size` +- Call `set_finalize` +- Return OK + +**get function** + +Header: +`virtual int get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size = NULL, size_t offset = 0);` + +Pseudo code: +- if not `_is_initialized` return "not initialized" error +- Take `_mutex` +- Using the `stat` API, extract file size +- Open file `key` for reading to achieve a file handle +- If failed, release `_mutex` and return "not found" error +- Read from file into a `key_metadata_t` structure +- Using `size` API, achieve file size +- Seek to `offset` + metadata size +- Set `actual_size` as the minimum of buffer size and remainder of data +- Read data from file to `buffer`, size is `actual_size` +- Close file +- Release `_mutex` +- Return OK + +**get_info function** + +Header: +`virtual int get_info(const char *key, info_t *info);` + +Pseudo code: +- if not `_is_initialized` return "not initialized" error +- Find file `key` under the FileSystemStore directory. If not existing, return "not found" error. +- Take `_mutex` +- Open file `key` for reading to achieve a file handle +- If failed, release `_mutex` and return "not found" error +- Using `size` API, achieve file size +- Read from file into a `key_metadata_t` structure +- Fill `info` structure with all relevant fields +- Close file +- Return OK + +**remove function** + +Header: +`virtual int remove(const char *key);` + +Pseudo code: +- if not `_is_initialized` return "not initialized" error +- Take `_mutex` +- Open file `key` for reading and read data into a `key_metadata_t` structure +- If not existing, return "not found error" +- If flag "write once" is preset, return "write once" error +- Delete file `key` +- Release `_mutex` +- Return OK + +### Incremental set APIs + +**set_start function** + +Header: +`virtual int set_start(set_handle_t *handle, const char *key, size_t final_data_size, uint32_t create_flags);` + +Pseudo code: +- Find file `key` under the FileSystemStore directory. If not existing, increase `_num_keys` by 1. +- Take `_mutex` +- Open file for reading and read data into a `key_metadata_t` structure +- If existing and flag "write once" is preset, return "write once" error +- Close file +- Allocate an `inc_set_handle_t` structure into `handle` +- Duplicate `key` in `handle`. +- Update `create_flags` in `handle` +- Fill `key_metadata_t` structure with all relevant values (`create_flags` from handle) +- Open file `key` for writing to achieve a file handle +- Write metadata structure to the file +- Close file + +**set_add_data function** + +Header: +`virtual int set_add_data(set_handle_t handle, const void *value_data, size_t data_size);` + +Pseudo code: +- Open file `key` for appending to achieve a file handle +- Write `value_data` to the file +- Close file + +**set_finalize function** + +Header: +`virtual int set_finalize(set_handle_t handle);` + +Pseudo code: +- Free `key` in `handle` and then `handle` +- Release `_mutex` + +### Key iterator APIs + +**iterator_open function** + +Header: +`virtual int iterator_open(iterator_t *it, const char *prefix = NULL);` + +Pseudo code: +- Take `_mutex` +- Allocate a `key_iterator_handle_t` structure into `it` +- Duplicate `prefix` into same field in iterator +- Using direcory `open` API, open FileSystemStore directory and store dir handle in the handle's `dir_handle` field +- Release `_mutex` + +**iterator_next function** + +Header: +`virtual int iterator_next(iterator_t it, char *key, size_t key_size);` + +Pseudo code: +- Take `_mutex` +- Using direcory `read` API on handle's `dir_handle` field, read next file in directory +- While not reached end of directory + - If name matches prefix + - Copy file name to `key` and return OK + - Using direcory `read` API on handle's `dir_handle` field, read next file in directory +- Return "not found" error +- Release `_mutex` + +**iterator_close function** + +Header: +`virtual int iterator_close(iterator_t it);` + +Pseudo code: +- Using direcory `close` API on `dir_handle` close handle +- Release `prefix` field in iterator and structure allocated at `it` + + +# Usage scenarios and examples + +### Standard usage of the class + +Following example code shows standard usage of the FileSystemStore class + +**Standard usage example** + +```C++ + +// External file system of LittleFS type. Should be initialized. +extern LittleFileSystem fs; + +// Instantiate fsstore with our file system +FileSystemStore fsstore(&fs); + +int res; + +// Initialize fsstore +res = fsstore.init(); + +// Add "Key1" +const char *val1 = "Value of key 1"; +const char *val2 = "Updated value of key 1"; +res = fsstore.set("Key1", val1, sizeof(val1), 0); +// Update value of "Key1" +res = fsstore.set("Key1", val2, sizeof(val2), 0); + +uint_8 value[32]; +size_t actual_size; +// Get value of "Key1". Value should return the updated value. +res = fsstore.get("Key1", value, sizeof(value), &actual_size); + +// Remove "Key1" +res = fsstore.remove("Key1"); + +// Incremental write, if need to generate large data with a small buffer +const int data_size = 1024; +char buf[8]; + +KVSTore::set_handle_t handle; +res = fsstore.set_start(&handle, "Key2", data_size, 0); +for (int i = 0; i < data_size / sizeof(buf); i++) { + memset(buf, i, sizeof(buf)); + res = fsstore.set_add_data(handle, buf, sizeof(buf)); +} +res = fsstore.set_finalize(handle); + +// Iterate over all keys starting with "Key" +res = 0; +KVSTore::iterator_t it; +fsstore.iterator_open(&it, "Key*"); +char key[KVSTore::KV_MAX_KEY_LENGTH]; +while (!res) { + res = fsstore.iterator_next(&it, key, sizeof(key)); +} +res = fsstore.iterator_close(&it); + +// Deinitialize FileSystemStore +res = fsstore.deinit(); +``` +# Other information + +### Open issues + +- Need to figure a way to prevent mutex abuse in incremental set APIs. diff --git a/docs/design-documents/features/storage/FileSystemStore/FileSystemStore_record.jpg b/docs/design-documents/features/storage/FileSystemStore/FileSystemStore_record.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2d0d9d7386eb87e2a293d35c47ec085c7741d484 GIT binary patch literal 14391 zcmd^l2UJtrns5+NKoO88AYh?GK#?NS6s07&fV4ysib_o)AiV{m2qInjH8ho8bEWrO zXPc{}`+%POzL{V`XP&V`XDwKh1fX{nVLLY-}9d z9A`MWxVX94PoL#E%f-X^&-G&yrXNo-A7^De$aRYC6yxrHanOGUaI*s9nIOze+<;@; zOw8O&^mc$415OsEzYM@%2h%a;<18mmva+$CVl1fU0vuz!``B@27MA138LNXC^MK>r zEN3rXy>)`e^uftX9=u9V6W*|1)_wP$&uoA!e(j-WC>uM!z&Sx72}vnw8Chi&RkiEt zH*Vk2(>E{#fz2%}p|E>aaC-+wCubKV%FElw*Uvv7@L5>+^N1IbQHe>(DXD3{zQVoD z$<50zC@d3~ib=3X5qlkp^q>lhv2L<|?&Y&_7$s0{l6 z4SwI}n_gTbhOJ9xEd|J5L`o#;zo=}yDt|8#B#r}oBm9#u0Q@(Dt3n6-05^h?NLC2v zT;C&)C{Ok=Q!6$}y%?$c7jXXW-&mqx@!b=jqBXlq^(`P5&P{nTtzgMr#EK6`!X~Zw z!^_@Vv}-p{9xC~*zoP^6hsY�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/dBMtdJNtnibP4VRDKLWSTreU+WAg5vo/gTyO7CplMvQqIJCPGqQWW7A81oG3QghGa9xyVEFyxrA+GIk1pqHoYllKUfbe14P1TMxzRI2AZYn6M/mZExRU69e0Wf6EsiuuTHdtYVjh8i6QoUnOehdz1/leZE1zvZfzzGBNRdiD30XIXUghVjZLtgnLNbU1bte5pxNrELWmqzlkQTKoVG8wLWocccFg7J2wDw0gPa2hVA684YmGNwu6rAU87VzvDZ/Be6Aear0Wq7vJ9tu/BwbGzbWs82kIeIkMHATYQ6KnYv3nfT0Y+dGxvP9R7NrTPNtV82mAuY6boMsOhtpZQHYDFKuEwc5rV3fyYlG2oVHTbgUy+nqlIqJI7cDHWqZGOKS2nllLZCtUzUNzR6MRg2JRG1GzcygMGRiHDapnZn1MLVZhghW9UMPv2dFkJotuWYKO5ExoMrqVBZ1SDDZE/6ebHKYL/69yc0R/8a+XG+1R/+JVTedkCe+I4ym+03dz6+wm5XydAxx5XYMPkw+ir6KuZO6N0Hfdq1AUD1B0wAtfVTA/DQvLdXMJll6qPqWl5rGYKKyZSmN7N7As1vckhc94Rc2iAOGcWXIK56ajodEc5q9X4J1vN2E3q7B5UxTGicyBZ9TOWKyne6EJwIQFJRUp1yIzzAwhzFulEhpA66OHuXKeMwXfUvTEkjBB9zKBE+vVFmIRPuUoYuSh02q5xV0G1rLtFNaQN79+LCqbtp9ve1vk+dh//Ag== \ No newline at end of file diff --git a/docs/design-documents/features/storage/KVStore/KVStore_classes.jpg b/docs/design-documents/features/storage/KVStore/KVStore_classes.jpg new file mode 100644 index 0000000000000000000000000000000000000000..72d89facd008a8dea016c2f732e5ce0bca7b9a38 GIT binary patch literal 17883 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/drxODdAi6ou7UN5gPh4PB6fOeOJ8MLbon6QtMq/ihS4R/y09sK5R0gQkYn+McjOIldBYIFMshSNOmDB/gCCPqIblsJ6YKiE4IpVQzARZQmJGmBUSvEyNFsJPty1ohnsAYuE8n30B0tVbtFp7Hf4I7AsdzsHPs4safKcSbEpcT+PhKvmY6cL6nyh/TqnqXjpQeGdF95KIZR9Kupb4IZbR5tdd39kto1bQqlOWoBxbCnfgAt5wvXaWWXCUzukZPJ7Y2KaFVRmrPTCGz3rV7X+1mBzMoNfKFHZuag3p6BWF5SzDNclOjiQnU/9lOFvs/PSAd/ms4USEtyEPsdybKyxaozl0sTuVOW2CY7vmLLtwdPmmGQTNTEnOuZAR9H4cCgZuCNbkIpped1YEuYNTTOkZM5hZVYJbbXijRpWTGcpnK1EqbA4AoLje1owbsrqEfgWjFcTpyq4MWr37isARWFigLoHoSIeQBSg5E6b4GwYoSqweokbv3S1EDgs79XBFDGK5Ze1rjsJ6gdU4RFFRh9ZkV++fwry/QU5Pace4wN6HLEJZXpjuo4eLblInk0C6TqHFCnQ8/cNgXPfjvoc9qjSDMndT2N2Gbvhr3/RqLTOQQ2LB9JBf9snts/cBFmSwKli22ELPEQdunsSTIfSJikKjtwazsVabGQCuKrfk0aO4quRo3DkyJ54z1GTyfaMpyX36iNfNlozsNitFRSfl86rLp29G+ZAAR29dIJ37YLTjyzMBSQbCZ+iPL8o4/gdRdk27jO0woN97CSm/kNrm5Dry/htmlsUDhPYhvj2zc1l5yxvLjVT9sUlinFoXlwu/EufOOAJJNPh6xvGejs1xTrghtx+yz5L2ok/Kjb/lUkPr0dJj6/fKOl62P1ZYM27f2TCu78= \ No newline at end of file diff --git a/docs/design-documents/features/storage/KVStore/KVStore_design.md b/docs/design-documents/features/storage/KVStore/KVStore_design.md new file mode 100644 index 00000000000..91718490a08 --- /dev/null +++ b/docs/design-documents/features/storage/KVStore/KVStore_design.md @@ -0,0 +1,463 @@ +# KVStore in Mbed OS + +- [KVStore in Mbed OS](#kvstore-in-mbed-os) + + [Revision history](#revision-history) +- [Introduction](#introduction) + + [Overview and background](#overview-and-background) + + [Requirements and assumptions](#requirements-and-assumptions) +- [System architecture and high-level design](#system-architecture-and-high-level-design) + * [Design basics](#design-basics) + + [Derived implementations](#derived-implementations) + * [Global Key Value interface](#global-key-value-interface) +- [Detailed design](#detailed-design) + * [KVStore class design](#kvstore-class-design) + + [KVStore Class header](#kvstore-class-header) + * [Global Key Value interface design](#global-key-value-interface-design) + + [Global Key Value APIs](#global-key-value-apis) + * [Mapping APIs](#mapping-apis) + * [Implementation](#implementation) + + [Important data structures](#important-data-structures) + + [Global Key Value API implementation](#global-key-value-api-implementation) + + [Attachment API implementation](#attachment-api-implementation) +- [Usage scenarios and examples](#usage-scenarios-and-examples) + + [Standard usage of the KVStore class](#standard-usage-of-the-kvstore-class) + + [Standard usage of the Global Key Value interface](#standard-usage-of-the-global-key-value-interface) +- [Other information](#other-information) + + [Open issues](#open-issues) + + +### Revision history + +| Revision | Date | Authors | Mbed OS version | Comments | +|---------- |---------------- |-------------------------------------------------------- |----------------- |------------------ | +| 1.0 | 26 September 2018 | David Saada ([@davidsaada](https://github.com/davidsaada/)) | 5.11+ | Initial revision | + +# Introduction + +### Overview and background + +KVStore is an interface class whose purpose is to define APIs for a Key Value Store like storage over a block device. + +### Requirements and assumptions + +# System architecture and high-level design + +## Design basics + +KVStore defines a simple, key value store like API set via this interface class. Classes implementing this interface store pairs of keys and values, where the keys are represented as character strings and the values are represented as binary blobs. Core APIs here are *get* and *set*, providing read and write access by key to the value in a single call. *remove* completes the set of core APIs. This simplifies the interface for the cases we need an actual key value store (like configurations). +APIs also support an "incremental set" mode, allowing the implementing class to aggregate chunks of data for the set operation. This is for the case the caller needs to generate large portions of data, but doesn't wish to allocate large buffers for a single set operation. Note that *get* API doesn't have or require this functionality. Instead, it has an offset parameter (defaulting to 0) allowing the calling layer to extract portions of the data. +Interface also includes iteration APIs, to let the user iterate over all available keys, given a prefix. +As some of the implementations use files as keys, key names must comply to file naming rules, meaning that characters like * , / etc. are not allowed in key names. + +### Derived implementations +![KVStore Classes](./KVStore_classes.jpg) + +KVStore currently has a few derived implementations. +- [TDBStore](../TDBStore/TDBStore_design.md) should be chosen as the default solution, as it gives the best performance, flash wear leveling and lowest overhead for a limited number of keys. +- [FileSystemStore](../FileSystemStore/FileSystemStore_design.md) is the preferred solution, if we already have a file system and don't wish to have an additional one, or if specific POSIX features (like file seeking) are required. It's also preferred if we don't have a limitaion for the number of keys. +- [SecureStore](../SecureStore/SecureStore_design.md) adds security features such as encryption, rollback protection and authentication. It uses one of the other KVStore solutions as the underlying storage type. + +## Global Key Value interface + +A parallel key-value API is provided as global C-style functions (for all functions, except for the incremental set ones). This API performs a limited type of mapping of partition or mount point names present in the keys. For each of the APIs defined in KVStore, the global version will extract a partition prefix from the key name. The prefix must be in the form "/partition/key-name". Then a lookup will be performed to map the partition name to a concrete KVStore instance, and the API call routed to that instance. The routed key name will have the partition prefix stripped, leaving only "key-name". +In the case of iteration APIs, the prefix must include the partition (in the form of "/partition/prefix"). + + +# Detailed design + +## KVStore class design + +As an interface class, KVStore has no implementation, just a class header. + +### KVStore Class header + +```C++ +class KVStore { + + enum create_flags { + WRITE_ONCE_FLAG = (1 << 0), + ENCRYPT_FLAG = (1 << 1), + AUTHENTICATE_FLAG = (1 << 2), + ROLLBACK_PROTECT_FLAG = (1 << 3), + }; + + static const uint32_t MAX_KEY_LENGTH = 128; + + typedef struct _opaque_set_handle *set_handle_t; + + typedef struct _opaque_key_iterator *iterator_t; + + typedef struct info { + size_t size; + uint32_t flags; + } info_t; + + // Initialization and reset + virtual int init(); + virtual int deinit(); + virtual int reset(); + + // Core API + virtual int set(const char *key, const void *buffer, size_t size, uint32_t create_flags); + virtual int get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size = NULL, size_t offset = 0); + virtual int get_info(const char *key, info_t *info); + virtual int remove(const char *key); + + // Incremental set API + virtual int set_start(set_handle_t *handle, const char *key, size_t final_data_size, uint32_t create_flags); + virtual int set_add_data(set_handle_t handle, const void *value_data, size_t data_size); + virtual int set_finalize(set_handle_t handle); + + // Key iterator + virtual int iterator_open(iterator_t *it, const char *prefix = NULL); + virtual int iterator_next(iterator_t it, char *key, size_t key_size); + virtual int iterator_close(iterator_t it); +} +``` + +## Global Key Value interface design + +As mentioned above, each KVStore API has a parallel C-style API, used globally with a partition name preceding the key name. + +### Global Key Value APIs + +```C +enum kv_create_flags { + KV_WRITE_ONCE_FLAG = (1 << 0), + KV_ENCRYPT_FLAG = (1 << 1), + KV_AUTHENTICATE_FLAG = (1 << 2), + KV_ROLLBACK_PROTECT_FLAG = (1 << 3), +}; + +static const uint32_t KV_MAX_KEY_LENGTH = 128; +typedef struct _opaque_set_handle *kv_set_handle_t; +typedef struct _opaque_key_iterator *kv_key_iterator_handle_t; + +typedef struct info { + size_t size; + uint32_t flags; +} kv_info_t; + +// Core API +int kv_set(const char *full_name_key, const void *buffer, size_t size, uint32_t create_flags); +int kv_get(const char *full_name_key, void *buffer, size_t buffer_size, size_t *actual_size); +int kv_get_info(const char *full_name_key, kv_info_t *info); +int kv_remove(const char *full_name_key); + +// Key iterator +int kv_iterator_open(kv_key_iterator_handle_t *it, const char *full_prefix = nullptr); +int kv_iterator_next(kv_key_iterator_handle_t it, char *key, size_t key_size); +int kv_iterator_close(kv_key_iterator_handle_t it); +``` + +## Mapping APIs + +In order to use the global C style APIs, we need APIs to map the partition name to the instance of the implementing KVStore class, typically called once at initialization time. So, for example a "/tdbstore/key1" name means that we wish to access "key1" key in a TDBStore instance. This means that we will need to attach "tdbstore" string to the TDBStore instance at initialization time. +These APIs will be part of a different header file ("kv_map.h"), as they serve the integration code and not the KVStore user code. +APIs are the following: + +```C +// Attach and detach +int kv_init(); +int kv_attach(const char *partition_name, KVStore *kv_instance); +int kv_detach(const char *partition_name); + +// Full name lookup and then break it into KVStore instance and key +int kv_lookup(const char *full_name, KVStore& *kv_instance, char *key); +``` + +## Implementation + +Following is the implementation of the Global Key Value interface and of the attachment APIs. KVStore class has no implemetation as it's an interface class. + +### Important data structures + +```C +// incremental set handle +typedef struct { + KVStore *kvstore_intance; + KVStore::set_handle_t *set_handle; +} kv_inc_set_handle_t; + +// iterator handle +typedef struct { + KVStore *kvstore_intance; + KVStore::iterator_t *iterator_handle; +} kv_key_iterator_handle_t; + +const int MAX_ATTACHED_KVS 16 + +typedef struct { + char *partition_name; + KVStore *kvstore_instance; +} kv_map_entry_t; + +// Attachment table +kv_map_entry_t kv_map_table[MAX_ATTACHED_KVS]; +int kv_num_attached_kvs; +``` + +### Global Key Value API implementation + +**kv_set function** + +Header: +`int kv_set(const char *full_name_key, const void *buffer, size_t size, uint32_t create_flags);` + +Pseudo code: +- Using `kv_lookup`, break `full_name_key` into `key` and `kvs_instance` +- Call `kvs_instance` `set` method with `key` and the rest of the arguments + +**kv_get function** + +Header: +`int kv_get(const char *full_name_key, void *buffer, size_t buffer_size, size_t *actual_size);` + +Pseudo code: +- Using `kv_lookup`, break `full_name_key` into `key` and `kvs_instance` +- Call `kvs_instance` `get` method with `key` and the rest of the arguments + +**kv_get_info function** + +Header: +`int kv_get_info(const char *full_name_key, kv_info_t *info);` + +Pseudo code: +- Using `kv_lookup`, break `full_name_key` into `key` and `kvs_instance` +- Call `kvs_instance` `get_info` method with `key` and the rest of the arguments + +**kv_remove function** + +Header: +`int kv_remove(const char *full_name_key);` + +Pseudo code: +- Using `kv_lookup`, break `full_name_key` into `key` and `kvs_instance` +- Call `kvs_instance` `remove` method with `key` and the rest of the arguments + +**kv_set_start function** + +Header: +`int kv_set_start(kv_set_handle_t *handle, const char *full_name_key, size_t final_data_size);` + +Pseudo code: +- Allocate an `kv_inc_set_handle_t` structure into `handle` +- Using `kv_lookup`, break `full_name_key` into allocated `key` and `kvs_instance` (in `handle`) +- Call `kvs_instance` `set_start` method with `key` and the rest of the arguments + +**kv_set_add_data function** + +Header: +`int kv_set_add_data(kv_set_handle_t handle, const void *value_data, size_t data_size);` + +Pseudo code: +- Extract `kvs_instance` and `set_handle` from `handle` +- Call `kvs_instance` `set_add_data` method with `set_handle` and the rest of the arguments + +**kv_set_finalize function** + +Header: +`int kv_set_finalize(kv_set_handle_t handle);` + +Pseudo code: +- Extract `kvs_instance` and `set_handle` from `handle` +- Call `kvs_instance` `set_finalize` method with `set_handle` +- Free `key` and `handle` + +**kv_iterator_open function** + +Header: +`int kv_iterator_open(kv_key_iterator_handle_t *it, const char *full_prefix = nullptr);` + +Pseudo code: +- Allocate a `kv_key_iterator_handle_t` structure into `it` +- Using `kv_lookup`, break `full_name_key` into allocated `prefix` and `kvs_instance` (in `handle`) +- Call `kvs_instance` `iterator_open` method with `iterator_handle`, `prefix` and the rest of the arguments + +**kv_iterator_next function** + +Header: +`int kv_iterator_next(kv_key_iterator_handle_t it, char *key, size_t key_size);` + +Pseudo code: +- Extract `kvs_instance` and `iterator_handle` from `handle` +- Call `kvs_instance` `iterator_next` method with `iterator_handle` and the rest of the arguments + +**kv_iterator_close function** + +Header: +`int kv_iterator_close(kv_key_iterator_handle_t it);` + +Pseudo code: +- Extract `kvs_instance` and `iterator_handle` from `handle` +- Call `kvs_instance` `set_finalize` method with `iterator_handle` +- Free `prefix` and `handle` + +### Attachment API implementation + +**kv_init function** + +Header: +`int kv_init();` + +Pseudo code: +- Set `kv_num_attached_kvs` to 0 + +**kv_attach function** + +Header: +`int kv_attach(const char *partition_name, KVStore *kv_instance);` + +Pseudo code: +- Duplicate `partition_name` and `kv_instance` to last entry in `kv_map_table` +- Increment `kv_num_attached_kvs` + +**kv_detach function** + +Header: +`int kv_detach(const char *partition_name);` + +Pseudo code: +- Look for entry with `partition_name` in `kv_map_table` +- Deallocate `partition_name` in this entry +- Copy all preceding entries back one position +- Decrement `kv_num_attached_kvs` + +**kv_lookup function** + +Header: +`int kv_lookup(const char *full_name, KVStore& *kv_instance, char *key);` + +Pseudo code: +- Break `full_name` string to `partition_name` and `key` +- Look for entry with `partition_name` in `kv_map_table` +- Extract `kv_instance` from table entry + +# Usage scenarios and examples + +### Standard usage of the KVStore class + +Following example code shows standard usage of the KVStore, using the TDBStore class + +**Standard usage example - with class APIs** + +```C++ +// Underlying block device. Here, SPI Flash is fully used. +// One can use SlicingBlockDevice if we want a partition. +SPIFBlockDevice bd(PTE2, PTE4, PTE1, PTE5); + +// Instantiate tdbstore with our block device and a maximum of 64 keys +kvstore = new TDBStore(64, &bd); + +int res; + +// Initialize storage +res = kvstore->init(); + +const char *val1 = "Value of key 1"; +const char *val2 = "Updated value of key 1"; +// Add "Key1" +res = kvstore->set("Key1", val1, sizeof(val1), 0); +// Update value of "Key1" +res = kvstore->set("Key1", val2, sizeof(val2), 0); + +uint_8 value[32]; +size_t actual_size; +// Get value of "Key1". Value should return the updated value. +res = kvstore->get("Key1", value, sizeof(value), &actual_size); + +// Remove "Key1" +res = kvstore->remove("Key1"); + +// Incremental write, if need to generate large data with a small buffer +const int data_size = 1024; +char buf[8]; + +KVSTore::set_handle_t handle; +res = kvstore->set_start(&handle, "Key2", data_size, 0); +for (int i = 0; i < data_size / sizeof(buf); i++) { + memset(buf, i, sizeof(buf)); + res = kvstore->set_add_data(handle, buf, sizeof(buf)); +} +res = kvstore->set_finalize(handle); + +// Iterate over all keys starting with "Key" +res = 0; +KVSTore::iterator_t it; +kvstore->iterator_open(&it, "Key*"); +char key[KVSTore::KV_MAX_KEY_LENGTH]; +while (!res) { + res = kvstore->iterator_next(&it, key, sizeof(key)); +} + +// Deinitialize TDBStore +res = kvstore->deinit(); +``` + +### Standard usage of the Global Key Value interface + +Following example code shows how to use the previous example with the global key value interface. Here, `tdtbstore` will be mapped to "/tdbstore/". + +**Standard usage example - with global C-style APIs** + +Assuming this code exists somewhere and called during initialization + +```C +// Assume TDBtore is already instantiated and initialized +extern TDBStore tdbstore; + +int res; + +// Attachment code. Should be called at initialization +res = kv_init(); +res = kv_attach("tdbstore", &tdbstore); + +``` + +Here's how to access kvstore via C global APIs + + +```C +const char *val1 = "Value of key 1"; +const char *val2 = "Updated value of key 1"; + +// Add "Key1", now with full name, including "/tdbstore/" prefix. +res = kv_set("/tdbstore/Key1", val1, sizeof(val1), 0); +// Update value of "Key1" +res = kv_set("/tdbstore/Key1", val2, sizeof(val2), 0); + +uint_8 value[32]; +size_t actual_size; +// Get value of "Key1". Value should return the updated value. +res = kv_get("/tdbstore/Key1", value, sizeof(value), &actual_size); + +// Remove "Key1" +res = kv_remove("/tdbstore/Key1"); + +// Incremental write, if need to generate large data with a small buffer +const int data_size = 1024; +char buf[8]; + +kv_set_handle_t handle; +res = kv_set_start(&handle, "/tdbstore/Key2", data_size, 0); +for (int i = 0; i < data_size / sizeof(buf); i++) { + memset(buf, i, sizeof(buf)); + res = kv_set_add_data(handle, buf, sizeof(buf)); +} +res = kv_set_finalize(handle); + +// Iterate over all keys starting with "Key" in tdbstore +res = 0; +kv_key_iterator_handle_t it; +kv_iterator_open(&it, "/tdbstore/Key"); +char key[KV_MAX_KEY_LENGTH]; +while (!res) { + res = kv_iterator_next(&it, key, sizeof(key)); +} +res = kv_iterator_close(&it); +``` + +# Other information + +### Open issues + diff --git a/docs/design-documents/features/storage/SecureStore/SecureStore_class_hierarchy.jpg b/docs/design-documents/features/storage/SecureStore/SecureStore_class_hierarchy.jpg new file mode 100644 index 0000000000000000000000000000000000000000..19047d5e665a27b7606d81fed3c8255872a3b2e0 GIT binary patch literal 19220 zcmeHv2UJtry6!?$no3bbx(d?Ema0@mrEYrfDk207NDoqifHXmrszHd-d!#E>sRAO> zCDa5#ItgM3kn)yipLfr_=WMoR0i}}wv|M`FO`@b3LN9ruVqN}N+ z3DD3001fy8s1v{?fOg-vFR{D=3~j zuXO416-_N|9bHp1a|=r=Ya6E<&Np3LA#Ogte*OWlz&jC<_oAX>V&jsMQ&Q8?A3s54 z=j7()7Zes1S5{Tm)YiRxRo~jy-qG3hw!3F=Xn16F?Bh5ZgPr>{zwmi+X&JxyW$P

IgtvfpM{`0ujp-wgYQU1;DCEe+Utw5)(S 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/fcTK5QVBp6bISPED8zXg8/jyHieNN4+2T5mn0RQUgHeYGW8ebOYz1BgOGPxbZ5cjdqJ8DoRYBKVXAXPwGAl1C1yKAVUPRKCWNSJugr5IEfNPAuNbqvam2VLK5aspD6ABzn8su+l0EJsrR0cCt8GcQYVSs3HNJsuD+W6jVOqH1HOYts08ujnlhi/RXEQ/Uew3yHhxvqpUy+VO8nYK03Ba05fMeD0hLvzUk5qQJ5MeGyzUULg8lzp2k1j2zI0qGv9bWp0nMdSgSxxuj1E23+I1gtjOL3xiV5rJ+TWZga264FCHN89E50JVNfArpN1t5UQBz8Nca5kZpKGS4lUVbH7G0jUXaul8EVrFS7/Cigdjs3XBE52wdZ3ZThwygF5mNAmUNc2wD2giMsHHOwyxjakKszCQs7SyFWkuZBcRS4EF5k6VKDOVHj9H4kcdC2sx6BrkBa9X6aWJplcq160FAcWF9gG0NoqB4AhWD0TtUISnzKDAogVmfxu9VOvQKLKqlwogwThkYlqarKMQHCsQDQdnrBOXXFXQYhSQY21zHkUoBWZwgUucq4KsIAiLlFH4gaJSFLjv13Q9ppxokN2LTrBz7tk/mXpXAZUumvbsW020GV2qtfaBZ9VRuGRq4RwwZTFEwHUPZaZR7PO2A2CVXjZdvRyvGQl9ryNlrSCcgP7WG9P/zEL0G5Nmb2ugz43GwJx4PN7SFVP5bu4Wh/DEjcOZ2m12NKmRI735YtdtBMfx5jMa8OzST55/0w0NV46P9sD9sGfLO1w/vL7nYzGAjfHiB3bUjXlhH9O4/sQIVfznPdauuqk695ri3fVYWoVfQAv3GqM21TitG95dzOS+L04cv56OWob92Ocdh9bogV6/eyXgPfwA= \ No newline at end of file diff --git a/docs/design-documents/features/storage/SecureStore/SecureStore_design.md b/docs/design-documents/features/storage/SecureStore/SecureStore_design.md new file mode 100644 index 00000000000..b7fba2049c5 --- /dev/null +++ b/docs/design-documents/features/storage/SecureStore/SecureStore_design.md @@ -0,0 +1,448 @@ +# SecureStore in Mbed OS + +- [SecureStore in Mbed OS](#securestore-in-mbed-os) + + [Revision history](#revision-history) +- [Introduction](#introduction) + + [Overview and background](#overview-and-background) + + [Requirements and assumptions](#requirements-and-assumptions) +- [System architecture and high-level design](#system-architecture-and-high-level-design) + * [Design basics](#design-basics) + + [Data layout](#data-layout) + + [Basic implementation concepts](#basic-implementation-concepts) +- [Detailed design](#detailed-design) + + [Class header](#class-header) + + [Important data structures](#important-data-structures) + + [Initialization and reset](#initialization-and-reset) + + [Core APIs](#core-apis) + + [Incremental set APIs](#incremental-set-apis) + + [Key iterator APIs](#key-iterator-apis) +- [Usage scenarios and examples](#usage-scenarios-and-examples) + + [Standard usage of the class](#standard-usage-of-the-class) +- [Other information](#other-information) + + [Open issues](#open-issues) + + +### Revision history + +| Revision | Date | Authors | Mbed OS version | Comments | +|---------- |---------------- |-------------------------------------------------------- |----------------- |------------------ | +| 1.0 | 02 October 2018 | David Saada ([@davidsaada](https://github.com/davidsaada/)) | 5.11+ | Initial revision | + +# Introduction + +### Overview and background + +SecureStore is a [KVStore](../KVStore/KVStore_design.md) based storage solution, providing security features on the stored data, such as encryption, authentication, rollback protection and write once, over an underlying KVStore class. It references an additional KVStore class for storing the rollback protection keys. + +### Requirements and assumptions + +SecureStore assumes that the underlying KVStore instances are instantiated and initialized. + +# System architecture and high-level design + +## Design basics + +SecureStore is a storage class, derived from KVStore. It adds security features to the underlying key value store. + +As such, it offers all KVStore APIs, with additional security options (which can be selected using the creation flags at set). These include: +- Encryption: Data is encrypted using the AES-CTR encryption method, with a randomly generated 8-byte IV. Key is derived from [Device Key](../../../../../../mbed-os/features/device_key/README.md), using the NIST SP 800-108 KDF in counter mode spec, where salt is the key trimmed to 32 bytes, with "ENC" as prefix. +- Authentication: A 16-byte CMAC is calculated on all stored data (including metadata) and stored at the end of the record. When reading the record, calculated CMAC is compared with the stored one. In case of encryption, CMAC is calculated on the encrypted data. The key used for generating the CMAC is derived from [Device Key](../../../../../../mbed-os/features/device_key/README.md), where salt is the key trimmed to 32 bytes, with "AUTH" as prefix. +- Rollback protection: (Requires authentication) CMAC is stored in a designated rollback protected storage (also of KVStore type) and compared to when reading the data under the same KVStore key. A missing or different key in the rollback protected storage would result in an error. +- Write once: Key can only be stored once and can't be removed. + + +![SecureStore Layers](./SecureStore_layers.jpg) + + +### Data layout + +When storing the data, it is stored with a preceding metadata header. Metadata includes flags and security related parameters such as IV. The CMAC, calculated for authentication, is stored at the end of the data as it is calculated on the fly, so it can't be stored with the metadata. + +![SecureStore Record](./SecureStore_record.jpg) + +Fields are: +- Metadata size: Size of metadata header +- Revision: SecureStore revision (currently 1) +- Data size: Size of user data +- Flags: User flags +- IV: Random generated IV +- Pad: Pad data to a multiple of 16 bytes (due to encryption) +- CMAC: CMAC calculated on key, metadata & data + +### Basic implementation concepts + +When setting the data, as the code can't construct a single buffer to store all data (including metadata and possibly encrypted data) in one shot, it will be done in chunks, using the incremental set APIs. As for get, it will use the offset argument to extract metadata, data & CMAC separately. +Rollback protection (RBP) keys are stored in the designated rollback protection storage, of KVStore type also. RBP keys are the same as the SecureStore keys. + + +# Detailed design + +![SecureStore Class Hierarchy](./SecureStore_class_hierarchy.jpg) + +Functionality, as defined by KVStore, includes the following: +- Initialization & reset +- Core actions: get, set & remove +- Incremental set actions +- Iterator actions + +### Class header + +SecureStore has the following header: + +```C++ +class SecureStore : KVStore { + +public: + SecureStore(KVStore *underlying_kv, KVStore *rbp_kv); + virtual ~SecureStore(); + + // Initialization and formatting + virtual int init(); + virtual int deinit(); + virtual int reset(); + + // Core API + virtual int set(const char *key, const void *buffer, size_t size, uint32_t create_flags); + virtual int get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size = NULL, size_t offset = 0); + virtual int get_info(const char *key, info_t *info); + virtual int remove(const char *key); + + // Incremental set API + virtual int set_start(set_handle_t *handle, const char *key, size_t final_data_size, uint32_t create_flags); + virtual int set_add_data(set_handle_t handle, const void *value_data, size_t data_size); + virtual int set_finalize(set_handle_t handle); + + // Key iterator + virtual int iterator_open(iterator_t *it, const char *prefix = NULL); + virtual int iterator_next(iterator_t it, char *key, size_t key_size); + virtual int iterator_close(iterator_t it); + +private: + Mutex _mutex; + KVStore *_underlying_kv; + KVStore *_rbp_kv; + void *_entropy; + uint8_t *_scratch_buf; +} +``` + +### Important data structures + +```C++ +// Record header +typedef struct { + uint16_t metadata_size; + uint16_t revision; + uint32_t data_size; + uint32_t create_flags; + uint8_t iv[8]; +} record_metadata_t; + +// incremental set handle +typedef struct { + record_metadata_t metadata; + bd_size_t offset; + char *key; + void *encrypt_handle; + void *auth_handle; + KVStore::set_handle_t underlying_handle; +} inc_set_handle_t; + +// iterator handle +typedef struct { + KVStore::iterator_t underlying_it; +} key_iterator_handle_t; +``` + + +### Initialization and reset + +**init function** + +Header: +`virtual int init();` + +Pseudo code: +- if `_is_initialized` return OK +- Take `_mutex` +- Initialize `_entropy` with TLS entropy APIs +- Using `DeviceKey` APIs, get the device key +- Allocate `_scratch_buf` as a 32 byte array +- Set `_is_initialized` to true +- Release `_mutex` + +**deinit function** + +Header: +`virtual int deinit();` + +Pseudo code: +- if not `_is_initialized` return OK +- Take `_mutex` +- Deinitialize `_entropy` +- Deallocate `_scratch_buf` +- Release `_mutex` + +**reset function** + +Header: +`virtual int reset();` + +Pseudo code: +- Take `_mutex` +- Call `_underlying_kv` `reset` API +- Call `_rbp_kv` `reset` API +- Release `_mutex` + +### Core APIs + +**set function** + +Header: +`virtual int set(const char *key, const void *buffer, size_t size, uint32_t create_flags);` + +Pseudo code: +- Call `set_start` with all fields and a local `set_handle_t` variable +- Call `set_add_data` with `buffer` and `size` +- Call `set_finalize` +- Return OK + +**get function** + +Header: +`virtual int get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size = NULL, size_t offset = 0);` + +Pseudo code: +- if not `_is_initialized` return error +- Take `_mutex` +- Call `_underlying_kv` `get` API with `metadata` size into a `metadata` local structure +- If failure + - If rollback protection flag set + - Call `_rbp_kv` `get` API on a local `rbp_cmac` variable, key is `key`, size 16 + - If no error, return "RBP authentication" error + - Return "Key not found error" +- If authentication flag set + - Derive a key from device key and `key` + - Allocate and initialize `auth_handle` CMAC calculation local handle with derived key + - Using `auth_handle` handle, calculate CMAC on `key` & `metadata` +- If encrypt flag set + - Derive a key from device key and `key` + - Allocate and initialize a local `enc_handle` AES-CTR local handle with derived key and `iv` field +- Set `data_size` local variable to data size in metadata +- Set `actual_size` to the minimum of `buffer_size` and `data_size` +- Set `current_offset` to 0 +- While `data_size` > 0 + - If `current_offset` between `offset` and `actual_size` + - Set `dest_buf` to `buffer` and `chunk_size` to `actual_size` + - Else + - Set `dest_buf` to `_scratch_buf` and `chunk_size` to `actual_size` + - Call `_underlying_kv` `get` API with `dest_buf` and `chunk_size` + - If authentication flag set, calculate CMAC on `dest_buf`, using `_auth_handle` handle + - If encrypt flag set, decrypt `dest_buf` (in place) using `_enc_handle` handle + - Decrement `data_size` by `chunk_size` +- Call `_underlying_kv` `get` API with on a local `read_cmac` variable, size 16 +- Generate CMAC on local `cmac` variable +- Using `mbedtls_ssl_safer_memcmp` function, compare `read_cmac` with `cmac`. Return "data corrupt error" if no match. +- If rollback protection flag set + - Call `_rbp_kv` `get` API on a local `rbp_cmac` variable, key is `key`, size 16 + - If `rbp_cmac` doesn't match `cmac`, clear `buffer` and return "RBP authentication" error +- Deinitialize and free `auth_handle` and `enc_handle` +- Release `_mutex` +- Return OK + +**get_info function** + +Header: +`virtual int get_info(const char *key, info_t *info);` + +Pseudo code: +- if not `_is_initialized` return error +- Call `get` API with `key` and 0 in `buffer_size` parameter +- If failed, return error code +- Call `_underlying_kv` `get` API with `metadata` size and `key` +- Fill fields in `info` according to `metadata` +- Return OK + +**remove function** + +Header: +`virtual int remove(const char *key);` + +Pseudo code: +- if not `_is_initialized` return error +- Take `_mutex` +- Call `_underlying_kv` `get` API with `metadata` size and `key` +- If not found, return "Not found" error +- If write once flag set, return "Already exists" error +- Call `_underlying_kv` `remove` API with `key` +- If rollback protect flag set, Call `_rbp_kv` `remove` API with `key` as key +- Return OK + +### Incremental set APIs + +**set_start function** + +Header: +`virtual int set_start(set_handle_t *handle, const char *key, size_t final_data_size, uint32_t create_flags);` + +Pseudo code: +- Take `_mutex` +- Allocate an `inc_set_handle_t` and assign in handle +- if flags include write once flag + - Call `_underlying_kv` `get_info` API + - If key exists, return "already exists" error + - Call `_rbp_kv` `get` API with `key` as key. If key exists, return "already exists" error +- If encrypt flag set + - Derive a key from device key and `key` as salt (trimmed to 32 bytes with "ENC" as prefix) + - Using TLS entropy function on `_entropy` handle, randomly generate `iv` field + - Allocate and initialize `enc_handle` AES-CTR handle field with derived key and `iv` field +- Fill all available fields in `metadata` +- If authentication flag set + - Derive a key from device key and `key` as salt (trimmed to 32 bytes with "AUTH" as prefix) + - Allocate and initialize `auth_handle` CMAC calculation handle field with derived key + - Using `auth_handle` handle, calculate CMAC on `key` & `metadata` +- Call `_underlying_kv` `set_start` API +- Call `_underlying_kv` `set_add_data` API with `metadata` field +- Return OK + +**set_add_data function** + +Header: +`virtual int set_add_data(set_handle_t handle, const void *value_data, size_t data_size);` + +Pseudo code: + +- If `offset` + `data_size` > data size in handle, return error +- If flags include encryption + - Iterate over `value_data` field in chunks of `_scratch_buf` size + - Using `enc_handle` handle field, encrypt chunk into `_scratch_buf` + - If authentication flag set, using `auth_handle` handle field, update CMAC of `_scratch_buf` + - Call `_underlying_kv` `set_add_data` API with `_scratch_buf` +- Else + - If authentication flag set, using `auth_handle` handle field, update CMAC of `value_data` + - Call `_underlying_kv` `set_add_data` API with `value_data` +- Update `offset` field in handle +- Return OK + +**set_finalize function** + +Header: +`virtual int set_finalize(set_handle_t handle);` + +Pseudo code: + +- Initialize a local `cmac` 16-byte array to 0. +- If authentication flag set, using `auth_handle` handle field, generate `cmac` +- Call `_underlying_kv` `set_add_data` API with `cmac` +- Call `_underlying_kv` `set_finalize` +- If rollback protect flag set, Call `_rbp_kv` `set` API with `key` as key and `cmac` as data +- Deinitialize and free `auth_handle` and `enc_handle` +- Free `handle` +- Release `_mutex` +- Return OK + +### Key iterator APIs + +**iterator_open function** + +Header: +`virtual int iterator_open(iterator_t *it, const char *prefix = NULL);` + +Pseudo code: +- Allocate a `key_iterator_handle_t` structure into `it` +- Take `_mutex` +- Call `_underlying_kv` `iterator_open` with `underlying_it` field +- Release `_mutex` +- Return OK + +**iterator_next function** + +Header: +`virtual int iterator_next(iterator_t it, char *key, size_t key_size);` + +Pseudo code: +- Take `_mutex` +- Call `_underlying_kv` `iterator_next` with `underlying_it` field +- Release `_mutex` +- Return OK + +**iterator_close function** + +Header: +`virtual int iterator_close(iterator_t it);` + +Pseudo code: +- Take `_mutex` +- Call `_underlying_kv` `iterator_close` with `underlying_it` field +- Release `_mutex` +- Deallocate `it` +- Return OK + + +# Usage scenarios and examples + +### Standard usage of the class + +Following example code shows standard usage of the SecureStore class + +**Standard usage example** + +```C++ +// Underlying key value store - here TDBStore (should be instantiated and initialized) +extern TDBStore tdbstore; + +// Rollback protect store - also of TDBStore type (should be instantiated and initialized) +extern TDBStore rbp_tdbstore; + +// Instantiate SecureStore with tdbstore as underlying key value store and rbp_tdbstore as RBP storage +SecureStore secure_store(&tdbstore, &rbp_tdbstore); + +int res; + +// Initialize secure_store +res = secure_store.init(); + +const char *val1 = "Value of key 1"; +const char *val2 = "Updated value of key 1"; +// Add "Key1" with encryption and authentication flags +res = secure_store.set("Key1", val1, sizeof(val1), KVSTore::ENCRYPT_FLAG | KVSTore::AUTHENTICATE_FLAG); +// Update value of "Key1" (flags must be the same per key) +res = secure_store.set("Key1", val2, sizeof(val2), KVSTore::ENCRYPT_FLAG | KVSTore::AUTHENTICATE_FLAG); + +uint_8 value[32]; +size_t actual_size; +// Get value of "Key1". Value should return the updated value. +res = secure_store.get("Key1", value, sizeof(value), &actual_size); + +// Remove "Key1" +res = secure_store.remove("Key1"); + +// Incremental write, if need to generate large data with a small buffer +const int data_size = 1024; +char buf[8]; + +KVSTore::set_handle_t handle; +res = secure_store.set_start(&handle, "Key2", data_size, 0); +for (int i = 0; i < data_size / sizeof(buf); i++) { + memset(buf, i, sizeof(buf)); + res = secure_store.set_add_data(handle, buf, sizeof(buf)); +} +res = secure_store.set_finalize(handle); + +// Iterate over all keys starting with "Key" +res = 0; +KVSTore::iterator_t it; +secure_store.iterator_open(&it, "Key*"); +char key[KVSTore::KV_MAX_KEY_LENGTH]; +while (!res) { + res = secure_store.iterator_next(&it, key, sizeof(key)e); +} +res = secure_store.iterator_close(&it); + +// Deinitialize SecureStore +res = secure_store.deinit(); +``` +# Other information + +### Open issues +- Need to figure a way to prevent mutex abuse in incremental set APIs. diff --git a/docs/design-documents/features/storage/SecureStore/SecureStore_layers.jpg b/docs/design-documents/features/storage/SecureStore/SecureStore_layers.jpg new file mode 100644 index 0000000000000000000000000000000000000000..75b2c80066542a1ada35cefc827f177e9e2ec1f9 GIT binary patch literal 44929 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 literal 0 HcmV?d00001 diff --git a/docs/design-documents/features/storage/SecureStore/SecureStore_layers.xml b/docs/design-documents/features/storage/SecureStore/SecureStore_layers.xml new file mode 100644 index 00000000000..e4789c966be --- /dev/null +++ b/docs/design-documents/features/storage/SecureStore/SecureStore_layers.xml @@ -0,0 +1 @@ +7VlZc9owEP41zLQP6WD5iPMYkh4znc50Ss9HIS+2JsKiQlz99ZWwZLAtBxoMpUdeYq3O3e/bQ6jn301WrwWeZu94AqyH+smq59/3EPLCEKl/WrIuJNdxUAhSQRMzaCsY0h9ghH0jndMEZpWBknMm6bQqJDzPgciKDAvBl9VhY86qu05xCg3BkGDWlH6hicwKaRz2t/I3QNPM7uz1Tc8Ik4dU8Hlu9ushf7z5K7on2K5lxs8ynPDljsh/2fPvBOey+Jqs7oBp21qzFfNetfSW5xaQy0MmRNfFjAVmc7BHjpiaO0joQh9Qro1Rou9zfarBmOfyaraB7FYN8ILpatupvlLzn9nBx68ysoKhlSiNRq2jgMwFDCUXUHaJ+mDXAkpWnLch3tjCSlFFIbRBG7Q9+6p7mVEJwykmunepvEPJMjlhquVptSljd5xxsZmruYEIUfKZFPwBdnqSaBSFUbnfLqIG5AUICasdkUH4NfAJSLFWQ2xvbNi2LulatJdbbgeWwdkOry3fsXGntFx6Syn1YVjVQsl+3Eoxyw7r/xZ/71EqPJVNryiD4XomYVLhxkFM6BDzBEM8dmIekRhG424wD4Ia5tdNzL3gZJjf7MW8o4jw8X7wD6CJbg5A03egWSamo+C08eKCXPh34T2OCbgj9igOg7B/Gu8t64WzeK/ntcJ9iUXBgHHycA8LSi4+3ychxEngYk+MRn7UUb5vsOessd9DR7PH74Q9j65SkuHt51/PH26edKhj4wjHKf3sNl8/36PNwS5zMt84SyZt1MIu33DVwh1lUt/hHDUjQp7c6jusauU8B80pPMs2VvWqFlRmEOuv2tovQtv8ZvpabQVJ7e4rsUjBjkGB23w75gkfiRwCGJZ0Ud3AZTGzw3tON8S2kcuvouOjmtFnfC4ImFm799o9C5Uw24UKpRsLbQAs1T4Q0z/rDv0/Xe4prp3p0lVcd5IuUXtt/Z89fwB76gnFyZ6T/biCXKV6zYazDE/1J5kLth4ITB50tN9nzK3li5ZUcZ3nqnl109UlB4XVGH0TNiyHHIYr/fU4ywWtfnfeOy0Xx9xlla1lFbgq200FsesaRoQZTTWeRCEISj7QyFGC2a3pmNAk0ds4mVJ1zC7Y4NXYEDX9yBWE6xXC08gQPh6E3SH1PPz4wBnTzwo9rekJIu6FqPlehRj9lPPXKKSvlfqZqfOc+Ne4fFn72Grf4fLlk2D3Ph9dSAL4pKyqMjPNUzXkKay5YH40yOCgTCs//NrvWL6jtOqKH6q5fXUtboHbp23/5U8= \ No newline at end of file diff --git a/docs/design-documents/features/storage/SecureStore/SecureStore_record.jpg b/docs/design-documents/features/storage/SecureStore/SecureStore_record.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f2de3c7a703af002f74d22ce636f9c2b684e07a0 GIT binary patch literal 20872 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 Ad;kCd literal 0 HcmV?d00001 diff --git a/docs/design-documents/features/storage/SecureStore/SecureStore_record.xml b/docs/design-documents/features/storage/SecureStore/SecureStore_record.xml new file mode 100644 index 00000000000..df94e4166d2 --- /dev/null +++ b/docs/design-documents/features/storage/SecureStore/SecureStore_record.xml @@ -0,0 +1 @@ +7VhNj5swEP01SNvDStgESI6bZHfbQ6SqkdqzAw5YazA1zld/fe1gAiQQshEoomouMW/8Mbx5nrExrFm0f+coCRfMx9SApr83rLkBIbBtKP8UcsgQdzzKgIATX3cqgCX5gzVoanRDfJxWOgrGqCBJFfRYHGNPVDDEOdtVu60Zra6aoABfAEsP0Uv0F/FFmKFj2yzwr5gEYb4yMLVlhbyPgLNNrNczoLU+/jJzhPK5dP80RD7blSDr1bBmnDGRtaL9DFPFbU5bNu6twXrym+NY3DJgov3YIrrBucsOlWOnPtnKZqCaObTKgQUWyEcC5Qa5wKqms5mKg6bU+b1R7zRds1g8p8eAv8gOwEz2hfFiCn6O1C0ksRpfr7l/FFynMz7BL1e5qHuRynywwhY8SgirIJnSvAuJwMsEecq6k1tOYqGIqHwCp9HloGsdbDEXeF+CtAjeMYuw4AfZRVvHWgd6v4Jcn7tC/a6GwpLwcwzp/RacJi40Jxtadg0SHN0lwb418kZRkA5U30+jYasROu1ytPuSo9MoxxORP/CWpITF10j+l7MFsB+YLty70sW8pVp1X0T+54f+8gNs19+oJ/0BE9wlwG8/hyqW8bDFYoF2sTi9iaX5cHNDano0c247c8Dqjbq6QnzGiLw+JarpbTg9TLm8fGHRTk3BY/YkkFDF3Jo/T8yOEhRwKsy57gVxsIY3OJ50QVxzhVQJ5aZMY1/NNDlwfg28OQVlfjTIXHIsqgFLBWcfeMYo4xKJWYyVy4TSMwhREqg4ejJyWOJTFTEir/Uv2hAR31fL1Cqkur26OCZZoKKCU00q759JnQ462T+Tu+rUd+QPOt3b5uPOpiAvNZ/kfLZ4mQ31dACcYR8P7BuKXEfHA/lYfNk72kqfT63Xvw== \ No newline at end of file diff --git a/docs/design-documents/features/storage/TDBStore/TDBStore_areas.jpg b/docs/design-documents/features/storage/TDBStore/TDBStore_areas.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6d30463cb1c5a186af677b2042268fc518253590 GIT binary patch literal 31484 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 X0)PqtC;*@WfC>O80H6YZ3IPAvS%zFZ literal 0 HcmV?d00001 diff --git a/docs/design-documents/features/storage/TDBStore/TDBStore_areas.xml b/docs/design-documents/features/storage/TDBStore/TDBStore_areas.xml new file mode 100644 index 00000000000..97ba384bdba --- /dev/null +++ b/docs/design-documents/features/storage/TDBStore/TDBStore_areas.xml @@ -0,0 +1 @@ +7Zhdk5owFIZ/DZedgURQL127u53p7M160esIR8hsIDRExf76JhA+zY52yth1p94Y3pOQk+c9AaOD12n5LEievPAImIPcqHTwVwchz/eR+tLKqVbmi1ktxIJGplMnbOgvMKJr1D2NoBh0lJwzSfOhGPIsg1AONCIEPw677TgbzpqTGM6ETUjYufqDRjKp1YXvdvo3oHHSzOy5JrIl4Vss+D4z8zkI76pPHU5Jcy/Tv0hIxI89CT86eC04l3UrLdfANNsGWz3u6Z1om7eATF4zIPDrEQfC9tCkXCUmTw2MajmgB7gOfjgmVMImJ6GOHpX9SktkytSVp5rnCZicDiAklD3JJPQMPAUpTqpL2dKsh5jiQb5J8thZgRamT9KzYWn6EeN+3N66I6AaBoIdiD+7DOQCAlLkdU3uaKmxTcHEHzGZuWdIsGdB0op/xcRWJAFTUzzsuFpSH07wc8+bwJei2tgr1cHz87ILqlZsvqu7bBthI0kWbfW6VwJIE1b5bcdDlFbP3cgjjxRXObSlkIK/wZozLpSS8Qx0mpSxkUQYjTN1GSq3QOkP2iWqng0rE0hpFOlprFthuFmmcD4YOo8tu8GbW6xHUzg/v5Xzq1DSA/w3vvcY/KfGL941vnXkhRSaE3JfIeQispv2wd4lwZWvEjwFw+Vlht9BJ+vdBbvllez8Cdg1tX+ZXacLy6Pi49L0ZjcsxcC7Fie+D3jzW9Yi+uy1qDjdsBbxZZxPAvRvgRrCNQQ/zVsfuSNrLE/d9rQ59Vs/sB1+Rta8QgHioJY8sufOSt5ygLJWPPrzM6W67A7wVaz3Lwl+/A0= \ No newline at end of file diff --git a/docs/design-documents/features/storage/TDBStore/TDBStore_class_hierarchy.jpg b/docs/design-documents/features/storage/TDBStore/TDBStore_class_hierarchy.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e865ee8df75b6535aad154e738c8a66c8a29554f GIT binary patch literal 19345 zcmeHv2Ut_vw(deiiiO?9jPcJg{sHA9Wd>j|)HTor zXlMX{hWZ0gCID@KcHj5o`y(y&*neRE_k-cU0lEW>3`|Un42+CShnNpB9XxW7k?}C= z;Uml}EUYX{huGNJSlFqrS-xL{=KCjU_cKsG$a0YJAa(RN9+Wo#D+5g}O$IFuE3l81 zhL)9v(h3Ms?~{(^F9+}+2hBd({d5QD85o%kQcpm!0Q;!ly>CA)9o_!@)U!jW?*sc; z>DZ2+zHoru{0jYvTO9ItpFC$csrBkDr$s+j_{>$`dyGt6+&sK|BBEmA5|RpvXO+$= zt7u=;(bdy8FtoI?wz0LdcW}MtcHP|r?CIwp5EujvzI{LZK}6)ksOW^mq~w&;r)lXq zxq0~o$QOl06_r(}>YCTJbu#R%;~S(Vm~2& zmw`j;$@5ol8Bd9RdnT23rNpm2*&&zH0=x`m^6dGxFrJ@18|Ga3Sa}V@TKUFcYqAjJcBIkh8XA z*18}H!267r%&UsGqX4;Jf;9yQ_s*gK?H)AcL~(Lgf3AVp+88$9{KEC?*uZpvwot6I zFmv8S)-TW~P$4rbWl1amTZso=4JaU3Twuz32(PV1nEtr&R-JBi%I=*wBjtNq8E)f6 z-GS_P7}X_)Jd!w7o@ zFBoamuh`kd3_BEWjFTS0@5;KG0HM*e z;Syw9uq)%@ohPAd7GSw;vyn40-ju$)TCflzcOUaIFn3~0t%*g#C zk+~YV`+ySr^?SiZr#|~y-Rug9j)phIB&%CYm3q0mhmG96yPsocX+t%?CP|3(=AwMdn1bm@+|)h^Dg?ASP^L z??JAkU1;(9A(;BYPLrYRUX$zkRHo0300o*YI)DQ(qdEV3o@f?aDuPJq9dFS@F|r^9 z81(0$S*iNzYL+9KpIAdFfb#$a_$>PRjLS9?1(-=CC-lY&5!*Cza^0lay~nm>mU;?s zS3{UE=!5iZ5+p><)T0@oXP5gM5J#q?J_S{YJUyOh7G8brqmaK5Q^K7Te6e}N321B+ zT);uW4Zeuv1Dm8{^u_oxhyzIz|zSXSEei1K&TpN1<86Az#(ogJ8?E1j-2!UG)I1~G|?kEm(^f379vq}}j zQIGccy;fiU6;0BncPKv8k0ZloR{E2aa2S{cy>Vsp(@lAD z0K=ZUbP5|IijPxSOkl@Vj93)qnUN749hjJ^q;MnG>nkIXsgJcP-y$JNu*kyPrtsjh zl#ZW4nTK#ACfYbS^-VlC)U|~IFp!xEM(vv10UPurRb#2P!*c>!9qruJ2PWFg4K;d@ zP9VK=A-ay&6QFA~treQx7Fst$ZlSX5dP}(jUsk#4j(;`C@13iDF}HIyXD7+>mbrnw zZKeM!`Eb`joVB&lRzbeySoL=J^=|1lqVu-3lrjkOO01PRTJRJh z?s-oc$>`OvtSm_?K1Pcz*!Cd>__RzGgpXgjinPu7-K4+Z(r}@nGI)N&g@i%f`ue^M&%P z6+jQroEJn;0H(nf^cEY=mdu%6P633?X#PF)%QHbwqZCoKafCcXrX}~)4!aTcsFup( zLkuhp2#KqUkGxg0&Zg1V;8(*tul(i>J?eKl9M54E*=zi=R1g+;ZcU0h!2)rrDjRGKrj{JDeelpo#*2Mq( zMR5F>ufD(w8V}&vzB^2S_)kxp<*XE-bC9I=eeKT6?VTg|-ch&t&-ef1v|sz962dEsn8^o|CYAcm`XJ8qLz3SI5EE!4`d$qk)1_p-G> z`9x+Pd%TQa*>7XRpV0GohijSdm6mw{crujq;Xp^T&qEb)!up&sZ8M)<41AUwhpHh7 zYkBE4JCvpQAR*>Aq8Bygzf3E(bvs`wD(*--{8m#T_n46OA8_xXto8%=1Wt~DRhi4-Ah z)?PVLiOd#%I9TXu#_(R$IN{yLK{Nh?5b?(rfS(JCPe8`u10$jI z%{a)mOqs_^^}OOI0uAZO6;7Z~uUW7x&Dxa%cT^ny>W6+P*nja@zwXQRbNTO^?y#IqCtujC#Ltb6xsAz(YQYgMG;d*73>5DtlvGFHZ~lSm2IlVR;2>?Q}@pc$ozgV{p0mQuZTPF-X8tI z>+%PfIoDm^y2TjPsjiP~jva-L>TTsCbDRm2M;0%O)8_wuZ2D!1{uaJ|{V8)za`0v< zLSx8}B#FTx*&7y=NG|TWmHwyTY z&k?kVbfnV}1M_pX%@p8{Gkf%2mW+^7zXF8rwHxp)aQT@aw+)sI|3QiE!X-f4fGtDP=vo4QT8u@rZu~66rg;)FKPbl--KQ`dK zd_?zKue>CtZXg5|upC&v>_1E_sAFsVH2!&wl#TyZU4VGlBt!w;sx}-i@Yuz~z}Sf@ z9_c|3y~x9PpcNJGNUtNg2RIwv2R+g1e(JCK_(_E2q`BtQkiUE0$nuv)o7L;A+q3H@ zOMD<(XvntQH0=1^y{Zj59h{gM!3E1S(R`*z&4Dy78k-I&u5Oj`wd^{cvB2YPN}Dbj zwrI$y;B#%Oaa6u0q%>6VqXZc*YxT`21Oic2-BnljpX{fmPuwvdE1d?nvK)1&2*K3s z)o8@Ja6b#a~Onb=eX z(D3@#QB#m$w&^SCUhTg{eMON!6w&_(K%nUY3o(+pU~+qfBu?rMvTqjoKuu&1iKPH- zXfJYee`$%v@WP6Q;3f$p!X1V=jsMcCRIQQ@?EbQ|q3xgmo;_lQqhz*m4~H*l;1|nv z@sHG4!NKl9Og(hnY|z|G_9qg~(t&slEb>?`9Q#0r{!?M%IRqa}g96+OA}I~KerP%Y zJ&E_nR_9Ag`3YpB3O)y(X%K!a%-UVlW?)0}Dz4Kce|m9ivt=AB#x#bi>|{4r7{hQdGF=NFWG6|Gn3`P%c2- zUr60{gN)$=NBSIj3)@~)Z6z$mr;o4JHKjW6QGkCmVGHxD0OC1vJ6>ffKB7E~mc&~# z{7ru`y~8xMdDp^eN{`Z=My)Ya>9aK%rnhII_$a6QsZwY9bA-N`iT z=!6oQc6sCgHAjo6M3$68WI)qhQ&S@k4B29>K9wLWQ7&WJc-w#@h)S2{6dT_CP$iRN z@x-sj1zBmCZoF-VvdeL)c+0({nW!ZM-kV^0^j2X|5H-EcX>};UAo7l+*tx-&%&COo znhM>=;Y9{oOzUgWx4GBGh=R~uy(%1YL>LY1=!0Yf>fqK_EXj8753V)zqr$VV;+;<8}5k0A!tGw?x z;FYQ+1?WHQ>(u9Ey{{pJZTr)gY&MBHttW}CGXV-f6mytgvGv0s&Jx> z9>3-Dew&@@{hUj%Li7+zk{Tfji)bA|v#Rl9ms--Z%W+a>*s{X;gQ8xi6&y1rf~v>( zLH4A|eFGOSMlN$q2td0+eaf?3IqryirByal1G*x*Hd)llvcM`U1SHQ3~hwffmCJzE0G3%hOo-L-hRT4T%O z<^1urSp2IOrBf53r7hwS~nWEjhA}iD2ipS&(%7#kryYm5wKcS{4}iS<|Eg4%&~IWM z(4dKP>DYuG0O`G@0Gy;NrOS>!^u?)e12WUK1~wzh!DZ zbjy2u+U}Neq=*Vne6iuyNv0#28zDWtlFsB!-v^WP@PPb0AJ<%(9$SG2c2Dyy?4sJu zsb~j3yyHj!1>k^LjrdB($fJ z*Yeg-=PC;8J{}U3G)fyv3PpN5n|QQU@3mqSmv9e(}W0 z^a;d8RH&X=O?CE^oXF{TOt39Wi?mxiPjMSB-NSJydeDMvii~ zko;@kYWwaaChwoDO`giGYu`wO3R2k4*dj@5v z02ZaPe@kRAN08N^jW8+V6+#m|VG6rCTeN~^EGg;1#ICh<2VE%lp-=TLz<&(Bul&iV z11m_rky`b>Qc?-6ko%=Pio9qvg@H`Wrj*{+_%x+vv#-L1sjz&^c~8l`8eg}an-f;t z+#oO)&jWR7kza9UA8as~A+XPCwoDxMgf1i)8yS9pqrB%Dn+&w1+QYg9&BbGR`nQ34 zZ8@|OqbsDjegP?MO{XwbYdf4_mQ{SqE9KNqG+KPFnYh`q2cJTZc)|TLlEdcksEv(H%)EIFY@(@KXLh|Qf5SkJ zIRGdA)wrnCd3Q5JNd&L_X~1UDUX!1sJr@i!!d7qN?Qkv+HBLGqBS>(c0tjC_@>s*! zcU}$iZo$(0<$Q`Z(c^(%wQEv(q@UYma^#!XMa}u}!EJLE=W0Dw!p5APibgWxlR%AI zndyt;l9|s9EWqzq*%nNvBUN)%b=OxJI#)8&>-;BkQtO94Yx1Do_vGf2H=@)maYDv5 zSof%~{b1IQJ#&p<=T*xGWo6qfCnswY=4Q$k0(K^=XD+DOq&?s@J)AykWtmx|opQ>B z4KKfN({LRL-9!a`sk!l2n8H+w`~Y<}0v)aqlMdYxrIqI(7pVd*BTX z=c20!&y`z#c~dX3t-=$-L?e|W#@<8j);h&wA52!KR8HMWD!LMyQ(uN%Ue3AcHBwOl zNz+P50E?~X`%gSsz#M84?<|3aycr-5z=awhGkwgR*cjtsl5&Xyv|zJq)q2EX?n1~Z zaf6|alh)M-A(E5(Dri5|JoQ@{inYEyiEguK-qew*;k7Ux=khePmd%&7*##ZtaFe*Q z{1wi)=bCLiK42}wvE)COd)T$e7!&5G2WdmIkxx&O`|GM7quFLZpo)dKKeIqBO-33_ zi$3-hdRS<)8y3&LcjP_MWUvj2^8PeF@&zf%dB^n-%5Nl6#WtW=@`-82q`{a}y`_1i zjJ|35KuG6T1+S+@3E{(M*Vn*ZY>-rpk8O@cX6C4H;pXS+MaCHy`Jof=*T`0)qO_Igcgc4+F$(qP-Hf*{;^F41)7iW z<|^#Q-u?zEZ{)#sYI?Yn1NisQZ<+}jf;QDo;b6$tT)-@u;%0h1#Sg z{Yo+aib_>q==-CGXx{9*qv@r2ZDU6<{yv;q>`|uz?$6@1!#_&5{j;jb0?B^fhoD0; zn87f^uZJSZY*RL&XL`R@ho-)N^|}sI1I8dKZ?xNPEU8_tGa9!}*7%S_Ue=1Yb@ro6nXgI#D{3A=si!<7*j7{7OXsd41l=8dh1DXR;R0*nDzaiL}W zP4oDnoHIse-wlQx%*yLG2=Q^_@v%_I9}}|{MSpK6SrtkSiv4q13Op8q!f z$T9H6e0zN-`2|boVU&=ni_s<~BL%;>P`S9!SnD3*YuK;c&KjH1pZ5i2V5DCI*(_8Q zuXR{&llrQ=mrwT}`EfzxFbxfuN>lC74UzkEh)Ch&MF^t}#&~qCKkDjZ{Tx+QlryL6 zW5<`YY1b5JI!Jbew=pZgNDLv$3#|*CF~E(c z*I-$3I2$$Fqc`hv|4S9(u)b*j4Y3dF&X`NL`b%>qs@c@dXwUNrVas9E=)ssulJ+57 zAZb!xx{r}N_Zs)onDOyXVuM}46N_sd{6SamoLOr+0y{qF@8#2y9@LDcA79oGdE8TK zcj5VXepA6xu~R$yjn>`QqIV8r?jsliWcV*)7jv=YZ?_#xBl6fis@XpWmirW(`#jR0 zG{GqNg67#F_(Kc>duv^F);xS4=yMnkt|{%s+pxNx-UzvbZ?4s~)pl|xAK8gyD@Je5 zUuHk9d-+290yth#rD=qDtxHC+;o98RuzjJfjCvrr)W2X}(js33E#+(?qq;NYEEA0J zk5|Mkt=;vT+hBkOU8utKKH)0OH3)_74sSz$c0h0Jyhkz-jW_q)%FxvgYU6%&PUVdVwZW{}b0O#6UjS**?SIzp3Rur=qgx*zW+ zm;e%$6L2q=iVN^CV+!zEj-0mkf)C|xHP6i}x>=c5fO8!wd5d`3 zH?$Wrq08ud(@Kgu@f^ixkBXk6eR%Kf)OM4tZtSCkB14GApb z0nG0?2S%A!mAxXp&WAuNMT-=&&`$>GSeKa$dfhO=5k=LG$KcF5DlfPW|iI zF#qzq`v*=a{d@>y6G!_AKH=Na_C0xB(IidM#G?8hvOD4E!>gu(^ih{f)rTLU z-Gd7z54Vb6zn0yNXlP&g=pSoF{{cZyqPvA%dK0L^RSZjysP)}kxs8AJ>~2VrQ*Fx1 zm253ufqN3fCnX-|%+Cwt|5+3=noVkHy^|;()>B7)F@enmjl$jgBI+LHzaR){)i*TM zW3$2sC;LRgWrQpA`?-Ryi^Q-Q2q&KBCo2=oiThxuAc}-!>?j_NeKJIwak|W7T;}d; z#yGM|W(?YUq4!K&i@Im<+`?aIPH=?LC#WXJ368}2se+Kp!~$O9%T`adYEZ)aE+VW)KtFLH&DM{vkQQ}3K#nPijevS zK^MkANYqc8KKJeL$(D5Eh02Od`KYULNspn&1P?>R4JDMN#EQP!RwEQfi_TDKFGHa9 zTq^CQ6Z!Ql{2%)q^Pc@ohunO?Mg#_H3TGMvtvHKgnb&Z+V;?>Bbn9QE2da#q>l@9C zhf88t_FZC$*zDy8_nx*ua(4|9%6&9!-%AB8q{LVDuULjBf=exocO+mkcvECpc%4!+ z{NuqkEJf2XcO2h)@f5D$nth!TVezD)oEs3i$jCNqQ*B9YO&r6y`@ey^`97bDx!r}X1Qpm|mGCB1lTZaYPc||4dbjuYy^Iw((-p`a! zFLIj8dp2E;1St{AqRo;L6C7knF=1SL*%aXF#?jA#WHGGi8|v;a`&fUu(jLNmfJ!u) znlOG9eYVpz?#aupuA9(fk8)61%7cOF)qT!=R^-q_^XEEI+#%kO|Ayf&=mw^JCl>7v z!7(lf)~Z%}~1_xO63tU9@kNcHrJH+`wRF;1P%u}Z7Ni7`*qz@ERU zN%1AC;Fn24Qh_D-Wny7*qhR_(K`Er4gqw+v=EJmPDK)1iK$k4~VG==LoZ_a+C;<| zPI2@1?#_vU(CEj$-s={Y$WZHnh48$c+>)S}4_l{BC&P64nL(m^;lgXBJ?6cr^2!mj z=;~CH0b}9RY%m32ZxYqR_O;(ehLh#nUOuM7xP zo|#}7xWG{|(#n011vVP0KWGW$xxkNd=)-0i#+*U;>IHet70{^ zB8!ol;e08F_1;~hvAlUkaiVFE@zr#balc#_?3al9ts1NZMBdov6m->U50X&a`nBo# z)yMG5Wt~fDUrvNPeRZc&@r}IfsQhWK4g{UVVDbF`FUAlxuP>#=#g=-U-u8xgRH}4V zt#z$Q1MG@Yl$nW9Dt%?sfbA&g#5Rb{b2XqiAF`X0iwiQPIwo5^T!INM**VmpnD`l)VgN|3TotJy4 z9MR|wM?Kpouq#`OVaz;!rBCaFHr^fmW^74fzCMU3XfaEkU{DP@iOd8&6Ppq;p;7>0&5kGP@*Lt|9_N)ohnVTl6;(kTuvD*oW4O3s% zNsc(Z7GrI^^C)7LyB*F!G8o=R5G9%q2FS5Ft)4wQ+Ee5>uT*nTZp_>5^2Lz^tf2xOf;cRPsv2IeXb5rj_{ym-%{MmV z@sh)LB1U6TEe%$v&3?a-(OLG9h?0!Rx(vA80TmW{oMXr8sB%#KN_%_B^3|Dn_rAy^ zi{TJWFgI7}r~F<_!ve3ey@Ql$yPzUYBW(`B2VI(d)adSxb??yRy3vNOXO8k#)655j z_xHZe>E}v6$zCNaaN?@xD7f7l~ zFjkm;S}QSB>KwC}9>FIMmT;U-PstF?S=Kd$7EH%`ImRD_1!8NOnHdNb6LWFf4-p-K zx3^p3j(jy83Q(R`@f z>AS)3(W)d@yfXG{vc2BaNJdo{hgK<*;3zK-l8IAN!||&axAFL4betqEoZWcL3an6D zs`RkIXTJOhVWKUJ1*g|0$1z4y8EBI6aA1x?b*kwFd5NawQGmT0wnNWN?yMMFOEWEI~RCFgjX)nRR1RSCn(B>?^xB&ZN zT9#TF9bPDVZ55>$(kAQpLNcz&?yH}(p>d(!ld4o|XY_9*)D3r^i zxAD=D8gpK@SN9Hn%BXB+{5}mdwp!x76L1?HIR0(wY}T~qKejD9>0SBjKMnXN+sB^` z(Ek(qO=r**eJ$$;w(+h6?O;U;5M^UPqJL&MC&a#DeoO}WM&HVy`PtF~GvFvdZ3Kmr zq)d-cfDcA(E(9A-H0cSoq3+)!|2Z2+_fPW;HmFknkp(kRW63&oU5J@<1-(9w8vb&2 zMMIz+Q>9PT!1YeL$1c$Vg2R8HW$Ncp+Yg)ge`5*epRVWs`F*`-|{y@W~5M}KD0M(kouK)l5 literal 0 HcmV?d00001 diff --git a/docs/design-documents/features/storage/TDBStore/TDBStore_class_hierarchy.xml b/docs/design-documents/features/storage/TDBStore/TDBStore_class_hierarchy.xml new file mode 100644 index 00000000000..0bcd9d203cd --- /dev/null +++ b/docs/design-documents/features/storage/TDBStore/TDBStore_class_hierarchy.xml @@ -0,0 +1 @@ +7VhLb9swDP41Prbwu9mxbtoVGAYMSPc6KhZtC5UtT1ZSZ79+ki35WbcNsGU5NIfE+kiRFEV+TGJ5N3n9kaMy+8wwUMu1cW15a8t1nSDw5YdCDi1ytdJAygnWSj2wIb9Bg7ZGdwRDNVIUjFFByjEYs6KAWIwwxDl7GqsljI69liiFGbCJEZ2j3wkWWYuuArvH74GkmfHs2FqyRfFjytmu0P4s10uaVyvOkbGl9asMYfY0gLxby7vhjIn2Ka9vgKrcmrS1++4WpF3cHArxpg06jj2iOzAhh1TujUoVnjjolIS/diqmKEc8JYXlXUupXdbyXYLNyRR+IVjZyvyBTEAtLhAlqd4Xy+CA9zblU6o/G89bAzyso41gHIxAnmM7VZZYOcUyrmI3VWXcOMseMdk/e9pMX7KK2lUnWjIgo2hsGNQdmXP3wAWR5XXdJmHdpCnSKVlTSNQuJrUS2lRDQuQteVHCCqGbw3H1+g7lhKq2uge6B2VVxSlyqpQ638oh1ItF4XSlJlsYWA6CH6SK3uB6uip097q+Xj/1veAYLBv0wUpjSLdf2pnuS1A+6CpcqEhnVpFfK5hlFAp8rRpdrlgJMouRRIa5wqjKAOukzPIDeEYAr2ZnePpQn5QDRYLsx7aeO74294UR6aXLtHc1yfQ0gxXb8Rj0rmEfTwwF9iuGhOxPEDNDzW10Z3zbBbnnTBkRZfHjGvYkfmeN07LGrARPyhr+ORflp2/vY+z/j7HVKesxeKYel0fYVrHWdGhJ+V2TwLU9H2+DVMl08MMPpXYZmOXPI6edLO5mQAy76TQTcIk1jp2Afjgx5P27CRieM9lEuyQBDvh9Ep4L8fgnnYRXs+J03vDtecQnI6apZOMIo40JylmBHzJVzq3I6PoG0KTVmJLEs9FuGRcZS1mB6G2PTu9wetFbJgTLlaWaiAHHydVP62X6s4+iv5ZmrNGP4hElhi9e+YV9GX5wvdG1O3+JIYNxNXXro38jOK8YWmDI3pBRZElSwZEsKpf93xqtev/fkXf7Bw== \ No newline at end of file diff --git a/docs/design-documents/features/storage/TDBStore/TDBStore_design.md b/docs/design-documents/features/storage/TDBStore/TDBStore_design.md new file mode 100644 index 00000000000..aaa2c60aef3 --- /dev/null +++ b/docs/design-documents/features/storage/TDBStore/TDBStore_design.md @@ -0,0 +1,492 @@ +# TDBStore in Mbed OS + +- [TDBStore in Mbed OS](#tdbstore-in-mbed-os) + + [Revision history](#revision-history) +- [Introduction](#introduction) + + [Overview and background](#overview-and-background) + + [Requirements and assumptions](#requirements-and-assumptions) +- [System architecture and high-level design](#system-architecture-and-high-level-design) + * [Design basics](#design-basics) + + [Sequential writes](#sequential-writes) + + [Memory layout and areas](#memory-layout-and-areas) + + [Garbage collection](#garbage-collection) + + [RAM Table](#ram-table) +- [Detailed design](#detailed-design) + + [Class header](#class-header) + + [Important data structures](#important-data-structures) + + [Initialization and reset](#initialization-and-reset) + + [Core APIs](#core-apis) + + [Incremental set APIs](#incremental-set-apis) + + [Key iterator APIs](#key-iterator-apis) +- [Usage scenarios and examples](#usage-scenarios-and-examples) + + [Standard usage of the class](#standard-usage-of-the-class) +- [Other information](#other-information) + + [Open issues](#open-issues) + + +### Revision history + +| Revision | Date | Authors | Mbed OS version | Comments | +|---------- |---------------- |-------------------------------------------------------- |----------------- |------------------ | +| 1.0 | 16 September 2018 | David Saada ([@davidsaada](https://github.com/davidsaada/)) | 5.11+ | Initial revision | + +# Introduction + +### Overview and background + +TDBStore (Tiny Database Storage) is a lightweight module aimed for storing data on a flash storage. It is part of of the [KVStore](../KVStore/KVStore_design.md) class family, meaning that it supports the get/set interface. It is designed to optimize performance (speed of access), reduce wearing of the flash and to minimize storage overhead. It is also resilient to power failures. + +### Requirements and assumptions +TDBStore assumes that the underlying block device is fully dedicated for it (starting offset 0). If one wishes that only a part of the device is dedicated to TDBStore, then a sliced block device should be used, typically with `SlicingBlockDevice`. +In addition, this feature requires a flash based block device such as `FlashIAPBlockDevice` or `SpifBlockDevice`. It can work on top of block devices that don't need erasing before writes, such as `HeapBlockDevice` or `SDBlockDevice`, but requires a flash simulator layer for this purpose, like the one offered by `FlashSimBlockDevice`. + +# System architecture and high-level design + +## Design basics + +TDBStore includes the following design basics: +- Sequential writes: All writes are made sequentially on the physical storage as records, superseding the previous ones for the same key. +- Memory layout - areas: The physical storage is divided equally into two areas - active and standby. All writes are made to the end of the active area's free space. When the active area is exhausted, a garbage collection process is invoked, copying only the up to date values of all keys to the standby area, and turning it active. +- RAM table: Indexes all keys in RAM, thus allowing fast access to their records in the physical storage. + +### Sequential writes +All writes are made sequentially on the physical storage as records, superseding the previous ones for the same key. Each data record is written right after the last written one. If a key is updated, a new record with this key is written, overriding the previous value of this key. If a key is deleted, a new record with a "deleted" flag is added. +Writes expect the storage to be erased. However, TDBStore takes the "erase as you go" approcah, meaning that when it crosses a sector boundary, it checks whether the next sector is erased, and if not - it gets erased. This saves a lot of time during initialization and garbage collection (see below). + +### Memory layout and areas +![TDBStore Areas](./TDBStore_areas.jpg) + +Each key is stored in a separate record on the active area. The first record in the area is the master record. Its main purpose is to hold an area version, protecting us against the case we have two valid areas (can happen in the extreme cases of power failures). + + +![TDBStore Record](./TDBStore_record.jpg) + +Record key and data are preceded by a 24-byte header. Fields are: + +- Magic: A constant value, for quick validity checking +- Header size: Size of header +- Revision: TDBStore revision (currently 1) +- User flags: Flags received from user. Currently only write once is dealt with (others are ignored) +- Internal flags: Internal TDBStore flags (currently only includes deleted flag) +- Key size: Size of key +- Data size: Size of data +- CRC: A 32-bit CRC, caluclated on header (except CRC), key and data +- Programming size pad: Padding to the storage programming size + +### Garbage collection +Garbage collection (GC) is the process of compacting the records stored in the active area to the standby one, by copying only the most recent values of all the keys (without the ones marked as deleted). Then, the standby area becomes the active one and the previously active area is erased (not fully, only its first sector). +GC is invoked in the following cases: +1. When the active area is exhausted. +2. During initialization, when a corruption is found while scanning the active area. In this case, GC is performed up to the record preceding the corruption. + +### Reserved space +Active area includes a fixed and small reserved space. This space is used for a quick storage and extraction of a write once data (like device key). Its size is 32 bytes, aligned up to the underlying block device. Once it is written, it can't be modified. It is also copied between the areas during garbage collection process. + +### RAM Table + +All keys are indexed in memory using a RAM table. Key names are represented by a 32-bit hash. The table includes the hash (and sorted by it) and the offset to the key record in the block device. This allows both fast searching in the table as well as a low memory footprint. In order to keep code simple, the same CRC function, used for recored validation, is used for hash calculation (as TLS hash calculation is too heavy). + +![TDBStore RAM Table](./TDBStore_ram_table.jpg) + +Key names may produce duplicate hash values. This is OK, as the hash is only used for fast access to the key, and the key needs to be verified when accessing the storage. If the key doesn't match, we'll move to the next duplicate in the table. + + +# Detailed design + +TDBStore fully implements the KVStore interface over a block device. Due to the fact it may write to the block device in program units that don't have to match the underlying device program units, it should use a `BufferedBlockDevice` for that purpose. + +![TDBStore Class Hierarchy](./TDBStore_class_hierarchy.jpg) + +Functionality, as defined by KVStore, includes the following: +- Initialization & reset +- Core actions: get, set & remove +- Incremental set actions +- Iterator actions + +### Class header + +TDBStore has the following header: + +```C++ +class TDBStore : KVStore { + +public: + TDBSTore(BlockDevice *bd = 0); + virtual ~TDBSTore(); + + // Initialization and reset + virtual int init(); + virtual int deinit(); + virtual int reset(); + + // Core API + virtual int set(const char *key, const void *buffer, size_t size, uint32_t create_flags); + virtual int get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size = NULL, size_t offset = 0); + virtual int get_info(const char *key, info_t *info); + virtual int remove(const char *key); + + // Incremental set API + virtual int set_start(set_handle_t *handle, const char *key, size_t final_data_size, uint32_t create_flags); + virtual int set_add_data(set_handle_t handle, const void *value_data, size_t data_size); + virtual int set_finalize(set_handle_t handle); + + // Key iterator + virtual int iterator_open(iterator_t *it, const char *prefix = NULL); + virtual int iterator_next(iterator_t it, char *key, size_t key_size); + virtual int iterator_close(iterator_t it); + + // Reserved space APIs + virtual int reserved_space_set(void *data); + virtual int reserved_space_get(void *data); + +private: + Mutex _mutex; + void *_ram_table; + size_t *_max_keys; + size_t *_num_keys; + BlockDevice *_bd; + bd_addr_t _free_space_offset; + BufferedBlockDevice *_buff_bd; + bool _is_initialized; + int _active_area; + + // Important internal functions + + // find record offset in flash + int find_record(const char *key, uint32_t *hash, bd_size_t *bd_offset, size_t ram_table_ind); + + // garbage collection + int garbage_collection(const char *key, const void *buffer, size_t size, uint32_t create_flags); +} +``` + +### Important data structures + +```C++ +// RAM table entry +typedef struct { + uint32_t hash; + bd_size_t bd_offset; +} ram_table_entry_t; + +// Record header +typedef struct { + uint32_t magic; + uint16_t header_size; + uint16_t revision; + uint32_t user_flags; + uint16_t int_flags; + uint16_t key_size; + uint32_t data_size; + uint32_t crc; +} record_header_t; + +// incremental set handle +typedef struct { + record_header_t header; + bd_size_t bd_base_offset; + bd_size_t bd_curr_offset; + uint32_t ram_table_ind; + uint32_t hash; + bool new_key; +} inc_set_handle_t; + +// iterator handle +typedef struct { + size_t ram_table_ind; + char *prefix; +} key_iterator_handle_t; +``` + + +### Initialization and reset + +**init function** + +Header: +`virtual int init();` + +Pseudo code: +- if `_is_initialized` return OK +- Take `_mutex` +- Set `_max_keys` to an initial value of 32 +- Allocate `_ram_table` as an array of `_max_keys` +- Allocate `_buff_bd` with `_bd` as the underlying block device and initialize it +- Check validity of master records on both areas +- If one is valid, set its area as `_active_area` +- If both are valid, set the one area whose master record has the higher version as `_active_area`. Erase first sector of the other one. +- If none are valid, set area 0 as `_active_area`, and write master record with version 0. +- Traverse active area until reaching an erased sector + - Read current record and check its validity (calculte CRC) + - If not valid, perform garbage collection and exit loop + - Advance `_free_space_offset` + - Call `find_record` function to calculate hash and find key + - If not found, add new RAM table entry with current hash + - Update position of key in RAM table +- Set `_is_initialized` to true +- Release `_mutex` + +**deinit function** + +Header: +`virtual int deinit();` + +Pseudo code: +- if not `_is_initialized` return OK +- Take `_mutex` +- Deinitialize `_buff_bd` and free it +- Free `_ram_table` +- Set `_is_initialized` to false +- Release `_mutex` + +**reset function** + +Header: +`virtual int reset();` + +Pseudo code: +- Take `_mutex` +- Erase first sector in both areas +- Set `_active_area` to 0 +- Write a master record with version 0 +- Set `_free_space_offset` to end of master record +- Set `_num_keys` to 0 +- Release `_mutex` + +### Core APIs + +**set function** + +Header: +`virtual int set(const char *key, const void *buffer, size_t size, uint32_t create_flags);` + +Pseudo code: +- if not `_is_initialized` return "not initialized" error +- Call `set_start` with all fields and a local `set_handle_t` variable +- Call `set_add_data` with `buffer` and `size` +- Call `set_finalize` +- Return OK + +**get function** + +Header: +`virtual int get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size = NULL, size_t offset = 0);` + +Pseudo code: +- if not `_is_initialized` return "not initialized" error +- Take `_mutex` +- Call `find_record` to find record in storage +- If not found, return "not found" error +- Read header and calculate CRC on it +- Update CRC with key (if offset is 0) +- Read data into user buffer, starting offset. Actual size is minimum of buffer size and remainder of data +- If offset is 0 + - Update CRC with buffer + - Compare calculate CRC with header CRC. Return "data corrupt" error if different. +- Release `_mutex` +- Return OK + +**get_info function** + +Header: +`virtual int get_info(const char *key, info_t *info);` + +Pseudo code: +- if not `_is_initialized` return "not initialized" error +- Take `_mutex` +- Call `find_record` to find record in storage +- If not found, return "not found" error +- Read header +- Copy relevant fields from header into structure +- Release `_mutex` +- Return OK + +**remove function** + +Header: +`virtual int remove(const char *key);` + +Pseudo code: +- Call `set` function with `key`, delete flag set in flags and empty data + +### Incremental set APIs + +**set_start function** + +Header: +`virtual int set_start(set_handle_t *handle, const char *key, size_t final_data_size, uint32_t create_flags);` + +Pseudo code: +- Take `_mutex` +- Check if final size fits in free space, if not call `garbage_collection` +- Call `find_record` to find record in storage and achieve `ram_table_ind` and `hash` +- If found and `flags` field in header includes write once flag, return "write once" error +- Set `new_key` field in handle to true if not found and delete key not set +- Allocate an `inc_set_handle_t` structure into `handle` +- Calculate hash on `key` and update in `handle` +- Update `bd_base_offset` in handle to `_free_space_offset` +- Update a `record_header_t` structure with all relevant values +- Update all header fields in `handle` +- Calculate crc on header +- Update `ram_table_ind` and `hash` in `handle` +- Program key in position after header +- Advance `_free_space_offset` and update in `bd_curr_offset` field in handle +- Set `_free_space_offset` and update in `bd_curr_offset` field in handle +- Call `find_record` to calculate hash and find record in storage (with null key and current hash) + +**set_add_data function** + +Header: +`virtual int set_add_data(set_handle_t handle, const void *value_data, size_t data_size);` + +Pseudo code: +- Calculate crc on `value_data` and update in handle +- Program `value_data` from `bd_curr_offset` +- Advance `bd_curr_offset` + +**set_finalize function** + +Header: +`virtual int set_finalize(set_handle_t handle);` + +Pseudo code: +- Advance `_free_space_offset` to padded offset +- Update a `record_header_t` structure with all relevant values +- Program header at `bd_base_offset` from handle with pads +- Call `sync` on buffered block device +- If delete flag set + - Remove entry in index `ram_table_ind` from ram table +- Else if `new_key` field is true + - If `_num_keys` = `_max_keys` + - Increase _max_keys by 1 + - Duplicate ram table to with new `_max_keys` entries + - Add entry `ram_table_ind` +- Update `bd_offset` and `hash` in `ram_table_ind` position of ram table +- Free `handle` +- Release `_mutex` + +### Key iterator APIs + +**iterator_open function** + +Header: +`virtual int iterator_open(iterator_t *it, const char *prefix = NULL);` + +Pseudo code: +- Take `_mutex` +- Allocate a `key_iterator_handle_t` structure into `it` +- Set `ram_table_ind` field in iterator to 0 +- Duplicate `prefix` into same field in iterator +- Release `_mutex` + +**iterator_next function** + +Header: +`virtual int iterator_next(iterator_t it, char *key, size_t key_size);` + +Pseudo code: +- Take `_mutex` +- While `ram_table_ind` field in iterator smaller than `_num_keys` + - Read key pointed to by ram table in `ram_table_ind` into a local variable + - If name matches prefix + - Advance `ram_table_ind` field in iterator + - Copy name to `key` and return OK + - Advance `ram_table_ind` field in iterator +- Return "not found" error +- Release `_mutex` + +**iterator_close function** + +Header: +`virtual int iterator_close(iterator_t it);` + +Pseudo code: +- Release `prefix` field in iterator and structure allocated at `it` + + +### Reserved space + +**reserved_space_set function** + +Header: +`virtual int reserved_space_set(void *data);` + +Pseudo code: +- Check if reserved space is not empty, if it is, return a "reserved space programmed error" +- Copy `data` contents to reserved space location + +**reserved_space_get function** + +Header: +`virtual int reserved_space_get(void *data);` + +Pseudo code: +- Copy contents from reserved space location `data` + + +# Usage scenarios and examples + +### Standard usage of the class + +Following example code shows standard usage of the TDBStore class + +**Standard usage example** + +```C++ +// Underlying block device. Here, SPI Flash is fully used. +// One can use SlicingBlockDevice if we want a partition. +SPIFBlockDevice bd(PTE2, PTE4, PTE1, PTE5); + +// Instantiate tdbstore with our block device +TDBStore tdbstore(&bd); + +int res; + +// Initialize tdbstore +res = tdbstore.init(); + +// Add "Key1" +const char *val1 = "Value of key 1"; +const char *val2 = "Updated value of key 1"; +res = tdbstore.set("Key1", val1, sizeof(val1), 0); +// Update value of "Key1" +res = tdbstore.set("Key1", val2, sizeof(val2), 0); + +uint_8 value[32]; +size_t actual_size; +// Get value of "Key1". Value should return the updated value. +res = tdbstore.get("Key1", value, sizeof(value), &actual_size); + +// Remove "Key1" +res = tdbstore.remove("Key1"); + +// Incremental write, if need to generate large data with a small buffer +const int data_size = 1024; +char buf[8]; + +KVSTore::set_handle_t handle; +res = tdbstore.set_start(&handle, "Key2", data_size, 0); +for (int i = 0; i < data_size / sizeof(buf); i++) { + memset(buf, i, sizeof(buf)); + res = tdbstore.set_add_data(handle, buf, sizeof(buf)); +} +res = tdbstore.set_finalize(handle); + +// Iterate over all keys starting with "Key" +res = 0; +KVSTore::iterator_t it; +tdbstore.iterator_open(&it, "Key*"); +char key[KVSTore::KV_MAX_KEY_LENGTH]; +while (!res) { + res = tdbstore.iterator_next(&it, key, sizeof(key)); +} +res = tdbstore.iterator_close(&it); + +// Deinitialize TDBStore +res = tdbstore.deinit(); +``` +# Other information + +### Open issues + +- Need to figure a way to prevent mutex abuse in incremental set APIs. diff --git a/docs/design-documents/features/storage/TDBStore/TDBStore_ram_table.jpg b/docs/design-documents/features/storage/TDBStore/TDBStore_ram_table.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f370dc22e12e26a3afd01b4b1e3ec8e0259d8cde GIT binary patch literal 44054 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 QM)Eh->c4fC;P2D_3)V?lP5=M^ literal 0 HcmV?d00001 diff --git a/docs/design-documents/features/storage/TDBStore/TDBStore_ram_table.xml b/docs/design-documents/features/storage/TDBStore/TDBStore_ram_table.xml new file mode 100644 index 00000000000..5638d79e1df --- /dev/null +++ b/docs/design-documents/features/storage/TDBStore/TDBStore_ram_table.xml @@ -0,0 +1 @@ +3Zlbb5swGIZ/DZedABdCL5P0JE3VpmbSuksHHLDq4Mw4DdmvnwEbAjgN3SAtrVTFvDa2ed7PB4wB5uv0jsFN9EADRAzbDFIDXBu2bTnOpfjJlH2hTDwphAwHslAlLPAfJEVTqlscoKRWkFNKON7URZ/GMfJ5TYOM0V292IqSeqsbGKKWsPAhaas/ccCjQvUcs9LvEQ4j1bJlypwl9J9DRrexbM+wwSr/K7LXUNUlyycRDOjuQAI3BpgzSnmRWqdzRDK2Cltx3+2R3LLfDMW8yw2OW9zxAskWqS7nHeN7BWMXYY4WG+hn1zthuAFmEV8TcWWJJEw2hQUrnCJR7azdB9mtF8Q4Sg8k2ac7RNeIs70oInMdFQgyfhSuXWWG7UktOjCiFKEMgLCsuoIgEpLDESYTDROXiCZmKyoe6RCO+3tLVcZFksfxVBSwwCatMkUqlL95LUslPE4fROEfcClqU3msWVp0d6nRiq4ouWGZwMzrLiWc0Wc0p4QyocQ0RlmvMSENCRIcxuLSF+Yhoc8y07AYGVOZscZBkDUz04VFHvlZEFyb/QSCV48DSxMI1kQXCH3EgXc0DkpDHmCSYbLNR+RTFug9a5hTh3RidL3jYAJ9MLw6zfAryjprjYKd05Gd0wM71+zKzu46e3wwmuWyeY5QdK2uOME44IFzxqL96WPRO2csgtM4bxkSuwKzgNCF4KdZ9F2zsWBpIr2cOvpe9d3ju7/2imXewyQ6X8C3EGpAv0L11C6g3F31He6TyzfMHiNmqtkdDMfUecNyNmKmmml5OKZvGvvfVqsE8ZFQtbyTw3+oxW7S4T2qGv2jpqqZAAaj2vnNCoydqmYKGIqqp1upGjhQHEyzM8Zsw0RgkmC/TkA8Jds/SVr5xa/s4otT0kFB6/ixtV3ikIVI7eCv9LwOgDgaIEpjiECOX+pN6ijJFr5TnB9zSTuaMzJw6jUkdMt8JG+qULfqAbZ+ClL1FE/cqie3rHzobi7q1sZ3d7F4h/ooLrr/6KJlNaNhOBs7HE6ftDHF/OkgXZnYj8EFtvqUXDP9yFR3HtMbnl+aDa8+ouk9nL47nU7fF5yy7DuQbV6I/6mfcxYJhuBnOXv/r2XYUa8YyvQBD9/FZfXRq4ia6ssiuPkL \ No newline at end of file diff --git a/docs/design-documents/features/storage/TDBStore/TDBStore_record.jpg b/docs/design-documents/features/storage/TDBStore/TDBStore_record.jpg new file mode 100644 index 0000000000000000000000000000000000000000..acb4a9a39a70bc2dbbf2511ee763ed1993bb6c3b GIT binary patch literal 30678 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 Jg46jj^e;gB?KA)Y literal 0 HcmV?d00001 diff --git a/docs/design-documents/features/storage/TDBStore/TDBStore_record.xml b/docs/design-documents/features/storage/TDBStore/TDBStore_record.xml new file mode 100644 index 00000000000..fd8b2b4e74f --- /dev/null +++ b/docs/design-documents/features/storage/TDBStore/TDBStore_record.xml @@ -0,0 +1 @@ +7VnbrqIwFP0ak5mHM4GWiz4qnksyOcmJZjLPFSo0VuqUepuvnyJFQAE9RIdhoi+WtXvZrr26uys96Cx3rxytgnfmYdoDmrfrwXEPAN00gfyKkX2C2H0jAXxOPNUpA6bkN1agptA18XBU6CgYo4KsiqDLwhC7ooAhztm22G3OaHHVFfLxGTB1ET1HfxJPBAnaN7UMf8PED9KVdU1ZZshd+JytQ7VeD8D54ZOYlyidS/WPAuSxbQ6Czz3ocMZE0lruHExjblPaknEvFdaj3xyH4poBlp2M2CC6xqnLFpVjRx7ZyKYfN1NolgLvyCduisrZZyU9tUjsFZ/Wr3X8g0ZzFoqn6BDtoeyga6tdZjybgp8iZQtJrMTROt+/GF9rPS9btjAfKPw2cIg2jvnUpHkbEIGnK+TG1q3cHRILxJLKJ/04Oh8fFbIN5gLvcpCK1ytmSyz4XnZR1r6SjtpaeiqlbSZUQ0FBTqO2wpDaGv5x4kwesqEUUq6WgdZILW8YeZh3VC6H5HRbAYJuC/CouBoFmvdSoF6pwCORE7z5Vkfwfx0bu8XYGJdj8yOqTwR1sXmhyI86mkW6fuiANve82ejUmWCptY3kqKOK6XgmAldkIvteirEaKeY73t+vdnjopFwn0Gixnm12+xkjgf5ukfkv56mOn2ywxfuUrlWXs3WcOxPnoZZW1GJcUQfdTy2gkVo+OPNrL0RN85X2MRx3O5pWm9GEl69LlRVJy8SZ2mXidHg35q64aFaf0S1TZ8FWqWt2l3qkkKpw2q2mkLJ7zgkhUYBWcdNdc7ofceQusLjMTEZj8iSQICyUj08D7TbEAQsUiIP6GW+ghDfY129BXHXhH5dJV9VPZm39lAIT7DLuyf7FP8Gvrq8SdyrELqkWxbhFgrMFdhhlXCIhC3HsOaH0BEKU+HE4XRlA6RUcxYEjLqJDZVgSz4uXKRVKcZPdQAy6bRfEcNwy+aQ4KJED+Pw2ko/Z+7WDLfcSEz7/AQ== \ No newline at end of file diff --git a/features/storage/kvstore/KVStore.h b/features/storage/kvstore/KVStore.h new file mode 100644 index 00000000000..d97c0e8dfd4 --- /dev/null +++ b/features/storage/kvstore/KVStore.h @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2018 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 MBED_KVSTORE_H +#define MBED_KVSTORE_H + +#include +#include +#include + +namespace mbed { + +/** KVStore class + * + * Interface class for Key Value Storage + */ + +class KVStore { +public: + enum create_flags { + WRITE_ONCE_FLAG = (1 << 0), + ENCRYPT_FLAG = (1 << 1), + AUTHENTICATE_FLAG = (1 << 2), + ROLLBACK_PROTECT_FLAG = (1 << 3), + }; + + static const uint32_t MAX_KEY_SIZE = 128; + + typedef struct _opaque_set_handle *set_handle_t; + + typedef struct _opaque_key_iterator *iterator_t; + + typedef struct info { + size_t size; + uint32_t flags; + } info_t; + + virtual ~KVStore() {}; + + /** + * @brief Initialize KVStore + * + * @returns 0 on success or a negative error code on failure + */ + virtual int init() = 0; + + /** + * @brief Deinitialize KVStore + * + * @returns 0 on success or a negative error code on failure + */ + virtual int deinit() = 0; + + + /** + * @brief Reset KVStore contents (clear all keys) + * + * @returns 0 on success or a negative error code on failure + */ + virtual int reset() = 0; + + /** + * @brief Set one KVStore item, given key and value. + * + * @param[in] key Key. + * @param[in] buffer Value data buffer. + * @param[in] size Value data size. + * @param[in] create_flags Flag mask. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int set(const char *key, const void *buffer, size_t size, uint32_t create_flags) = 0; + + /** + * @brief Get one KVStore item, given key. + * + * @param[in] key Key. + * @param[in] buffer Value data buffer. + * @param[in] buffer_size Value data buffer size. + * @param[out] actual_size Actual read size (NULL to pass nothing). + * @param[in] offset Offset to read from in data. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size = NULL, size_t offset = 0) = 0; + + /** + * @brief Get information of a given key. + * + * @param[in] key Key. + * @param[out] info Returned information structure (NULL to pass nothing). + * + * @returns 0 on success or a negative error code on failure + */ + virtual int get_info(const char *key, info_t *info = NULL) = 0; + + /** + * @brief Remove a KVStore item, given key. + * + * @param[in] key Key. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int remove(const char *key) = 0; + + + /** + * @brief Start an incremental KVStore set sequence. + * + * @param[out] handle Returned incremental set handle. + * @param[in] key Key. + * @param[in] final_data_size Final value data size. + * @param[in] create_flags Flag mask. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int set_start(set_handle_t *handle, const char *key, size_t final_data_size, uint32_t create_flags) = 0; + + /** + * @brief Add data to incremental KVStore set sequence. + * + * @param[in] handle Incremental set handle. + * @param[in] value_data value data to add. + * @param[in] data_size value data size. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int set_add_data(set_handle_t handle, const void *value_data, size_t data_size) = 0; + + /** + * @brief Finalize an incremental KVStore set sequence. + * + * @param[in] handle Incremental set handle. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int set_finalize(set_handle_t handle) = 0; + + /** + * @brief Start an iteration over KVStore keys. + * + * @param[out] it Returned iterator handle. + * @param[in] prefix Key prefix (null for all keys). + * + * @returns 0 on success or a negative error code on failure + */ + virtual int iterator_open(iterator_t *it, const char *prefix = NULL) = 0; + + /** + * @brief Get next key in iteration. + * + * @param[in] it Iterator handle. + * @param[in] key Buffer for returned key. + * @param[in] key_size Key buffer size. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int iterator_next(iterator_t it, char *key, size_t key_size) = 0; + + /** + * @brief Close iteration. + * + * @param[in] it Iterator handle. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int iterator_close(iterator_t it) = 0; + + /** Convenience function for checking key validity + * + * @param[in] key_size Key buffer size. + * + * @returns 0 on success or a negative error code on failure + */ + bool is_valid_key(const char *key) const + { + if (!key || !strlen(key) || (strlen(key) > MAX_KEY_SIZE)) { + return false; + } + + if (strpbrk(key, " */?:;\"|<>\\")) { + return false; + } + return true; + } + +}; +/** @}*/ + +} // namespace mbed + +#endif diff --git a/platform/mbed_error.h b/platform/mbed_error.h index c48d05c27c7..46cea162040 100644 --- a/platform/mbed_error.h +++ b/platform/mbed_error.h @@ -532,6 +532,9 @@ typedef enum _mbed_module_type { BLE_NO_FRAME_INITIALIZED, 321 BLE No frame initialized BLE_BACKEND_CREATION_FAILED 322 BLE Backend creation failed BLE_BACKEND_NOT_INITIALIZED 323 BLE Backend not initialized + ASSERTION_FAILED 324 Assertion Failed + AUTHENTICATION_FAILED 325 Authentication Failed + RBP_AUTHENTICATION_FAILED 326 Rollback Protect Authentication Failed \endverbatim * * @note @@ -783,6 +786,8 @@ typedef enum _mbed_error_code { MBED_DEFINE_SYSTEM_ERROR(BLE_BACKEND_CREATION_FAILED, 66), /* 322 BLE Backend creation failed */ MBED_DEFINE_SYSTEM_ERROR(BLE_BACKEND_NOT_INITIALIZED, 67), /* 323 BLE Backend not initialized */ MBED_DEFINE_SYSTEM_ERROR(ASSERTION_FAILED, 68), /* 324 Assertion Failed */ + MBED_DEFINE_SYSTEM_ERROR(AUTHENTICATION_FAILED, 69), /* 325 Authentication Failed */ + MBED_DEFINE_SYSTEM_ERROR(RBP_AUTHENTICATION_FAILED, 70), /* 326 Rollback Protection Authentication Failed */ //Everytime you add a new system error code, you must update //Error documentation under Handbook to capture the info on From 5a4f1d1cc45a9142f4160e7fcac327f06a2e30bd Mon Sep 17 00:00:00 2001 From: David Saada Date: Wed, 7 Nov 2018 15:41:31 +0200 Subject: [PATCH 44/45] Implement TDBStore class --- .../TESTS/kvstore/tdbstore_whitebox/main.cpp | 561 +++++++ .../blockdevice/BufferedBlockDevice.cpp | 5 +- .../storage/kvstore/tdbstore/TDBStore.cpp | 1386 +++++++++++++++++ features/storage/kvstore/tdbstore/TDBStore.h | 461 ++++++ 4 files changed, 2412 insertions(+), 1 deletion(-) create mode 100644 features/storage/TESTS/kvstore/tdbstore_whitebox/main.cpp create mode 100644 features/storage/kvstore/tdbstore/TDBStore.cpp create mode 100644 features/storage/kvstore/tdbstore/TDBStore.h diff --git a/features/storage/TESTS/kvstore/tdbstore_whitebox/main.cpp b/features/storage/TESTS/kvstore/tdbstore_whitebox/main.cpp new file mode 100644 index 00000000000..1730418dbda --- /dev/null +++ b/features/storage/TESTS/kvstore/tdbstore_whitebox/main.cpp @@ -0,0 +1,561 @@ +/* +* Copyright (c) 2018 ARM Limited. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 +* Licensed under the Apache License, Version 2.0 (the License); you may +* not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an AS IS BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "TDBStore.h" +#ifdef MBED_CONF_RTOS_PRESENT +#include "Thread.h" +#endif +#include "mbed_error.h" +#include "Timer.h" +#include "HeapBlockDevice.h" +#include "FlashSimBlockDevice.h" +#include "SlicingBlockDevice.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest/utest.h" +#include +#include +#include +#include + +// Define this if you want to run the test locally (requires SPIF/SD block device) +#define LOCAL_TEST + +// MBED_TEST_SIM_BLOCKDEVICE is used here only to filter out inappropriate boards (little RAM etc.) +#if !defined(MBED_TEST_SIM_BLOCKDEVICE) && !defined(LOCAL_TEST) +#error [NOT_SUPPORTED] KVStore test not supported on this platform +#endif + +using namespace mbed; + +#undef TEST_SPIF +#undef TEST_SD + +#ifdef TEST_SPIF +#include "SPIFBlockDevice.h" +SPIFBlockDevice bd(MBED_CONF_SPIF_DRIVER_SPI_MOSI, MBED_CONF_SPIF_DRIVER_SPI_MISO, + MBED_CONF_SPIF_DRIVER_SPI_CLK, MBED_CONF_SPIF_DRIVER_SPI_CS); +SlicingBlockDevice flash_bd(&bd, 0, 16 * 4096); + +#elif defined(TEST_SD) +#include "SDBlockDevice.h" +SDBlockDevice bd(MBED_CONF_SPIF_DRIVER_SPI_MOSI, MBED_CONF_SPIF_DRIVER_SPI_MISO, + MBED_CONF_SPIF_DRIVER_SPI_CLK, MBED_CONF_SPIF_DRIVER_SPI_CS); +SlicingBlockDevice slice_bd(&bd, 0, 512 * 512); +FlashSimBlockDevice flash_bd(&slice_bd); + +#else +HeapBlockDevice bd(64 * 4096, 1, 1, 4096); +FlashSimBlockDevice flash_bd(&bd); +#endif + + +using namespace utest::v1; + +typedef struct { + size_t size; + size_t read_size; + size_t prog_size; + size_t erase_size; +} bd_params_t; + +static const char *const key1 = "key1"; +static const char *const key1_val1 = "val1"; +static const char *const key2 = "name_of_key2"; +static const char *const key2_val1 = "val3"; +static const char *const key2_val2 = "val2 of key 2"; +static const char *const key2_val3 = "Val1 value of key 2 "; +static const char *const key3 = "This_is_the_name_of_key3"; +static const char *const key3_val1 = "Data value of key 3 is the following"; +static const char *const key4 = "This_is_the_name_of_key4"; +static const char *const key4_val1 = "Is this the value of key 4?"; +static const char *const key4_val2 = "What the hell is the value of key 4, god damn it!"; +static const char *const key5 = "This_is_the_real_name_of_Key5"; +static const char *const key5_val1 = "Key 5 value that should definitely be written"; +static const char *const key5_val2 = "Key 5 value that should definitely not be written"; +static const char *const res_val1 = "This should be saved as the reserved data"; +static const char *const res_val2 = "This should surely not be saved as the reserved data"; + +static void white_box_test() +{ + bd_params_t bd_params[] = { + {8192, 1, 16, 4096}, // Standard + {4096 * 4, 1, 1, 4096}, // K82F like + {8192, 64, 128, 2048}, // Awkward read and program sizes, both larger than header size + {2048, 1, 4, 128}, // Small sector and total sizes + {0, 0, 0, 0}, // Place holder for real non volatile device + }; + + int num_bds = sizeof(bd_params) / sizeof(bd_params_t); + uint8_t get_buf[256]; + size_t actual_data_size; + int result; + mbed::Timer timer; + int elapsed; + KVStore::info_t info; + +#if defined(TEST_SPIF) || defined(TEST_SD) + flash_bd.init(); + bd_params[num_bds - 1].size = flash_bd.size(); + bd_params[num_bds - 1].read_size = flash_bd.get_read_size(); + bd_params[num_bds - 1].prog_size = flash_bd.get_program_size(); + bd_params[num_bds - 1].erase_size = flash_bd.get_erase_size(); + flash_bd.deinit(); +#endif + + timer.start(); + for (int bd_num = 0; bd_num < num_bds; bd_num++) { + bd_params_t *bdp = &bd_params[bd_num]; + if (!bdp->size) { + break; + } + printf("\n\nBD #%d: size %d, read %d, prog %d, erase %d\n", + bd_num, bdp->size, bdp->read_size, bdp->prog_size, bdp->erase_size); + HeapBlockDevice heap_bd(bdp->size, bdp->read_size, bdp->prog_size, bdp->erase_size); + FlashSimBlockDevice flash_sim_bd(&heap_bd); + BlockDevice *test_bd; + if (bd_num == num_bds - 1) { + test_bd = &flash_bd; + // Required for deinit + flash_sim_bd.init(); + } else { + test_bd = &flash_sim_bd; + } + + TDBStore *tdbs = new TDBStore(test_bd); + + timer.reset(); + result = tdbs->init(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + elapsed = timer.read_ms(); + printf("Elapsed time for init %d ms\n", elapsed); + + timer.reset(); + result = tdbs->reset(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + elapsed = timer.read_ms(); + printf("Elapsed time for reset is %d ms\n", elapsed); + + result = tdbs->reserved_data_set(res_val1, strlen(res_val1)); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = tdbs->reserved_data_set(res_val2, strlen(res_val2)); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_WRITE_FAILED, result); + + result = tdbs->set(key1, key1_val1, strlen(key1_val1), 0); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = tdbs->set(key2, key2_val1, strlen(key2_val1), 0); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = tdbs->set(key2, key2_val2, strlen(key2_val2), 0); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + timer.reset(); + result = tdbs->set(key2, key2_val3, strlen(key2_val3), 0); + elapsed = timer.read_ms(); + printf("Elapsed time for set is %d ms\n", elapsed); + + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = tdbs->set(key3, key3_val1, strlen(key3_val1), 0); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = tdbs->get(key3, get_buf, sizeof(get_buf), &actual_data_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(strlen(key3_val1), actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key3_val1, get_buf, strlen(key3_val1)); + + KVStore::set_handle_t handle; + result = tdbs->set_start(&handle, key4, 15, 0); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = tdbs->set_add_data(handle, key4_val2, 10); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + result = tdbs->set_add_data(handle, key4_val2 + 10, 5); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + result = tdbs->set_finalize(handle); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = tdbs->get(key4, get_buf, sizeof(get_buf), &actual_data_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(15, actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key4_val2, get_buf, actual_data_size); + + result = tdbs->get(key4, get_buf, 7, &actual_data_size, 4); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(7, actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key4_val2 + 4, get_buf, actual_data_size); + + for (int j = 0; j < 2; j++) { + result = tdbs->set(key4, key4_val1, strlen(key4_val1), 0); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = tdbs->set(key4, key4_val2, strlen(key4_val2), 0); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + } + + result = tdbs->remove(key3); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = tdbs->remove(key3); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_ITEM_NOT_FOUND, result); + + result = tdbs->get_info(key5, &info); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_ITEM_NOT_FOUND, result); + + result = tdbs->set(key5, key5_val1, strlen(key5_val1), KVStore::WRITE_ONCE_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = tdbs->set(key5, key5_val2, strlen(key5_val2), 0); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_WRITE_PROTECTED, result); + + result = tdbs->remove(key5); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_WRITE_PROTECTED, result); + + result = tdbs->get_info(key5, &info); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(strlen(key5_val1), info.size); + TEST_ASSERT_EQUAL(KVStore::WRITE_ONCE_FLAG, info.flags); + + result = tdbs->get(key5, get_buf, sizeof(get_buf), &actual_data_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(strlen(key5_val1), actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key5_val1, get_buf, strlen(key5_val1)); + + for (int i = 0; i < 2; i++) { + printf("%s deinit/init\n", i ? "After" : "Before"); + result = tdbs->get(key1, get_buf, sizeof(get_buf), &actual_data_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(strlen(key1_val1), actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key1_val1, get_buf, strlen(key1_val1)); + + timer.reset(); + result = tdbs->get(key2, get_buf, sizeof(get_buf), &actual_data_size); + elapsed = timer.read_ms(); + printf("Elapsed time for get is %d ms\n", elapsed); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(strlen(key2_val3), actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key2_val3, get_buf, strlen(key2_val3)); + + result = tdbs->get(key3, get_buf, sizeof(get_buf), &actual_data_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_ITEM_NOT_FOUND, result); + + result = tdbs->get(key4, get_buf, sizeof(get_buf), &actual_data_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(strlen(key4_val2), actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key4_val2, get_buf, strlen(key4_val2)); + + result = tdbs->get(key5, get_buf, sizeof(get_buf), &actual_data_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(strlen(key5_val1), actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key5_val1, get_buf, strlen(key5_val1)); + + KVStore::iterator_t it; + char *char_get_buf = reinterpret_cast (get_buf); + + result = tdbs->iterator_open(&it, "This"); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = tdbs->iterator_next(it, char_get_buf, sizeof(get_buf)); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + bool got_key4 = !strcmp(key4, char_get_buf); + bool got_key5 = !strcmp(key5, char_get_buf); + TEST_ASSERT_EQUAL(true, got_key4 || got_key5); + + result = tdbs->iterator_next(it, char_get_buf, sizeof(get_buf)); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + if (got_key4) { + TEST_ASSERT_EQUAL_STRING(key5, char_get_buf); + } else { + TEST_ASSERT_EQUAL_STRING(key4, char_get_buf); + } + + result = tdbs->iterator_next(it, (char *)get_buf, sizeof(get_buf)); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_ITEM_NOT_FOUND, result); + + result = tdbs->iterator_close(it); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = tdbs->reserved_data_get(get_buf, strlen(res_val1)); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL_STRING_LEN(res_val1, get_buf, strlen(res_val1)); + + result = tdbs->deinit(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + timer.reset(); + result = tdbs->init(); + elapsed = timer.read_ms(); + printf("Elapsed time for init is %d ms\n", elapsed); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + } + + result = tdbs->deinit(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + delete tdbs; + } +} + + +static void multi_set_test() +{ + char *key; + uint8_t *get_buf, *set_buf; + size_t key_size = 32; + size_t data_size = 512; + size_t num_keys = 16; + size_t set_iters = 3; + size_t actual_data_size; + int result; + mbed::Timer timer; + int elapsed; + size_t i; + uint8_t key_ind; + + timer.start(); +#if !defined(TEST_SPIF) && !defined(TEST_SD) + HeapBlockDevice heap_bd(4096 * 64, 1, 1, 4096); + FlashSimBlockDevice flash_bd(&heap_bd); +#endif + + TDBStore *tdbs = new TDBStore(&flash_bd); + + timer.reset(); + result = tdbs->init(); + elapsed = timer.read_ms(); + printf("Elapsed time for initial init is %d ms\n", elapsed); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + key = new char[key_size + 1]; + get_buf = new uint8_t[data_size]; + set_buf = new uint8_t[data_size]; + + srand(1); + for (i = 0; i < key_size; i++) { + // Alphabet characters only + key[i] = 'a' + rand() % ('z' - 'a' + 1); + } + key[key_size] = '\0'; + + for (i = 0; i < data_size; i++) { + set_buf[i] = rand() % 256; + } + + int max_set_time = 0, total_set_time = 0; + int max_get_time = 0, total_get_time = 0; + + timer.reset(); + result = tdbs->reset(); + elapsed = timer.read_ms(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + for (i = 0; i < set_iters; i++) { + for (key_ind = 0; key_ind < num_keys; key_ind++) { + key[0] = 'A' + key_ind; + set_buf[0] = key_ind * (i + 1); + timer.reset(); + result = tdbs->set(key, set_buf, data_size, 0); + elapsed = timer.read_ms(); + TEST_ASSERT_EQUAL(0, result); + if (elapsed > max_set_time) { + max_set_time = elapsed; + } + total_set_time += elapsed; + } + } + + for (key_ind = 0; key_ind < num_keys; key_ind++) { + key[0] = 'A' + key_ind; + set_buf[0] = key_ind * set_iters; + timer.reset(); + result = tdbs->get(key, get_buf, data_size, &actual_data_size); + elapsed = timer.read_ms(); + if (result) { + printf("Result %d\n", result & 0xFFFF); + } + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(data_size, actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(set_buf, get_buf, data_size); + if (elapsed > max_get_time) { + max_get_time = elapsed; + } + total_get_time += elapsed; + } + + printf("set time: Total (%d * %d times) - %d ms, Average - %d ms, Max - %d ms\n", + set_iters, num_keys, total_set_time, + total_set_time / (set_iters * num_keys), max_set_time); + printf("get time: Total (%d times) - %d ms, Average - %d ms, Max - %d ms\n", + num_keys, total_get_time, + total_get_time / num_keys, max_get_time); + + result = tdbs->deinit(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + timer.reset(); + result = tdbs->init(); + elapsed = timer.read_ms(); + printf("Elapsed time for init is %d ms\n", elapsed); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = tdbs->deinit(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + delete[] key; + delete[] get_buf; + delete[] set_buf; + + delete tdbs; +} + +static void error_inject_test() +{ + char *key; + uint8_t *get_buf, *set_buf, *exists; + size_t key_size = 8; + size_t data_size = 16; + size_t num_keys = 'Z' - 'A' + 1; + size_t num_blocks = 4; + size_t block_size = 1024; + size_t actual_data_size; + int result; + uint8_t set_iters = 5; + uint8_t i, key_ind; + + // Don't use a non volatile BD here (won't work in this test) + HeapBlockDevice bd(num_blocks * block_size, 1, 1, block_size); + FlashSimBlockDevice flash_bd(&bd); + + result = flash_bd.init(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + result = flash_bd.erase(0, num_blocks * block_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + result = flash_bd.deinit(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + TDBStore *tdbs = new TDBStore(&flash_bd); + + result = tdbs->init(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = tdbs->reset(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + key = new char[key_size + 1]; + get_buf = new uint8_t[data_size]; + set_buf = new uint8_t[data_size]; + exists = new uint8_t[num_keys]; + + memset(exists, 0, num_keys); + + for (i = 0; i < set_iters; i++) { + for (key_ind = 0; key_ind < num_keys; key_ind++) { + memset(key, 'A' + key_ind, key_size); + key[key_size + 1] = '\0'; + memset(set_buf, key_ind * i, data_size); + if ((key_ind != (num_keys - 1)) && exists[key_ind] && !(rand() % 3)) { + result = tdbs->remove(key); + exists[key_ind] = 0; + } else { + result = tdbs->set(key, set_buf, data_size, 0); + exists[key_ind] = 1; + } + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + } + } + + for (uint8_t get_iter = 0; get_iter < 2; get_iter++) { + for (key_ind = 0; key_ind < num_keys; key_ind++) { + memset(key, 'A' + key_ind, key_size); + key[key_size + 1] = '\0'; + if (key_ind == (num_keys - 1)) { + // last key will hold the previous version at the second iteration (after being crippled) + memset(set_buf, key_ind * (set_iters - get_iter - 1), data_size); + } else { + memset(set_buf, key_ind * (set_iters - 1), data_size); + } + result = tdbs->get(key, get_buf, data_size, &actual_data_size); + if (exists[key_ind]) { + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(data_size, actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(set_buf, get_buf, data_size); + } else { + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_ITEM_NOT_FOUND, result); + } + } + + if (get_iter) { + break; + } + + // Cripple the last key + uint8_t erase_val = (uint8_t) flash_bd.get_erase_value(); + uint8_t x; + bd_addr_t addr; + for (addr = bd.size() - 1; addr > 0; addr--) { + bd.read(&x, addr, 1); + if (x != erase_val) { + break; + } + } + x++; + bd.program(&x, addr, 1); + + result = tdbs->get(key, get_buf, data_size, &actual_data_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_INVALID_DATA_DETECTED, result); + + result = tdbs->deinit(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = tdbs->init(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + } + + delete[] key; + delete[] get_buf; + delete[] set_buf; + delete[] exists; + + delete tdbs; +} + + +utest::v1::status_t greentea_failure_handler(const Case *const source, const failure_t reason) +{ + greentea_case_failure_abort_handler(source, reason); + return STATUS_CONTINUE; +} + +Case cases[] = { + Case("TDBStore: White box test", white_box_test, greentea_failure_handler), + Case("TDBStore: Multiple set test", multi_set_test, greentea_failure_handler), + Case("TDBStore: Error inject test", error_inject_test, greentea_failure_handler), +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(120, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + return !Harness::run(specification); +} diff --git a/features/storage/blockdevice/BufferedBlockDevice.cpp b/features/storage/blockdevice/BufferedBlockDevice.cpp index 9e87a96f8a7..cc43a53ed27 100644 --- a/features/storage/blockdevice/BufferedBlockDevice.cpp +++ b/features/storage/blockdevice/BufferedBlockDevice.cpp @@ -123,7 +123,10 @@ int BufferedBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size) if (aligned_addr != _curr_aligned_addr) { // Need to flush if moved to another program unit - flush(); + int ret = flush(); + if (ret) { + return ret; + } _curr_aligned_addr = aligned_addr; moved_unit = true; } diff --git a/features/storage/kvstore/tdbstore/TDBStore.cpp b/features/storage/kvstore/tdbstore/TDBStore.cpp new file mode 100644 index 00000000000..df5ef6afab6 --- /dev/null +++ b/features/storage/kvstore/tdbstore/TDBStore.cpp @@ -0,0 +1,1386 @@ +/* + * Copyright (c) 2018 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. + */ + +// ----------------------------------------------------------- Includes ----------------------------------------------------------- + +#include "TDBStore.h" + +#include +#include +#include +#include "mbed_error.h" +#include "mbed_assert.h" +#include "mbed_wait_api.h" + +using namespace mbed; + +// --------------------------------------------------------- Definitions ---------------------------------------------------------- + +static const uint32_t delete_flag = (1UL << 31); + +typedef struct { + uint32_t magic; + uint16_t header_size; + uint16_t revision; + uint32_t flags; + uint16_t key_size; + uint16_t reserved; + uint32_t data_size; + uint32_t crc; +} record_header_t; + +typedef struct { + uint32_t hash; + bd_size_t bd_offset; +} ram_table_entry_t; + +static const char *master_rec_key = "TDBS"; +static const uint32_t tdbstore_magic = 0x54686683; // "TDBS" in ASCII +static const uint32_t tdbstore_revision = 1; + +typedef struct { + uint16_t version; + uint16_t tdbstore_revision; + uint32_t reserved; +} master_record_data_t; + +typedef enum { + TDBSTORE_AREA_STATE_NONE = 0, + TDBSTORE_AREA_STATE_EMPTY, + TDBSTORE_AREA_STATE_VALID, +} area_state_e; + +static const uint32_t work_buf_size = 64; +static const uint32_t initial_crc = 0xFFFFFFFF; +static const uint32_t initial_max_keys = 16; + +// incremental set handle +typedef struct { + record_header_t header; + bd_size_t bd_base_offset; + bd_size_t bd_curr_offset; + uint32_t offset_in_data; + uint32_t ram_table_ind; + uint32_t hash; + bool new_key; +} inc_set_handle_t; + +// iterator handle +typedef struct { + size_t ram_table_ind; + char *prefix; +} key_iterator_handle_t; + + +// -------------------------------------------------- Local Functions Declaration ---------------------------------------------------- + +// -------------------------------------------------- Functions Implementation ---------------------------------------------------- + +static inline uint32_t align_up(uint32_t val, uint32_t size) +{ + return (((val - 1) / size) + 1) * size; +} + + +// CRC32 calculation. Supports "rolling" calculation (using the initial value). +static uint32_t crc32(uint32_t init_crc, uint32_t data_size, const void *data_buf) +{ + uint32_t i, j; + uint32_t crc, mask; + const char *data = static_cast (data_buf); + + crc = init_crc; + for (i = 0; i < data_size; i++) { + crc = crc ^ (uint32_t) (data[i]); + for (j = 0; j < 8; j++) { + mask = -(crc & 1); + crc = (crc >> 1) ^ (0xEDB88320 & mask); + } + } + return crc; +} + + +// Class member functions + +TDBStore::TDBStore(BlockDevice *bd) : _ram_table(0), _max_keys(0), + _num_keys(0), _bd(bd), _buff_bd(0), _free_space_offset(0), _master_record_offset(0), + _is_initialized(false), _active_area(0), _active_area_version(0), _size(0), + _prog_size(0), _work_buf(0), _key_buf(0), _variant_bd_erase_unit_size(false), _inc_set_handle(0) +{ +} + +TDBStore::~TDBStore() +{ + deinit(); +} + +int TDBStore::read_area(uint8_t area, uint32_t offset, uint32_t size, void *buf) +{ + int os_ret = _buff_bd->read(buf, _area_params[area].address + offset, size); + + if (os_ret) { + return MBED_ERROR_READ_FAILED; + } + + return MBED_SUCCESS; +} + +int TDBStore::write_area(uint8_t area, uint32_t offset, uint32_t size, const void *buf) +{ + int os_ret = _buff_bd->program(buf, _area_params[area].address + offset, size); + if (os_ret) { + return MBED_ERROR_WRITE_FAILED; + } + + return MBED_SUCCESS; +} + +int TDBStore::erase_erase_unit(uint8_t area, uint32_t offset) +{ + uint32_t bd_offset = _area_params[area].address + offset; + uint32_t eu_size = _buff_bd->get_erase_size(bd_offset); + + int os_ret = _buff_bd->erase(bd_offset, eu_size); + if (os_ret) { + return MBED_ERROR_WRITE_FAILED; + } + return MBED_SUCCESS; +} + +void TDBStore::calc_area_params() +{ + size_t bd_size = _bd->size(); + + memset(_area_params, 0, sizeof(_area_params)); + size_t area_0_size = 0; + bd_size_t prev_erase_unit_size = _bd->get_erase_size(area_0_size); + _variant_bd_erase_unit_size = 0; + + while (area_0_size < bd_size / 2) { + bd_size_t erase_unit_size = _bd->get_erase_size(area_0_size); + _variant_bd_erase_unit_size |= (erase_unit_size != prev_erase_unit_size); + area_0_size += erase_unit_size; + } + + _area_params[0].address = 0; + _area_params[0].size = area_0_size; + _area_params[1].address = area_0_size; + _area_params[1].size = bd_size - area_0_size; +} + + +// This function, reading a record from the BD, is used for multiple purposes: +// - Init (scan all records, no need to return file name and data) +// - Get (return file data) +// - Get first/next file (check whether name matches, return name if so) +int TDBStore::read_record(uint8_t area, uint32_t offset, char *key, + void *data_buf, uint32_t data_buf_size, + uint32_t& actual_data_size, size_t data_offset, bool copy_key, + bool copy_data, bool check_expected_key, bool calc_hash, + uint32_t& hash, uint32_t& flags, uint32_t& next_offset) +{ + int ret; + record_header_t header; + uint32_t total_size, key_size, data_size; + uint32_t curr_data_offset; + char *user_key_ptr; + uint32_t crc = initial_crc; + // Upper layers typically use non zero offsets for reading the records chunk by chunk, + // so only validate entire record at first chunk (otherwise we'll have a serious performance penalty). + bool validate = (data_offset == 0); + + ret = MBED_SUCCESS; + // next offset should only be updated to the end of record if successful + next_offset = offset; + + ret = read_area(area, offset, sizeof(header), &header); + if (ret) { + return ret; + } + + if (header.magic != tdbstore_magic) { + return MBED_ERROR_INVALID_DATA_DETECTED; + } + + offset += align_up(sizeof(header), _prog_size); + + key_size = header.key_size; + data_size = header.data_size; + flags = header.flags; + + if ((!key_size) || (key_size >= MAX_KEY_SIZE)) { + return MBED_ERROR_INVALID_DATA_DETECTED; + } + + total_size = key_size + data_size; + + if (offset + total_size >= _size) { + return MBED_ERROR_INVALID_DATA_DETECTED; + } + + if (data_offset > data_size) { + return MBED_ERROR_INVALID_SIZE; + } + + actual_data_size = std::min(data_buf_size, data_size - data_offset); + + if (copy_data && actual_data_size && !data_buf) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + if (validate) { + // Calculate CRC on header (excluding CRC itself) + crc = crc32(crc, sizeof(record_header_t) - sizeof(crc), &header); + curr_data_offset = 0; + } else { + // Non validation case: No need to read the key, nor the parts before data_offset + // or after the actual part requested by the user. + total_size = actual_data_size; + curr_data_offset = data_offset; + offset += data_offset + key_size; + // Mark code that key handling is finished + key_size = 0; + } + + user_key_ptr = key; + hash = initial_crc; + + while (total_size) { + uint8_t *dest_buf; + uint32_t chunk_size; + if (key_size) { + // This means that we're on the key part + if (copy_key) { + dest_buf = reinterpret_cast (user_key_ptr); + chunk_size = key_size; + user_key_ptr[key_size] = '\0'; + } else { + dest_buf = _work_buf; + chunk_size = std::min(key_size, work_buf_size); + } + } else { + // This means that we're on the data part + // We have four cases that need different handling: + // 1. Before data_offset - read to work buffer + // 2. After data_offset, but before actual part is finished - read to user buffer + // 3. After actual part is finished - read to work buffer + // 4. Copy data flag not set - read to work buffer + if (curr_data_offset < data_offset) { + chunk_size = std::min(work_buf_size, data_offset - curr_data_offset); + dest_buf = _work_buf; + } else if (copy_data && (curr_data_offset < data_offset + actual_data_size)) { + chunk_size = actual_data_size; + dest_buf = static_cast (data_buf); + } else { + chunk_size = std::min(work_buf_size, total_size); + dest_buf = _work_buf; + } + } + ret = read_area(area, offset, chunk_size, dest_buf); + if (ret) { + goto end; + } + + if (validate) { + // calculate CRC on current read chunk + crc = crc32(crc, chunk_size, dest_buf); + } + + if (key_size) { + // We're on key part. May need to calculate hash or check whether key is the expected one + if (check_expected_key) { + if (memcmp(user_key_ptr, dest_buf, chunk_size)) { + ret = MBED_ERROR_ITEM_NOT_FOUND; + } + } + + if (calc_hash) { + hash = crc32(hash, chunk_size, dest_buf); + } + + user_key_ptr += chunk_size; + key_size -= chunk_size; + if (!key_size) { + offset += data_offset; + } + } else { + curr_data_offset += chunk_size; + } + + total_size -= chunk_size; + offset += chunk_size; + } + + if (validate && (crc != header.crc)) { + ret = MBED_ERROR_INVALID_DATA_DETECTED; + goto end; + } + + next_offset = align_up(offset, _prog_size); + +end: + return ret; +} + +int TDBStore::find_record(uint8_t area, const char *key, uint32_t& offset, + uint32_t& ram_table_ind, uint32_t& hash) +{ + ram_table_entry_t *ram_table = (ram_table_entry_t *) _ram_table; + ram_table_entry_t *entry; + int ret = MBED_ERROR_ITEM_NOT_FOUND; + uint32_t actual_data_size; + uint32_t flags, dummy_hash, next_offset; + + + hash = crc32(initial_crc, strlen(key), key); + + for (ram_table_ind = 0; ram_table_ind < _num_keys; ram_table_ind++) { + entry = &ram_table[ram_table_ind]; + offset = entry->bd_offset; + if (hash < entry->hash) { + continue; + } + if (hash > entry->hash) { + return MBED_ERROR_ITEM_NOT_FOUND; + } + ret = read_record(_active_area, offset, const_cast (key), 0, 0, actual_data_size, 0, + false, false, true, false, dummy_hash, flags, next_offset); + // not found return code here means that hash doesn't belong to name. Continue searching. + if (ret != MBED_ERROR_ITEM_NOT_FOUND) { + break; + } + } + + return ret; +} + +uint32_t TDBStore::record_size(const char *key, uint32_t data_size) +{ + return align_up(sizeof(record_header_t), _prog_size) + + align_up(strlen(key) + data_size, _prog_size); +} + + +int TDBStore::set_start(set_handle_t *handle, const char *key, size_t final_data_size, + uint32_t create_flags) +{ + int ret; + uint32_t offset; + uint32_t hash, ram_table_ind; + inc_set_handle_t *ih; + + if (!is_valid_key(key)) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + *handle = reinterpret_cast (_inc_set_handle); + ih = reinterpret_cast (*handle); + + if (!strcmp(key, master_rec_key)) { + // Master record - special case (no need to protect by the mutex, as it is already covered + // in the upper layers). + ih->bd_base_offset = _master_record_offset; + ih->new_key = false; + } else { + + _mutex.lock(); + + // A valid magic in the header means that this function has been called after an aborted + // incremental set process. This means that our media may be in a bad state - call GC. + if (ih->header.magic == tdbstore_magic) { + ret = garbage_collection(); + if (ret) { + goto fail; + } + } + + // If we have no room for the record, perform garbage collection + uint32_t rec_size = record_size(key, final_data_size); + if (_free_space_offset + rec_size > _size) { + ret = garbage_collection(); + if (ret) { + goto fail; + } + } + + // If even after GC we have no room for the record, return error + if (_free_space_offset + rec_size > _size) { + ret = MBED_ERROR_MEDIA_FULL; + goto fail; + } + + ret = find_record(_active_area, key, offset, ram_table_ind, hash); + + if (ret == MBED_SUCCESS) { + ret = read_area(_active_area, offset, sizeof(ih->header), &ih->header); + if (ret) { + goto fail; + } + if (ih->header.flags & WRITE_ONCE_FLAG) { + ret = MBED_ERROR_WRITE_PROTECTED; + goto fail; + } + ih->new_key = false; + } else if (ret == MBED_ERROR_ITEM_NOT_FOUND) { + if (create_flags & delete_flag) { + goto fail; + } + if (_num_keys >= _max_keys) { + increment_max_keys(); + } + ih->new_key = true; + } else { + goto fail; + } + ih->bd_base_offset = _free_space_offset; + + check_erase_before_write(_active_area, ih->bd_base_offset, rec_size); + } + + ret = MBED_SUCCESS; + + // Fill handle and header fields + // Jump to offset after header (header will be written at finalize phase) + ih->bd_curr_offset = ih->bd_base_offset + align_up(sizeof(record_header_t), _prog_size); + ih->offset_in_data = 0; + ih->hash = hash; + ih->ram_table_ind = ram_table_ind; + ih->header.magic = tdbstore_magic; + ih->header.header_size = sizeof(record_header_t); + ih->header.revision = tdbstore_revision; + ih->header.flags = create_flags; + ih->header.key_size = strlen(key); + ih->header.reserved = 0; + ih->header.data_size = final_data_size; + // Calculate CRC on header and key + ih->header.crc = crc32(initial_crc, sizeof(record_header_t) - sizeof(ih->header.crc), &ih->header); + ih->header.crc = crc32(ih->header.crc, ih->header.key_size, key); + + // Write key now + ret = write_area(_active_area, ih->bd_curr_offset, ih->header.key_size, key); + if (ret) { + goto fail; + } + ih->bd_curr_offset += ih->header.key_size; + goto end; + +fail: + // mark handle as invalid by clearing magic field in header + ih->header.magic = 0; + _mutex.unlock(); + +end: + return ret; +} + +int TDBStore::set_add_data(set_handle_t handle, const void *value_data, size_t data_size) +{ + int ret = MBED_SUCCESS; + inc_set_handle_t *ih; + + if (handle != _inc_set_handle) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + if (!value_data && data_size) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + _inc_set_mutex.lock(); + + ih = reinterpret_cast (handle); + + if (!ih->header.magic) { + ret = MBED_ERROR_INVALID_ARGUMENT; + goto end; + } + + if (ih->offset_in_data + data_size > ih->header.data_size) { + ret = MBED_ERROR_INVALID_SIZE; + goto end; + } + + // Update CRC with data chunk + ih->header.crc = crc32(ih->header.crc, data_size, value_data); + + // Write the data chunk + ret = write_area(_active_area, ih->bd_curr_offset, data_size, value_data); + if (ret) { + goto end; + } + ih->bd_curr_offset += data_size; + ih->offset_in_data += data_size; + +end: + _inc_set_mutex.unlock(); + return ret; +} + +int TDBStore::set_finalize(set_handle_t handle) +{ + int os_ret, ret = MBED_SUCCESS; + inc_set_handle_t *ih; + ram_table_entry_t *ram_table = (ram_table_entry_t *) _ram_table; + ram_table_entry_t *entry; + + if (handle != _inc_set_handle) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + ih = reinterpret_cast (handle); + + if (!ih->header.magic) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + _inc_set_mutex.lock(); + + if (ih->offset_in_data != ih->header.data_size) { + ret = MBED_ERROR_INVALID_SIZE; + // Need GC as otherwise our storage is left in a non-usable state + garbage_collection(); + goto end; + } + + // Write header + ret = write_area(_active_area, ih->bd_base_offset, sizeof(record_header_t), &ih->header); + if (ret) { + goto end; + } + + // Need to flush buffered BD as our record is totally written now + os_ret = _buff_bd->sync(); + if (os_ret) { + ret = MBED_ERROR_WRITE_FAILED; + goto end; + } + + // In master record case we don't update RAM table + if (ih->bd_base_offset == _master_record_offset) { + goto end; + } + + // Update RAM table + if (ih->header.flags & delete_flag) { + _num_keys--; + if (ih->ram_table_ind < _num_keys) { + memmove(&ram_table[ih->ram_table_ind], &ram_table[ih->ram_table_ind + 1], + sizeof(ram_table_entry_t) * (_num_keys - ih->ram_table_ind)); + } + } else { + if (ih->new_key) { + if (ih->ram_table_ind < _num_keys) { + memmove(&ram_table[ih->ram_table_ind + 1], &ram_table[ih->ram_table_ind], + sizeof(ram_table_entry_t) * (_num_keys - ih->ram_table_ind)); + } + _num_keys++; + } + entry = &ram_table[ih->ram_table_ind]; + entry->hash = ih->hash; + entry->bd_offset = ih->bd_base_offset; + } + + _free_space_offset = align_up(ih->bd_curr_offset, _prog_size); + +end: + // mark handle as invalid by clearing magic field in header + ih->header.magic = 0; + + _inc_set_mutex.unlock(); + if (ih->bd_base_offset != _master_record_offset) { + _mutex.unlock(); + } + return ret; +} + +int TDBStore::set(const char *key, const void *buffer, size_t size, uint32_t create_flags) +{ + int ret; + set_handle_t handle; + + // Don't wait till we get to set_add_data to catch this + if (!buffer && size) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + ret = set_start(&handle, key, size, create_flags); + if (ret) { + return ret; + } + + ret = set_add_data(handle, buffer, size); + if (ret) { + return ret; + } + + ret = set_finalize(handle); + return ret; +} + +int TDBStore::remove(const char *key) +{ + return set(key, 0, 0, delete_flag); +} + +int TDBStore::get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size, size_t offset) +{ + int ret; + uint32_t actual_data_size; + uint32_t bd_offset, next_bd_offset; + uint32_t flags, hash, ram_table_ind; + + if (!is_valid_key(key)) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + _mutex.lock(); + + ret = find_record(_active_area, key, bd_offset, ram_table_ind, hash); + + if (ret != MBED_SUCCESS) { + goto end; + } + + ret = read_record(_active_area, bd_offset, const_cast(key), buffer, buffer_size, + actual_data_size, offset, false, true, false, false, hash, flags, next_bd_offset); + + if (actual_size) { + *actual_size = actual_data_size; + } + +end: + _mutex.unlock(); + return ret; +} + +int TDBStore::get_info(const char *key, info_t *info) +{ + int ret; + uint32_t bd_offset, next_bd_offset; + uint32_t flags, hash, ram_table_ind; + uint32_t actual_data_size; + + if (!is_valid_key(key)) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + _mutex.lock(); + + ret = find_record(_active_area, key, bd_offset, ram_table_ind, hash); + + if (ret) { + goto end; + } + + // Give a large dummy buffer size in order to achieve actual data size + // (as copy_data flag is not set, data won't be copied anywhere) + ret = read_record(_active_area, bd_offset, const_cast(key), 0, (uint32_t) -1, + actual_data_size, 0, false, false, false, false, hash, flags, + next_bd_offset); + + if (ret) { + goto end; + } + + if (info) { + info->flags = flags; + info->size = actual_data_size; + } + +end: + _mutex.unlock(); + return ret; +} + +int TDBStore::write_master_record(uint8_t area, uint16_t version, uint32_t& next_offset) +{ + master_record_data_t master_rec; + + master_rec.version = version; + master_rec.tdbstore_revision = tdbstore_revision; + master_rec.reserved = 0; + next_offset = _master_record_offset + record_size(master_rec_key, sizeof(master_rec)); + return set(master_rec_key, &master_rec, sizeof(master_rec), 0); +} + +int TDBStore::copy_record(uint8_t from_area, uint32_t from_offset, uint32_t to_offset, + uint32_t& to_next_offset) +{ + int ret; + record_header_t header; + uint32_t total_size; + uint16_t chunk_size; + + ret = read_area(from_area, from_offset, sizeof(header), &header); + if (ret) { + return ret; + } + + total_size = align_up(sizeof(record_header_t), _prog_size) + + align_up(header.key_size + header.data_size, _prog_size);; + + + ret = check_erase_before_write(1 - from_area, to_offset, total_size); + if (ret) { + return ret; + } + + chunk_size = align_up(sizeof(record_header_t), _prog_size); + ret = write_area(1 - from_area, to_offset, chunk_size, &header); + if (ret) { + return ret; + } + + from_offset += chunk_size; + to_offset += chunk_size; + total_size -= chunk_size; + + while (total_size) { + chunk_size = std::min(total_size, work_buf_size); + ret = read_area(from_area, from_offset, chunk_size, _work_buf); + if (ret) { + return ret; + } + + ret = write_area(1 - from_area, to_offset, chunk_size, _work_buf); + if (ret) { + return ret; + } + + from_offset += chunk_size; + to_offset += chunk_size; + total_size -= chunk_size; + } + + to_next_offset = align_up(to_offset, _prog_size); + return MBED_SUCCESS; +} + +int TDBStore::copy_all_records(uint8_t from_area, uint32_t to_offset, + uint32_t& to_next_offset) +{ + return MBED_SUCCESS; +} + +int TDBStore::garbage_collection() +{ + ram_table_entry_t *ram_table = (ram_table_entry_t *) _ram_table; + uint32_t to_offset, to_next_offset; + uint32_t chunk_size, reserved_size; + int ret; + size_t ind; + + ret = check_erase_before_write(1 - _active_area, 0, _master_record_offset); + if (ret) { + return ret; + } + + // Copy reserved data + to_offset = 0; + reserved_size = RESERVED_AREA_SIZE; + + while (reserved_size) { + chunk_size = std::min(work_buf_size, reserved_size); + ret = read_area(_active_area, to_offset, chunk_size, _work_buf + to_offset); + if (ret) { + return ret; + } + ret = write_area(1 - _active_area, to_offset, chunk_size, _work_buf + to_offset); + if (ret) { + return ret; + } + to_offset += chunk_size; + reserved_size -= chunk_size; + } + + + to_offset = _master_record_offset + record_size(master_rec_key, sizeof(master_record_data_t)); + + // Initialize in case table is empty + to_next_offset = to_offset; + + // Go over ram table and copy all entries to opposite area + for (ind = 0; ind < _num_keys; ind++) { + uint32_t from_offset = ram_table[ind].bd_offset; + ret = copy_record(_active_area, from_offset, to_offset, to_next_offset); + if (ret) { + return ret; + } + // Update RAM table + ram_table[ind].bd_offset = to_offset; + to_offset = to_next_offset; + } + + to_offset = to_next_offset; + _free_space_offset = to_next_offset; + + // Now we can switch to the new active area + _active_area = 1 - _active_area; + + // Now write master record, with version incremented by 1. + _active_area_version++; + ret = write_master_record(_active_area, _active_area_version, to_offset); + if (ret) { + return ret; + } + + // Now reset standby area + ret = reset_area(1 - _active_area); + if (ret) { + return ret; + } + + return MBED_SUCCESS; +} + + +int TDBStore::build_ram_table() +{ + ram_table_entry_t *ram_table = (ram_table_entry_t *) _ram_table; + uint32_t offset, next_offset = 0, dummy; + int ret = MBED_SUCCESS; + uint32_t hash; + uint32_t flags; + uint32_t actual_data_size; + uint32_t ram_table_ind; + + _num_keys = 0; + offset = _master_record_offset; + + while (offset < _free_space_offset) { + ret = read_record(_active_area, offset, _key_buf, 0, 0, actual_data_size, 0, + true, false, false, true, hash, flags, next_offset); + + if (ret) { + goto end; + } + + ret = find_record(_active_area, _key_buf, dummy, ram_table_ind, hash); + + if ((ret != MBED_SUCCESS) && (ret != MBED_ERROR_ITEM_NOT_FOUND)) { + goto end; + } + + uint32_t save_offset = offset; + offset = next_offset; + + if (ret == MBED_ERROR_ITEM_NOT_FOUND) { + // Key doesn't exist, need to add it to RAM table + + if (flags & delete_flag) { + continue; + } + if (_num_keys >= _max_keys) { + // In order to avoid numerous reallocations of ram table, + // Add a chunk of entries now + increment_max_keys(reinterpret_cast(&ram_table)); + } + memmove(&ram_table[ram_table_ind + 1], &ram_table[ram_table_ind], + sizeof(ram_table_entry_t) * (_num_keys - ram_table_ind)); + + _num_keys++; + } else if (flags & delete_flag) { + _num_keys--; + memmove(&ram_table[ram_table_ind], &ram_table[ram_table_ind + 1], + sizeof(ram_table_entry_t) * (_num_keys - ram_table_ind)); + + continue; + } + + // update record parameters + ram_table[ram_table_ind].hash = hash; + ram_table[ram_table_ind].bd_offset = save_offset; + } + +end: + _free_space_offset = next_offset; + return ret; +} + +int TDBStore::increment_max_keys(void **ram_table) +{ + // Reallocate ram table with new size + ram_table_entry_t *old_ram_table = (ram_table_entry_t *) _ram_table; + ram_table_entry_t *new_ram_table = new ram_table_entry_t[_max_keys + 1]; + + // Copy old content to new table + memcpy(new_ram_table, old_ram_table, sizeof(ram_table_entry_t) * _max_keys); + _max_keys++; + + _ram_table = new_ram_table; + delete[] old_ram_table; + + if (ram_table) { + *ram_table = _ram_table; + } + return MBED_SUCCESS; +} + + +int TDBStore::init() +{ + ram_table_entry_t *ram_table; + area_state_e area_state[_num_areas]; + uint32_t next_offset; + uint32_t flags, hash; + uint32_t actual_data_size; + int os_ret, ret = MBED_SUCCESS; + uint16_t versions[_num_areas]; + + _mutex.lock(); + + _max_keys = initial_max_keys; + + ram_table = new ram_table_entry_t[_max_keys]; + _ram_table = ram_table; + _num_keys = 0; + + // Underlying BD size must fit into 32 bits + if ((uint32_t)_bd->size() != _bd->size()) { + MBED_ERROR(MBED_ERROR_INVALID_SIZE, "Underlying BD size should not exceed 32 bits"); + } + + // Underlying BD must have flash attributes, i.e. have an erase value + if (_bd->get_erase_value() == -1) { + MBED_ERROR(MBED_ERROR_INVALID_ARGUMENT, "Underlying BD must have flash attributes"); + } + + _size = (size_t) -1; + + _buff_bd = new BufferedBlockDevice(_bd); + _buff_bd->init(); + + _prog_size = _bd->get_program_size(); + _work_buf = new uint8_t[work_buf_size]; + _key_buf = new char[MAX_KEY_SIZE]; + _inc_set_handle = new inc_set_handle_t; + memset(_inc_set_handle, 0, sizeof(inc_set_handle_t)); + + _master_record_offset = align_up(RESERVED_AREA_SIZE, _prog_size); + + calc_area_params(); + + for (uint8_t area = 0; area < _num_areas; area++) { + area_state[area] = TDBSTORE_AREA_STATE_NONE; + versions[area] = 0; + + _size = std::min(_size, _area_params[area].size); + + // Check validity of master record + master_record_data_t master_rec; + ret = read_record(area, _master_record_offset, const_cast (master_rec_key), + &master_rec, sizeof(master_rec), actual_data_size, 0, false, true, true, false, + hash, flags, next_offset); + if ((ret != MBED_SUCCESS) && (ret != MBED_ERROR_INVALID_DATA_DETECTED)) { + MBED_ERROR(ret, "TDBSTORE: Unable to read record at init"); + } + + // Master record may be corrupt, but it can be erased. Now check if its entire erase unit is erased + if (ret == MBED_ERROR_INVALID_DATA_DETECTED) { + bool erased; + uint32_t erase_unit_num = _master_record_offset / _bd->get_erase_size(_master_record_offset); + if (is_erase_unit_erased(area, erase_unit_num, erased)) { + MBED_ERROR(MBED_ERROR_READ_FAILED, "TDBSTORE: Unable to check whether erase unit is erased at init"); + } + if (erased) { + area_state[area] = TDBSTORE_AREA_STATE_EMPTY; + continue; + } + } + + // We have a non valid master record, just reset the area. + if (ret == MBED_ERROR_INVALID_DATA_DETECTED) { + ret = reset_area(area); + if (ret) { + MBED_ERROR(ret, "TDBSTORE: Unable to reset area at init"); + } + area_state[area] = TDBSTORE_AREA_STATE_EMPTY; + continue; + } + versions[area] = master_rec.version; + + area_state[area] = TDBSTORE_AREA_STATE_VALID; + + // Unless both areas are valid (a case handled later), getting here means + // that we found our active area. + _active_area = area; + _active_area_version = versions[area]; + } + + // In case we have two empty areas, arbitrarily use area 0 as the active one. + if ((area_state[0] == TDBSTORE_AREA_STATE_EMPTY) && (area_state[1] == TDBSTORE_AREA_STATE_EMPTY)) { + _active_area = 0; + _active_area_version = 1; + ret = write_master_record(_active_area, _active_area_version, _free_space_offset); + if (ret) { + MBED_ERROR(ret, "TDBSTORE: Unable to write master record at init"); + } + // Nothing more to do here if active area is empty + goto end; + } + + // In case we have two valid areas, choose the one having the higher version (or 0 + // in case of wrap around). Erase the other one. + if ((area_state[0] == TDBSTORE_AREA_STATE_VALID) && (area_state[1] == TDBSTORE_AREA_STATE_VALID)) { + if ((versions[0] > versions[1]) || (!versions[0])) { + _active_area = 0; + } else { + _active_area = 1; + } + _active_area_version = versions[_active_area]; + ret = erase_erase_unit(1 - _active_area, 0); + if (ret) { + MBED_ERROR(ret, "TDBSTORE: Unable to erase media start at init"); + } + } + + // Currently set free space offset pointer to the end of free space. + // Ram table build process needs it, but will update it. + _free_space_offset = _size; + ret = build_ram_table(); + + if ((ret != MBED_SUCCESS) && (ret != MBED_ERROR_INVALID_DATA_DETECTED)) { + MBED_ERROR(ret, "TDBSTORE: Unable to build RAM table at init"); + } + + if ((ret == MBED_ERROR_INVALID_DATA_DETECTED) && (_free_space_offset < _size)) { + // Space after last valid record may be erased, hence "corrupt". Now check if it really is erased. + bool erased; + if (is_erase_unit_erased(_active_area, _free_space_offset, erased)) { + MBED_ERROR(MBED_ERROR_READ_FAILED, "TDBSTORE: Unable to check whether erase unit is erased at init"); + } + if (erased) { + // Erased - all good + ret = MBED_SUCCESS; + } + } + + // If we have a corrupt record somewhere, perform garbage collection to salvage + // all preceding records + if (ret == MBED_ERROR_INVALID_DATA_DETECTED) { + ret = garbage_collection(); + if (ret) { + MBED_ERROR(ret, "TDBSTORE: Unable to perform GC at init"); + } + os_ret = _buff_bd->sync(); + if (os_ret) { + MBED_ERROR(MBED_ERROR_WRITE_FAILED, "TDBSTORE: Unable to sync BD at init"); + } + } + +end: + _is_initialized = true; + _mutex.unlock(); + return ret; +} + +int TDBStore::deinit() +{ + _mutex.lock(); + if (_is_initialized) { + _buff_bd->deinit(); + delete _buff_bd; + + ram_table_entry_t *ram_table = (ram_table_entry_t *) _ram_table; + delete[] ram_table; + delete[] _work_buf; + delete[] _key_buf; + } + + _is_initialized = false; + _mutex.unlock(); + + return MBED_SUCCESS; +} + +int TDBStore::reset_area(uint8_t area) +{ + int ret; + uint32_t bd_offset = 0; + + // Erase reserved area and master record + do { + ret = erase_erase_unit(area, bd_offset); + if (ret) { + return ret; + } + bd_offset += _buff_bd->get_erase_size(bd_offset); + } while (bd_offset <= _master_record_offset); + + return MBED_SUCCESS; +} + +int TDBStore::reset() +{ + uint8_t area; + int ret; + + if (!_is_initialized) { + return MBED_ERROR_NOT_READY; + } + + _mutex.lock(); + + // Reset both areas + for (area = 0; area < _num_areas; area++) { + ret = reset_area(area); + if (ret) { + goto end; + } + } + + _active_area = 0; + _num_keys = 0; + _free_space_offset = _master_record_offset; + _active_area_version = 1; + + // Write an initial master record on active area + ret = write_master_record(_active_area, _active_area_version, _free_space_offset); + +end: + _mutex.unlock(); + return ret; +} + +int TDBStore::iterator_open(iterator_t *it, const char *prefix) +{ + key_iterator_handle_t *handle; + + if (!_is_initialized) { + return MBED_ERROR_NOT_READY; + } + + if (!it) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + _mutex.lock(); + + handle = new key_iterator_handle_t; + *it = reinterpret_cast(handle); + + if (prefix && strcmp(prefix, "")) { + handle->prefix = new char[strlen(prefix) + 1]; + strcpy(handle->prefix, prefix); + } else { + handle->prefix = 0; + } + handle->ram_table_ind = 0; + + _mutex.unlock(); + + return MBED_SUCCESS; +} + +int TDBStore::iterator_next(iterator_t it, char *key, size_t key_size) +{ + ram_table_entry_t *ram_table = (ram_table_entry_t *) _ram_table; + key_iterator_handle_t *handle; + int ret; + uint32_t actual_data_size, hash, flags, next_offset; + + if (!_is_initialized) { + return MBED_ERROR_NOT_READY; + } + + _mutex.lock(); + + handle = reinterpret_cast(it); + + ret = MBED_ERROR_ITEM_NOT_FOUND; + + while (ret && (handle->ram_table_ind < _num_keys)) { + ret = read_record(_active_area, ram_table[handle->ram_table_ind].bd_offset, _key_buf, + 0, 0, actual_data_size, 0, true, false, false, false, hash, flags, next_offset); + if (ret) { + goto end; + } + if (!handle->prefix || (strstr(_key_buf, handle->prefix) == _key_buf)) { + if (strlen(_key_buf) >= key_size) { + ret = MBED_ERROR_INVALID_SIZE; + goto end; + } + strcpy(key, _key_buf); + } else { + ret = MBED_ERROR_ITEM_NOT_FOUND; + } + handle->ram_table_ind++; + } + +end: + _mutex.unlock(); + return ret; +} + +int TDBStore::iterator_close(iterator_t it) +{ + key_iterator_handle_t *handle; + + if (!_is_initialized) { + return MBED_ERROR_NOT_READY; + } + + _mutex.lock(); + + handle = reinterpret_cast(it); + delete[] handle->prefix; + delete handle; + + _mutex.unlock(); + + return MBED_SUCCESS; +} + +int TDBStore::reserved_data_set(const void *reserved_data, size_t reserved_data_buf_size) +{ + uint32_t check_size = RESERVED_AREA_SIZE, chunk_size, offset = 0; + uint8_t blank = _buff_bd->get_erase_value(); + int os_ret, ret = MBED_SUCCESS; + + if (reserved_data_buf_size > RESERVED_AREA_SIZE) { + return MBED_ERROR_INVALID_SIZE; + } + + _mutex.lock(); + + while (check_size) { + chunk_size = std::min(work_buf_size, (uint32_t) check_size); + ret = read_area(_active_area, offset, chunk_size, _work_buf + offset); + if (ret) { + goto end; + } + for (uint32_t i = 0; i < chunk_size; i++) { + if (_work_buf[i] != blank) { + ret = MBED_ERROR_WRITE_FAILED; + goto end; + } + } + offset += chunk_size; + check_size -= chunk_size; + } + + ret = write_area(_active_area, 0, reserved_data_buf_size, reserved_data); + if (ret) { + goto end; + } + + os_ret = _buff_bd->sync(); + if (os_ret) { + ret = MBED_ERROR_WRITE_FAILED; + } + +end: + _mutex.unlock(); + return ret; +} + +int TDBStore::reserved_data_get(void *reserved_data, size_t reserved_data_buf_size) +{ + _mutex.lock(); + if (reserved_data_buf_size > RESERVED_AREA_SIZE) { + reserved_data_buf_size = RESERVED_AREA_SIZE; + } + + int ret = read_area(_active_area, 0, reserved_data_buf_size, reserved_data); + + _mutex.unlock(); + return ret; +} + + +void TDBStore::offset_in_erase_unit(uint8_t area, uint32_t offset, + uint32_t& offset_from_start, uint32_t& dist_to_end) +{ + uint32_t bd_offset = _area_params[area].address + offset; + if (!_variant_bd_erase_unit_size) { + uint32_t eu_size = _bd->get_erase_size(); + offset_from_start = bd_offset % eu_size; + dist_to_end = eu_size - offset_from_start; + return; + } + + uint32_t agg_offset = 0; + while (bd_offset < agg_offset + _bd->get_erase_size(agg_offset)) { + agg_offset += _bd->get_erase_size(agg_offset); + } + offset_from_start = bd_offset - agg_offset; + dist_to_end = _bd->get_erase_size(agg_offset) - offset_from_start; + +} + +int TDBStore::is_erase_unit_erased(uint8_t area, uint32_t offset, bool& erased) +{ + uint32_t offset_from_start, dist; + offset_in_erase_unit(area, offset, offset_from_start, dist); + uint8_t buf[sizeof(record_header_t)], blanks[sizeof(record_header_t)]; + memset(blanks, _bd->get_erase_value(), sizeof(blanks)); + + while (dist) { + uint32_t chunk = std::min(dist, (uint32_t) sizeof(buf)); + int ret = read_area(area, offset, chunk, buf); + if (ret) { + return MBED_ERROR_READ_FAILED; + } + if (memcmp(buf, blanks, chunk)) { + erased = false; + return MBED_SUCCESS; + } + offset += chunk; + dist -= chunk; + } + erased = true; + return MBED_SUCCESS; +} + +int TDBStore::check_erase_before_write(uint8_t area, uint32_t offset, uint32_t size) +{ + // In order to save init time, we don't check that the entire area is erased. + // Instead, whenever reaching an erase unit start, check that it's erased, and if not - + // erase it. This is very not likely to happen (assuming area was initialized + // by TDBStore). This can be achieved as all records (except for the master record + // in offset 0) are written in an ascending order. + + if (!offset) { + // Master record in offset 0 is a special case - don't check it + return MBED_SUCCESS; + } + + while (size) { + uint32_t dist, offset_from_start; + int ret; + offset_in_erase_unit(area, offset, offset_from_start, dist); + uint32_t chunk = std::min(size, dist); + + if (!offset_from_start) { + // We're at the start of an erase unit. Here (and only here), check if it's erased. + bool erased; + ret = is_erase_unit_erased(area, offset, erased); + if (ret) { + return MBED_ERROR_WRITE_FAILED; + } + if (!erased) { + ret = erase_erase_unit(area, offset); + if (ret) { + return MBED_ERROR_WRITE_FAILED; + } + } + } + offset += chunk; + size -= chunk; + } + return MBED_SUCCESS; +} diff --git a/features/storage/kvstore/tdbstore/TDBStore.h b/features/storage/kvstore/tdbstore/TDBStore.h new file mode 100644 index 00000000000..1a5e3cf94ba --- /dev/null +++ b/features/storage/kvstore/tdbstore/TDBStore.h @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2018 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 MBED_TDBSTORE_H +#define MBED_TDBSTORE_H + +#include +#include +#include "KVStore.h" +#include "BlockDevice.h" +#include "BufferedBlockDevice.h" +#include "PlatformMutex.h" + +namespace mbed { + +/** TDBStore class + * + * Lightweight Key Value storage over a block device + */ + +class TDBStore : public KVStore { +public: + + static const uint32_t RESERVED_AREA_SIZE = 64; + + /** + * @brief Class constructor + * + * @param[in] bd Underlying block device. + * @param[in] max_keys Maximum number of keys (0 if unlimited). + * + * @returns none + */ + TDBStore(BlockDevice *bd); + + /** + * @brief Class destructor + * + * @returns none + */ + virtual ~TDBStore(); + + /** + * @brief Initialize KVStore + * + * @returns 0 on success or a negative error code on failure + */ + virtual int init(); + + /** + * @brief Deinitialize KVStore + * + * @returns 0 on success or a negative error code on failure + */ + virtual int deinit(); + + + /** + * @brief Reset KVStore contents (clear all keys) + * Warning: This function is not thread safe. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int reset(); + + /** + * @brief Set one KVStore item, given key and value. + * + * @param[in] key Key. + * @param[in] buffer Value data buffer. + * @param[in] size Value data size. + * @param[in] create_flags Flag mask. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int set(const char *key, const void *buffer, size_t size, uint32_t create_flags); + + /** + * @brief Get one KVStore item, given key. + * + * @param[in] key Key. + * @param[in] buffer Value data buffer. + * @param[in] buffer_size Value data buffer size. + * @param[out] actual_size Actual read size. + * @param[in] offset Offset to read from in data. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size = NULL, + size_t offset = 0); + + /** + * @brief Get information of a given key. + * + * @param[in] key Key. + * @param[out] info Returned information structure. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int get_info(const char *key, info_t *info); + + /** + * @brief Remove a KVStore item, given key. + * + * @param[in] key Key. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int remove(const char *key); + + + /** + * @brief Start an incremental KVStore set sequence. + * + * @param[out] handle Returned incremental set handle. + * @param[in] key Key. + * @param[in] final_data_size Final value data size. + * @param[in] create_flags Flag mask. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int set_start(set_handle_t *handle, const char *key, size_t final_data_size, uint32_t create_flags); + + /** + * @brief Add data to incremental KVStore set sequence. + * + * @param[in] handle Incremental set handle. + * @param[in] value_data value data to add. + * @param[in] data_size value data size. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int set_add_data(set_handle_t handle, const void *value_data, size_t data_size); + + /** + * @brief Finalize an incremental KVStore set sequence. + * + * @param[in] handle Incremental set handle. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int set_finalize(set_handle_t handle); + + /** + * @brief Start an iteration over KVStore keys. + * + * @param[out] it Returned iterator handle. + * @param[in] prefix Key prefix (null for all keys). + * + * @returns 0 on success or a negative error code on failure + */ + virtual int iterator_open(iterator_t *it, const char *prefix = NULL); + + /** + * @brief Get next key in iteration. + * + * @param[in] it Iterator handle. + * @param[in] key Buffer for returned key. + * @param[in] key_size Key buffer size. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int iterator_next(iterator_t it, char *key, size_t key_size); + + /** + * @brief Close iteration. + * + * @param[in] it Iterator handle. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int iterator_close(iterator_t it); + + /** + * @brief Set data in reserved area. + * + * @param[in] reserved_data Reserved data buffer. + * @param[in] reserved_data_buf_size + * Reserved data buffer size. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int reserved_data_set(const void *reserved_data, size_t reserved_data_buf_size); + + /** + * @brief Get data from reserved area. + * + * @param[in] reserved_data Reserved data buffer. + * @param[in] reserved_data_buf_size + * Reserved data buffer size. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int reserved_data_get(void *reserved_data, size_t reserved_data_buf_size); + +private: + + typedef struct { + uint32_t address; + size_t size; + } tdbstore_area_data_t; + + static const int _num_areas = 2; + + PlatformMutex _mutex; + PlatformMutex _inc_set_mutex; + void *_ram_table; + size_t _max_keys; + size_t _num_keys; + BlockDevice *_bd; + BufferedBlockDevice *_buff_bd; + uint32_t _free_space_offset; + uint32_t _master_record_offset; + bool _is_initialized; + int _active_area; + uint16_t _active_area_version; + size_t _size; + tdbstore_area_data_t _area_params[_num_areas]; + uint32_t _prog_size; + uint8_t *_work_buf; + char *_key_buf; + bool _variant_bd_erase_unit_size; + void *_inc_set_handle; + + /** + * @brief Read a block from an area. + * + * @param[in] area Area. + * @param[in] offset Offset in area. + * @param[in] size Number of bytes to read. + * @param[in] buf Output buffer. + * + * @returns 0 for success, nonzero for failure. + */ + int read_area(uint8_t area, uint32_t offset, uint32_t size, void *buf); + + /** + * @brief Write a block to an area. + * + * @param[in] area Area. + * @param[in] offset Offset in area. + * @param[in] size Number of bytes to write. + * @param[in] buf Input buffer. + * + * @returns 0 for success, non-zero for failure. + */ + int write_area(uint8_t area, uint32_t offset, uint32_t size, const void *buf); + + /** + * @brief Reset an area (erase its start). + * + * @param[in] area Area. + * + * @returns 0 for success, nonzero for failure. + */ + int reset_area(uint8_t area); + + /** + * @brief Erase an erase unit. + * + * @param[in] area Area. + * @param[in] offset Offset in area. + * + * @returns 0 for success, non-zero for failure. + */ + int erase_erase_unit(uint8_t area, uint32_t offset); + + /** + * @brief Calculate addresses and sizes of areas. + */ + void calc_area_params(); + + /** + * @brief Read a TDBStore record from a given location. + * + * @param[in] area Area. + * @param[in] offset Offset of record in area. + * @param[in] key Key. + * @param[in] data_buf Data buffer. + * @param[in] data_buf_size Data buffer size. + * @param[out] actual_data_size Actual data size. + * @param[in] data_offset Offset in data. + * @param[in] copy_key Copy key to user buffer. + * @param[in] copy_data Copy data to user buffer. + * @param[in] check_expected_key Check whether key belongs to this record. + * @param[in] calc_hash Calculate hash (on key). + * @param[out] hash Calculated hash. + * @param[out] flags Record flags. + * @param[out] next_offset Offset of next record. + * + * @returns 0 for success, nonzero for failure. + */ + int read_record(uint8_t area, uint32_t offset, char *key, + void *data_buf, uint32_t data_buf_size, + uint32_t& actual_data_size, size_t data_offset, bool copy_key, + bool copy_data, bool check_expected_key, bool calc_hash, + uint32_t& hash, uint32_t& flags, uint32_t& next_offset); + + /** + * @brief Write a master record of a given area. + * + * @param[in] area Area. + * @param[in] version Area version. + * @param[out] next_offset Offset of next record. + * + * @returns 0 for success, nonzero for failure. + */ + int write_master_record(uint8_t area, uint16_t version, uint32_t& next_offset); + + /** + * @brief Copy a record from one area to the opposite one. + * + * @param[in] from_area Area to copy record from. + * @param[in] from_offset Offset in source area. + * @param[in] to_offset Offset in destination area. + * @param[out] to_next_offset Offset of next record in destination area. + * + * @returns 0 for success, nonzero for failure. + */ + int copy_record(uint8_t from_area, uint32_t from_offset, uint32_t to_offset, + uint32_t& to_next_offset); + + /** + * @brief As part of GC process, copy all records in RAM table to the opposite area. + * + * @param[in] from_area Area to copy record from. + * @param[in] to_offset Offset in destination area. + * @param[out] to_next_offset Offset of next record in destination area. + * + * @returns 0 for success, nonzero for failure. + */ + int copy_all_records(uint8_t from_area, uint32_t to_offset, uint32_t& to_next_offset); + + /** + * @brief Garbage collection (compact all records from active area to the standby one). + * + * @returns 0 for success, nonzero for failure. + */ + int garbage_collection(); + + /** + * @brief Return record size given key and data size. + * + * @param[in] key Key. + * @param[in] data_size Data size. + * + * @returns record size. + */ + uint32_t record_size(const char *key, uint32_t data_size); + + /** + * @brief Find a record given key + * + * @param[in] area Area. + * @param[in] key Key. + * @param[out] offset Offset of record. + * @param[out] ram_table_ind Index in ram table (target one if not found). + * @param[out] hash Calculated key hash. + * + * @returns 0 for success, nonzero for failure. + */ + int find_record(uint8_t area, const char *key, uint32_t& offset, + uint32_t& ram_table_ind, uint32_t& hash); + /** + * @brief Actual logics of get API (covers also all other get APIs). + * + * @param[in] key Key. + * @param[in] copy_data Copy data to user buffer. + * @param[in] data_buf Buffer to store data on. + * @param[in] data_buf_size Data buffer size (bytes). + * @param[out] actual_data_size Actual data size (bytes). + * @param[out] flags Flags. + * + * @returns 0 for success, nonzero for failure. + */ + int do_get(const char *key, bool copy_data, + void *data_buf, uint32_t data_buf_size, uint32_t& actual_data_size, + uint32_t& flags); + + /** + * @brief Actual logics of set API (covers also the remove API). + * + * @param[in] key Key. + * @param[in] data_buf Data buffer. + * @param[in] data_buf_size Data buffer size (bytes). + * @param[in] flags Flags. + * + * @returns 0 for success, nonzero for failure. + */ + int do_set(const char *key, const void *data_buf, uint32_t data_buf_size, uint32_t flags); + + /** + * @brief Build RAM table and update _free_space_offset (scanning all the records in the area). + * + * @returns 0 for success, nonzero for failure. + */ + int build_ram_table(); + + /** + * @brief Increment max number of keys and reallocate ram table accordingly. + * + * @param[out] ram_table Updated ram table. + * + * @returns 0 for success, nonzero for failure. + */ + int increment_max_keys(void **ram_table = 0); + + /** + * @brief Calculate offset from start of erase unit. + * + * @param[in] area Area. + * @param[in] offset Offset in area. + * @param[out] offset_from_start Offset from start of erase unit. + * @param[out] dist_to_end Distance to end of erase unit. + * + * @returns offset in erase unit. + */ + void offset_in_erase_unit(uint8_t area, uint32_t offset, uint32_t& offset_from_start, + uint32_t& dist_to_end); + + /** + * @brief Check whether erase unit is erased (from offset until end of unit). + * + * @param[in] area Area. + * @param[in] offset Offset in area. + * @param[out] erased Unit is erased. + * + * @returns 0 for success, nonzero for failure. + */ + int is_erase_unit_erased(uint8_t area, uint32_t offset, bool& erased); + + /** + * @brief Before writing a record, check whether we are crossing an erase unit. + * If we do, check if it's erased, and erase it if not. + * + * @param[in] area Area. + * @param[in] offset Offset in area. + * @param[in] size Write size. + * + * @returns 0 for success, nonzero for failure. + */ + int check_erase_before_write(uint8_t area, uint32_t offset, uint32_t size); +}; +/** @}*/ + +} // namespace mbed + +#endif From 4b6a2fe99f12d7cfe8cadeb50f105d69017f569b Mon Sep 17 00:00:00 2001 From: David Saada Date: Wed, 7 Nov 2018 15:04:22 +0200 Subject: [PATCH 45/45] Implement SecureStore class --- .../kvstore/securestore_whitebox/main.cpp | 520 +++++++++++ .../kvstore/securestore/SecureStore.cpp | 867 ++++++++++++++++++ .../storage/kvstore/securestore/SecureStore.h | 213 +++++ .../storage/kvstore/securestore/mbed_lib.json | 6 + 4 files changed, 1606 insertions(+) create mode 100644 features/storage/TESTS/kvstore/securestore_whitebox/main.cpp create mode 100644 features/storage/kvstore/securestore/SecureStore.cpp create mode 100644 features/storage/kvstore/securestore/SecureStore.h create mode 100644 features/storage/kvstore/securestore/mbed_lib.json diff --git a/features/storage/TESTS/kvstore/securestore_whitebox/main.cpp b/features/storage/TESTS/kvstore/securestore_whitebox/main.cpp new file mode 100644 index 00000000000..f19d1877368 --- /dev/null +++ b/features/storage/TESTS/kvstore/securestore_whitebox/main.cpp @@ -0,0 +1,520 @@ +/* +* Copyright (c) 2018 ARM Limited. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 +* Licensed under the Apache License, Version 2.0 (the License); you may +* not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an AS IS BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "SecureStore.h" +#include "TDBStore.h" +#ifdef MBED_CONF_RTOS_PRESENT +#include "Thread.h" +#endif +#include "mbed_error.h" +#include "Timer.h" +#include "HeapBlockDevice.h" +#include "FlashSimBlockDevice.h" +#include "SlicingBlockDevice.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest/utest.h" +#include +#include +#include +#include + +#define LOCAL_TEST + +// MBED_TEST_SIM_BLOCKDEVICE is used here only to filter out inappropriate boards (little RAM etc.) +#if !defined(MBED_TEST_SIM_BLOCKDEVICE) && !defined(LOCAL_TEST) +#error [NOT_SUPPORTED] KVStore test not supported on this platform +#endif + +using namespace mbed; + +const size_t ul_bd_size = 16 * 4096; +const size_t rbp_bd_size = 4 * 4096; + +#undef TEST_SPIF +#undef TEST_FSSTORE_UL +#undef NO_RBP_MODE + +#ifdef TEST_SPIF +#include "SPIFBlockDevice.h" +SPIFBlockDevice flash_bd(MBED_CONF_SPIF_DRIVER_SPI_MOSI, MBED_CONF_SPIF_DRIVER_SPI_MISO, + MBED_CONF_SPIF_DRIVER_SPI_CLK, MBED_CONF_SPIF_DRIVER_SPI_CS); +SlicingBlockDevice ul_bd(&flash_bd, 0, ul_bd_size); +SlicingBlockDevice rbp_bd(&flash_bd, ul_bd_size, ul_bd_size + rbp_bd_size); +#else +HeapBlockDevice bd(ul_bd_size + rbp_bd_size, 1, 1, 4096); +FlashSimBlockDevice flash_bd(&bd); +SlicingBlockDevice ul_bd(&flash_bd, 0, ul_bd_size); +SlicingBlockDevice rbp_bd(&flash_bd, ul_bd_size, ul_bd_size + rbp_bd_size); +#endif + +#ifdef TEST_FSSTORE_UL +#include "LittleFileSystem.h" +#include "FileSystemStore.h" +#endif + +using namespace utest::v1; + +static const char *const key1 = "key1"; +static const char *const key1_val1 = "val1"; +static const char *const key2 = "name_of_key2"; +static const char *const key2_val1 = "val3"; +static const char *const key2_val2 = "val2 of key 2"; +static const char *const key2_val3 = "Val1 value of key 2 "; +static const char *const key3 = "This_is_the_name_of_key3"; +static const char *const key3_val1 = "Data value of key 3 is the following"; +static const char *const key3_val2 = "Unfollow"; +static const char *const key4 = "This_is_the_name_of_key4"; +static const char *const key4_val1 = "Is this the value of key 4?"; +static const char *const key4_val2 = "What the hell is the value of key 4, god damn it!"; +static const char *const key5 = "This_is_the_real_name_of_Key5"; +static const char *const key5_val1 = "Key 5 value that should definitely be written"; +static const char *const key5_val2 = "Key 5 value that should definitely not be written"; +static const char *const key6 = "Key6_name"; +static const char *const key6_val1 = "Value 1 of key6"; +static const char *const key6_val2 = "Value 2 of key6. That's it."; +static const char *const key7 = "Key7"; +static const char *const key7_val1 = "7 is a lucky number"; + +static void white_box_test() +{ + uint8_t get_buf[256]; + size_t actual_data_size; + int result; + mbed::Timer timer; + int elapsed; + KVStore::info_t info; + +#ifdef TEST_FSSTORE_UL + LittleFileSystem *fs = new LittleFileSystem("fs", &ul_bd); + + result = fs->mount(&ul_bd); + + if (result) { + result = fs->reformat(&ul_bd); + TEST_ASSERT_EQUAL(0, result); + } + + FileSystemStore *ul_kv = new FileSystemStore(fs); +#else + TDBStore *ul_kv = new TDBStore(&ul_bd); +#endif + +#ifdef NO_RBP_MODE + TDBStore *rbp_kv = 0; +#else + TDBStore *rbp_kv = new TDBStore(&rbp_bd); +#endif + + SecureStore *sec_kv = new SecureStore(ul_kv, rbp_kv); + + timer.reset(); + result = sec_kv->init(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + elapsed = timer.read_ms(); + printf("Elapsed time for init %d ms\n", elapsed); + + timer.reset(); + result = sec_kv->reset(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + elapsed = timer.read_ms(); + printf("Elapsed time for reset is %d ms\n", elapsed); + + result = sec_kv->set(key1, key1_val1, strlen(key1_val1), KVStore::ENCRYPT_FLAG | KVStore::AUTHENTICATE_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = sec_kv->set(key2, key2_val1, strlen(key2_val1), KVStore::AUTHENTICATE_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = sec_kv->set(key2, key2_val2, strlen(key2_val2), KVStore::ENCRYPT_FLAG | KVStore::AUTHENTICATE_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = sec_kv->get(key2, get_buf, sizeof(get_buf), &actual_data_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(strlen(key2_val2), actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key2_val2, get_buf, strlen(key2_val2)); + + timer.reset(); + result = sec_kv->set(key2, key2_val3, strlen(key2_val3), KVStore::AUTHENTICATE_FLAG | KVStore::ROLLBACK_PROTECT_FLAG); + elapsed = timer.read_ms(); + printf("Elapsed time for set is %d ms\n", elapsed); + + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = sec_kv->set(key3, key3_val1, strlen(key3_val1), + KVStore::AUTHENTICATE_FLAG | KVStore::ENCRYPT_FLAG | KVStore::ROLLBACK_PROTECT_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = sec_kv->set(key3, key3_val2, strlen(key3_val2), + KVStore::AUTHENTICATE_FLAG | KVStore::ENCRYPT_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_INVALID_ARGUMENT, result); + + result = sec_kv->get(key3, get_buf, sizeof(get_buf), &actual_data_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(strlen(key3_val1), actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key3_val1, get_buf, strlen(key3_val1)); + + for (int j = 0; j < 2; j++) { + result = sec_kv->set(key4, key4_val1, strlen(key4_val1), + KVStore::AUTHENTICATE_FLAG | KVStore::ROLLBACK_PROTECT_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = sec_kv->set(key4, key4_val2, strlen(key4_val2), + KVStore::AUTHENTICATE_FLAG | KVStore::ENCRYPT_FLAG | KVStore::ROLLBACK_PROTECT_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + } + + result = sec_kv->get_info(key3, &info); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(KVStore::AUTHENTICATE_FLAG | KVStore::ENCRYPT_FLAG | KVStore::ROLLBACK_PROTECT_FLAG, info.flags); + TEST_ASSERT_EQUAL(strlen(key3_val1), info.size); + + result = ul_kv->get_info(key3, &info); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(KVStore::AUTHENTICATE_FLAG | KVStore::ENCRYPT_FLAG | KVStore::ROLLBACK_PROTECT_FLAG, info.flags); + +#ifndef NO_RBP_MODE + result = rbp_kv->get_info(key3, &info); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); +#endif + + result = sec_kv->remove(key3); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = sec_kv->remove(key3); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_ITEM_NOT_FOUND, result); + + result = ul_kv->get_info(key3, &info); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_ITEM_NOT_FOUND, result); + +#ifndef NO_RBP_MODE + result = rbp_kv->get_info(key3, &info); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_ITEM_NOT_FOUND, result); +#endif + + result = sec_kv->get_info(key5, &info); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_ITEM_NOT_FOUND, result); + + result = sec_kv->set(key5, key5_val1, strlen(key5_val1), + KVStore::AUTHENTICATE_FLAG | KVStore::ROLLBACK_PROTECT_FLAG | KVStore::WRITE_ONCE_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + +#ifndef NO_RBP_MODE + result = rbp_kv->get_info(key5, &info); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(KVStore::WRITE_ONCE_FLAG, info.flags); +#endif + + result = sec_kv->set(key5, key5_val2, strlen(key5_val2), + KVStore::AUTHENTICATE_FLAG | KVStore::ROLLBACK_PROTECT_FLAG | KVStore::WRITE_ONCE_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_WRITE_PROTECTED, result); + + result = sec_kv->remove(key5); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_WRITE_PROTECTED, result); + + result = sec_kv->get(key5, get_buf, sizeof(get_buf), &actual_data_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(strlen(key5_val1), actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key5_val1, get_buf, strlen(key5_val1)); + + result = sec_kv->get(key1, get_buf, sizeof(get_buf), &actual_data_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(strlen(key1_val1), actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key1_val1, get_buf, strlen(key1_val1)); + + timer.reset(); + result = sec_kv->get(key2, get_buf, sizeof(get_buf), &actual_data_size); + elapsed = timer.read_ms(); + printf("Elapsed time for get is %d ms\n", elapsed); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(strlen(key2_val3), actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key2_val3, get_buf, strlen(key2_val3)); + + result = sec_kv->get(key4, get_buf, sizeof(get_buf), &actual_data_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(strlen(key4_val2), actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key4_val2, get_buf, strlen(key4_val2)); + + result = sec_kv->get(key4, get_buf, 7, &actual_data_size, 30); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(7, actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key4_val2 + 30, get_buf, 7); + + result = sec_kv->get(key5, get_buf, sizeof(get_buf), &actual_data_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(strlen(key5_val1), actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key5_val1, get_buf, strlen(key5_val1)); + + KVStore::iterator_t it; + char *char_get_buf = reinterpret_cast (get_buf); + + result = sec_kv->iterator_open(&it, "This"); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = sec_kv->iterator_next(it, char_get_buf, sizeof(get_buf)); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + bool got_key4 = !strcmp(key4, char_get_buf); + bool got_key5 = !strcmp(key5, char_get_buf); + TEST_ASSERT_EQUAL(true, got_key4 || got_key5); + + result = sec_kv->iterator_next(it, char_get_buf, sizeof(get_buf)); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + if (got_key4) { + TEST_ASSERT_EQUAL_STRING(key5, char_get_buf); + } else { + TEST_ASSERT_EQUAL_STRING(key4, char_get_buf); + } + + result = sec_kv->iterator_next(it, (char *)get_buf, sizeof(get_buf)); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_ITEM_NOT_FOUND, result); + + result = sec_kv->iterator_close(it); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = sec_kv->deinit(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + timer.reset(); + result = sec_kv->init(); + elapsed = timer.read_ms(); + printf("Elapsed time for init is %d ms\n", elapsed); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = sec_kv->get(key4, get_buf, sizeof(get_buf), &actual_data_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(strlen(key4_val2), actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(key4_val2, get_buf, strlen(key4_val2)); + + result = sec_kv->set(key6, key6_val1, strlen(key6_val1), + KVStore::AUTHENTICATE_FLAG | KVStore::ENCRYPT_FLAG | KVStore::ROLLBACK_PROTECT_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + +#ifndef NO_RBP_MODE + // Simulate a rollback attack + char attack_buf[sizeof(get_buf)]; + size_t attack_size; + result = ul_kv->get(key6, attack_buf, sizeof(attack_buf), &attack_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = sec_kv->set(key6, key6_val2, strlen(key6_val2), + KVStore::AUTHENTICATE_FLAG | KVStore::ENCRYPT_FLAG | KVStore::ROLLBACK_PROTECT_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = ul_kv->set(key6, attack_buf, attack_size, + KVStore::AUTHENTICATE_FLAG | KVStore::ENCRYPT_FLAG | KVStore::ROLLBACK_PROTECT_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = sec_kv->get_info(key6, 0); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_RBP_AUTHENTICATION_FAILED, result); + + // Make sure encrypted data is truly encrypted + result = rbp_kv->get_info(key6, &info); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + int cmac_size = info.size; + uint8_t *cmac = new uint8_t[cmac_size]; + + result = sec_kv->set(key7, key7_val1, strlen(key7_val1), KVStore::AUTHENTICATE_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = ul_kv->get(key7, attack_buf, sizeof(attack_buf), &attack_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + int data_offset = attack_size - cmac_size - strlen(key7_val1); + TEST_ASSERT_EQUAL(0, strncmp(key7_val1, attack_buf + data_offset, strlen(key7_val1))); + + result = sec_kv->set(key7, key7_val1, strlen(key7_val1), KVStore::AUTHENTICATE_FLAG | KVStore::ENCRYPT_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = ul_kv->get(key7, attack_buf, sizeof(attack_buf), &attack_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_NOT_EQUAL(0, strncmp(key7_val1, attack_buf + data_offset, strlen(key7_val1))); + + // Simulate a wrong CMAC + result = ul_kv->get(key7, attack_buf, attack_size - cmac_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + result = ul_kv->get(key7, cmac, cmac_size, &actual_data_size, attack_size - cmac_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + cmac[0]++; + + KVStore::set_handle_t handle; + result = ul_kv->set_start(&handle, key7, attack_size, KVStore::AUTHENTICATE_FLAG | KVStore::ENCRYPT_FLAG); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = ul_kv->set_add_data(handle, attack_buf, attack_size - cmac_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + result = ul_kv->set_add_data(handle, cmac, cmac_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + result = ul_kv->set_finalize(handle); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = ul_kv->get(key7, attack_buf, sizeof(attack_buf), &attack_size); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = sec_kv->get_info(key7, 0); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_ERROR_AUTHENTICATION_FAILED, result); + + delete[] cmac; +#endif + + result = sec_kv->deinit(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + delete sec_kv; + delete ul_kv; + delete rbp_kv; + +#ifdef TEST_FSSTORE_UL + delete fs; +#endif +} + +#if 0 +static void multi_set_test() +{ + char *key; + uint8_t *get_buf, *set_buf; + size_t key_size = 32; + size_t data_size = 512; + size_t num_keys = 16; + size_t set_iters = 3; + size_t actual_data_size; + int result; + mbed::Timer timer; + int elapsed; + size_t i; + uint8_t key_ind; + + timer.start(); +#if !defined(TEST_SPIF) && !defined(TEST_SD) + HeapBlockDevice heap_bd(4096 * 64, 1, 1, 4096); + FlashSimBlockDevice flash_bd(&heap_bd); +#endif + + // TODO: Fix + KVStore *kvs = new TDBStore(&flash_bd); + + timer.reset(); + result = kvs->init(); + elapsed = timer.read_ms(); + printf("Elapsed time for initial init is %d ms\n", elapsed); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + key = new char[key_size + 1]; + get_buf = new uint8_t[data_size]; + set_buf = new uint8_t[data_size]; + + srand(1); + for (i = 0; i < key_size; i++) { + // printable characters only + key[i] = rand() % 223 + 32; + } + key[key_size] = '\0'; + + for (i = 0; i < data_size; i++) { + set_buf[i] = rand() % 256; + } + + int max_set_time = 0, total_set_time = 0; + int max_get_time = 0, total_get_time = 0; + + timer.reset(); + result = kvs->reset(); + elapsed = timer.read_ms(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + for (i = 0; i < set_iters; i++) { + for (key_ind = 0; key_ind < num_keys; key_ind++) { + key[0] = '0' + key_ind; + set_buf[0] = key_ind * (i + 1); + timer.reset(); + result = kvs->set(key, set_buf, data_size, 0); + elapsed = timer.read_ms(); + TEST_ASSERT_EQUAL(0, result); + if (elapsed > max_set_time) { + max_set_time = elapsed; + } + total_set_time += elapsed; + } + } + + for (key_ind = 0; key_ind < num_keys; key_ind++) { + key[0] = '0' + key_ind; + set_buf[0] = key_ind * set_iters; + timer.reset(); + result = kvs->get(key, get_buf, data_size, &actual_data_size); + elapsed = timer.read_ms(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + TEST_ASSERT_EQUAL(data_size, actual_data_size); + TEST_ASSERT_EQUAL_STRING_LEN(set_buf, get_buf, data_size); + if (elapsed > max_get_time) { + max_get_time = elapsed; + } + total_get_time += elapsed; + } + + printf("set time: Total (%d * %d times) - %d ms, Average - %d ms, Max - %d ms\n", + set_iters, num_keys, total_set_time, + total_set_time / (set_iters * num_keys), max_set_time); + printf("get time: Total (%d times) - %d ms, Average - %d ms, Max - %d ms\n", + num_keys, total_get_time, + total_get_time / num_keys, max_get_time); + + result = kvs->deinit(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + timer.reset(); + result = kvs->init(); + elapsed = timer.read_ms(); + printf("Elapsed time for init is %d ms\n", elapsed); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + result = kvs->deinit(); + TEST_ASSERT_EQUAL_ERROR_CODE(MBED_SUCCESS, result); + + delete[] key; + delete[] get_buf; + delete[] set_buf; + + delete kvs; +} +#endif + + + +utest::v1::status_t greentea_failure_handler(const Case *const source, const failure_t reason) +{ + greentea_case_failure_abort_handler(source, reason); + return STATUS_CONTINUE; +} + +Case cases[] = { + Case("SecureStore: White box test", white_box_test, greentea_failure_handler), +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(120, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + return !Harness::run(specification); +} diff --git a/features/storage/kvstore/securestore/SecureStore.cpp b/features/storage/kvstore/securestore/SecureStore.cpp new file mode 100644 index 00000000000..18ca1b83587 --- /dev/null +++ b/features/storage/kvstore/securestore/SecureStore.cpp @@ -0,0 +1,867 @@ +/* + * Copyright (c) 2018 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. + */ + +// ----------------------------------------------------------- Includes ----------------------------------------------------------- + +#include "SecureStore.h" + +#include "aes.h" +#include "cmac.h" +#include "entropy.h" +#include "DeviceKey.h" +#include "mbed_assert.h" +#include "mbed_wait_api.h" +#include "mbed_error.h" +#include +#include +#include + +using namespace mbed; + +// --------------------------------------------------------- Definitions ---------------------------------------------------------- + +static const uint32_t securestore_revision = 1; + +static const uint32_t enc_block_size = 16; +static const uint32_t cmac_size = 16; +static const uint32_t iv_size = 8; +static const uint32_t scratch_buf_size = 256; +static const uint32_t derived_key_size = 16; + +static const char *const enc_prefix = "ENC"; +static const char *const auth_prefix = "AUTH"; + +typedef struct { + uint16_t metadata_size; + uint16_t revision; + uint32_t data_size; + uint32_t create_flags; + uint8_t iv[iv_size]; +} record_metadata_t; + +// incremental set handle +typedef struct { + record_metadata_t metadata; + char *key; + uint32_t offset_in_data; + uint8_t ctr_buf[enc_block_size]; + mbedtls_aes_context enc_ctx; + mbedtls_cipher_context_t auth_ctx; + KVStore::set_handle_t underlying_handle; +} inc_set_handle_t; + +// iterator handle +typedef struct { + KVStore::iterator_t underlying_it; +} key_iterator_handle_t; + + +// -------------------------------------------------- Local Functions Declaration ---------------------------------------------------- + +// -------------------------------------------------- Functions Implementation ---------------------------------------------------- + +int encrypt_decrypt_start(mbedtls_aes_context& enc_aes_ctx, uint8_t *iv, const char *key, + uint8_t *ctr_buf, uint8_t *salt_buf, int salt_buf_size) +{ + DeviceKey& devkey = DeviceKey::get_instance(); + char *salt = reinterpret_cast (salt_buf); + uint8_t encrypt_key[derived_key_size]; + strcpy(salt, enc_prefix); + int pos = strlen(enc_prefix); + strncpy(salt + pos, key, salt_buf_size - pos - 1); + salt_buf[salt_buf_size - 1] = 0; + int os_ret = devkey.generate_derived_key(salt_buf, strlen(salt), encrypt_key, DEVICE_KEY_16BYTE); + if (os_ret) { + return os_ret; + } + + mbedtls_aes_init(&enc_aes_ctx); + mbedtls_aes_setkey_enc(&enc_aes_ctx, encrypt_key, enc_block_size * 8); + + memcpy(ctr_buf, iv, iv_size); + memset(ctr_buf + iv_size, 0, iv_size); + + return 0; +} + +int encrypt_decrypt_data(mbedtls_aes_context& enc_aes_ctx, const uint8_t *in_buf, + uint8_t *out_buf, uint32_t chunk_size, uint8_t *ctr_buf, size_t& aes_offs) +{ + uint8_t stream_block[enc_block_size]; + + return mbedtls_aes_crypt_ctr(&enc_aes_ctx, chunk_size, &aes_offs, ctr_buf, + stream_block, in_buf, out_buf); +} + +int cmac_calc_start(mbedtls_cipher_context_t& auth_ctx, const char *key, uint8_t *salt_buf, int salt_buf_size) +{ + DeviceKey& devkey = DeviceKey::get_instance(); + char *salt = reinterpret_cast (salt_buf); + uint8_t auth_key[derived_key_size]; + strcpy(salt, auth_prefix); + int pos = strlen(auth_prefix); + strncpy(salt + pos, key, salt_buf_size - pos - 1); + salt_buf[salt_buf_size - 1] = 0; + int os_ret = devkey.generate_derived_key(salt_buf, strlen(salt), auth_key, DEVICE_KEY_16BYTE); + if (os_ret) { + return os_ret; + } + + const mbedtls_cipher_info_t *cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB); + + mbedtls_cipher_init(&auth_ctx); + + if ((os_ret = mbedtls_cipher_setup(&auth_ctx, cipher_info)) != 0 ) { + return os_ret; + } + + os_ret = mbedtls_cipher_cmac_starts(&auth_ctx, auth_key, cmac_size * 8); + if (os_ret != 0) { + return os_ret; + } + + return 0; +} + +int cmac_calc_data(mbedtls_cipher_context_t& auth_ctx, const void *input, size_t ilen) +{ + int os_ret; + + os_ret = mbedtls_cipher_cmac_update(&auth_ctx, static_cast (input), ilen); + + return os_ret; +} + +int cmac_calc_finish(mbedtls_cipher_context_t& auth_ctx, uint8_t *output) +{ + int os_ret; + + os_ret = mbedtls_cipher_cmac_finish(&auth_ctx, output); + + return os_ret; +} + + + + +// Class member functions + +SecureStore::SecureStore(KVStore *underlying_kv, KVStore *rbp_kv) : + _is_initialized(false), _underlying_kv(underlying_kv), _rbp_kv(rbp_kv), _entropy(0), + _inc_set_handle(0), _scratch_buf(0) +{ +} + +SecureStore::~SecureStore() +{ + deinit(); +} + + +int SecureStore::set_start(set_handle_t *handle, const char *key, size_t final_data_size, + uint32_t create_flags) +{ + int ret, os_ret; + inc_set_handle_t *ih; + info_t info; + bool enc_started = false, auth_started = false; + + if (!_is_initialized) { + return MBED_ERROR_NOT_READY; + } + + if (!is_valid_key(key)) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + // RBP requires authentication + if ((create_flags & ROLLBACK_PROTECT_FLAG) && !(create_flags & AUTHENTICATE_FLAG)) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + *handle = static_cast (_inc_set_handle); + ih = reinterpret_cast (*handle); + + _mutex.lock(); + + ret = _underlying_kv->get(key, &ih->metadata, sizeof(record_metadata_t)); + if (ret == MBED_SUCCESS) { + // Must not remove Rollback protect flag + if (!(create_flags & ROLLBACK_PROTECT_FLAG) && (ih->metadata.create_flags & ROLLBACK_PROTECT_FLAG)) { + ret = MBED_ERROR_INVALID_ARGUMENT; + goto fail; + } + } else if (ret != MBED_ERROR_ITEM_NOT_FOUND) { + ret = MBED_ERROR_READ_FAILED; + goto fail; + } + + if (ih->metadata.create_flags & WRITE_ONCE_FLAG) { + // If write once flag set, check whether key exists in either of the underlying and RBP stores + if (ret != MBED_ERROR_ITEM_NOT_FOUND) { + ret = MBED_ERROR_WRITE_PROTECTED; + goto fail; + } + + if (_rbp_kv) { + ret = _rbp_kv->get_info(key, &info); + if (ret != MBED_ERROR_ITEM_NOT_FOUND) { + if (ret == MBED_SUCCESS) { + ret = MBED_ERROR_WRITE_PROTECTED; + } + goto fail; + } + } + } + + // Fill metadata + ih->metadata.create_flags = create_flags; + ih->metadata.data_size = final_data_size; + ih->metadata.metadata_size = sizeof(record_metadata_t); + ih->metadata.revision = securestore_revision; + + if (create_flags & ENCRYPT_FLAG) { + // generate a new random iv + os_ret = mbedtls_entropy_func(_entropy, ih->metadata.iv, iv_size); + if (os_ret) { + ret = MBED_ERROR_FAILED_OPERATION; + goto fail; + } + os_ret = encrypt_decrypt_start(ih->enc_ctx, ih->metadata.iv, key, ih->ctr_buf, _scratch_buf, + scratch_buf_size); + if (os_ret) { + ret = MBED_ERROR_FAILED_OPERATION; + goto fail; + } + enc_started = true; + } else { + memset(ih->metadata.iv, 0, iv_size); + } + + if (create_flags & AUTHENTICATE_FLAG) { + os_ret = cmac_calc_start(ih->auth_ctx, key, _scratch_buf, scratch_buf_size); + if (os_ret) { + ret = MBED_ERROR_FAILED_OPERATION; + goto fail; + } + auth_started = true; + // Although name is not part of the data, we calculate CMAC on it as well + os_ret = cmac_calc_data(ih->auth_ctx, key, strlen(key)); + if (os_ret) { + ret = MBED_ERROR_FAILED_OPERATION; + goto fail; + } + os_ret = cmac_calc_data(ih->auth_ctx, &ih->metadata, sizeof(record_metadata_t)); + if (os_ret) { + ret = MBED_ERROR_FAILED_OPERATION; + goto fail; + } + } + + ih->offset_in_data = 0; + ih->key = 0; + + ret = _underlying_kv->set_start(&ih->underlying_handle, key, + sizeof(record_metadata_t) + final_data_size + cmac_size, + create_flags); + if (ret) { + goto fail; + } + + ret = _underlying_kv->set_add_data(ih->underlying_handle, &ih->metadata, + sizeof(record_metadata_t)); + if (ret) { + goto fail; + } + + if (create_flags & ROLLBACK_PROTECT_FLAG) { + ih->key = new char[strlen(key) + 1]; + strcpy(ih->key, key); + } + + goto end; + +fail: + if (enc_started) { + mbedtls_aes_free(&ih->enc_ctx); + } + + if (auth_started) { + mbedtls_cipher_free(&ih->auth_ctx); + } + + // mark handle as invalid by clearing metadata size field in header + ih->metadata.metadata_size = 0; + _mutex.unlock(); + +end: + return ret; +} + +int SecureStore::set_add_data(set_handle_t handle, const void *value_data, size_t data_size) +{ + size_t aes_offs = 0; + int os_ret, ret = MBED_SUCCESS; + inc_set_handle_t *ih; + const uint8_t *src_ptr; + + if (handle != _inc_set_handle) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + if (!value_data && data_size) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + ih = reinterpret_cast (handle); + if (!ih->metadata.metadata_size) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + if (ih->offset_in_data + data_size > ih->metadata.data_size) { + ret = MBED_ERROR_INVALID_SIZE; + goto end; + } + + src_ptr = static_cast (value_data); + while (data_size) { + uint32_t chunk_size; + const uint8_t *dst_ptr; + if (ih->metadata.create_flags & ENCRYPT_FLAG) { + // In encrypt mode we don't want to allocate a buffer in the size given by the user - + // Encrypt the data chunk by chunk + chunk_size = std::min((uint32_t) data_size, scratch_buf_size); + dst_ptr = _scratch_buf; + os_ret = encrypt_decrypt_data(ih->enc_ctx, src_ptr, _scratch_buf, + chunk_size, ih->ctr_buf, aes_offs); + if (os_ret) { + ret = MBED_ERROR_FAILED_OPERATION; + goto fail; + } + } else { + chunk_size = data_size; + dst_ptr = static_cast (value_data); + } + + if (ih->metadata.create_flags & AUTHENTICATE_FLAG) { + os_ret = cmac_calc_data(ih->auth_ctx, dst_ptr, chunk_size); + if (os_ret) { + ret = MBED_ERROR_FAILED_OPERATION; + goto fail; + } + } + + ret = _underlying_kv->set_add_data(ih->underlying_handle, dst_ptr, chunk_size); + if (ret) { + goto fail; + } + data_size -= chunk_size; + src_ptr += chunk_size; + ih->offset_in_data += chunk_size; + } + + goto end; + +fail: + if (ih->key) { + delete[] ih->key; + } + if (ih->metadata.create_flags & ENCRYPT_FLAG) { + mbedtls_aes_free(&ih->enc_ctx); + } + + if (ih->metadata.create_flags & AUTHENTICATE_FLAG) { + mbedtls_cipher_free(&ih->auth_ctx); + } + + // mark handle as invalid by clearing metadata size field in header + ih->metadata.metadata_size = 0; + _mutex.unlock(); + +end: + return ret; +} + +int SecureStore::set_finalize(set_handle_t handle) +{ + int os_ret, ret = MBED_SUCCESS; + inc_set_handle_t *ih; + uint8_t cmac[cmac_size] = {0}; + + if (handle != _inc_set_handle) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + ih = reinterpret_cast (handle); + + if (!ih->metadata.metadata_size) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + if (ih->offset_in_data != ih->metadata.data_size) { + ret = MBED_ERROR_INVALID_SIZE; + goto end; + } + + if (ih->metadata.create_flags & AUTHENTICATE_FLAG) { + os_ret = cmac_calc_finish(ih->auth_ctx, cmac); + if (os_ret) { + ret = MBED_ERROR_FAILED_OPERATION; + goto end; + } + } + + ret = _underlying_kv->set_add_data(ih->underlying_handle, cmac, cmac_size); + if (ret) { + goto end; + } + + ret = _underlying_kv->set_finalize(ih->underlying_handle); + if (ret) { + goto end; + } + + if (_rbp_kv && (ih->metadata.create_flags & ROLLBACK_PROTECT_FLAG)) { + // In rollback protect case, we need to store CMAC in RBP store. + // If it's also write once case, set write once flag in the RBP key as well. + ret = _rbp_kv->set(ih->key, cmac, cmac_size, ih->metadata.create_flags & WRITE_ONCE_FLAG); + delete[] ih->key; + if (ret) { + goto end; + } + } + +end: + // mark handle as invalid by clearing metadata size field in header + ih->metadata.metadata_size = 0; + if (ih->metadata.create_flags & ENCRYPT_FLAG) { + mbedtls_aes_free(&ih->enc_ctx); + } + + if (ih->metadata.create_flags & AUTHENTICATE_FLAG) { + mbedtls_cipher_free(&ih->auth_ctx); + } + + _mutex.unlock(); + return ret; +} + +int SecureStore::set(const char *key, const void *buffer, size_t size, uint32_t create_flags) +{ + int ret; + set_handle_t handle; + + // Don't wait till we get to set_add_data to catch this + if (!buffer && size) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + ret = set_start(&handle, key, size, create_flags); + if (ret) { + return ret; + } + + ret = set_add_data(handle, buffer, size); + if (ret) { + return ret; + } + + ret = set_finalize(handle); + return ret; +} + +int SecureStore::remove(const char *key) +{ + info_t info; + _mutex.lock(); + + int ret = do_get(key, 0, 0, 0, 0, &info); + if (ret) { + goto end; + } + + if (info.flags & WRITE_ONCE_FLAG) { + ret = MBED_ERROR_WRITE_PROTECTED; + goto end; + } + + ret = _underlying_kv->remove(key); + if (ret) { + goto end; + } + + if (_rbp_kv && (info.flags & ROLLBACK_PROTECT_FLAG)) { + ret = _rbp_kv->remove(key); + if ((ret != MBED_SUCCESS) && (ret != MBED_ERROR_ITEM_NOT_FOUND)) { + goto end; + } + } + + ret = MBED_SUCCESS; + +end: + _mutex.unlock(); + return ret; +} + +int SecureStore::do_get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size, + size_t offset, info_t *info) +{ + int os_ret, ret; + bool rbp_key_exists = false; + uint8_t rbp_cmac[cmac_size]; + size_t aes_offs = 0; + uint32_t data_size; + uint32_t actual_data_size; + uint32_t current_offset; + uint32_t chunk_size; + uint32_t enc_lead_size; + uint8_t *dest_buf; + bool enc_started = false, auth_started = false; + uint32_t create_flags; + + if (!is_valid_key(key)) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + // Use member variable _inc_set_handle as no set operation is used now, + // and it saves us the need to define all members on stack + inc_set_handle_t *ih = static_cast (_inc_set_handle); + + if (_rbp_kv) { + ret = _rbp_kv->get(key, rbp_cmac, cmac_size, 0); + if (!ret) { + rbp_key_exists = true; + } else if (ret != MBED_ERROR_ITEM_NOT_FOUND) { + goto end; + } + } + + ret = _underlying_kv->get(key, &ih->metadata, sizeof(record_metadata_t)); + if (ret) { + // In case we have the key in the RBP KV, then even if the key wasn't found in + // the underlying KV, we may have been exposed to an attack. Return an RBP authentication error. + if (rbp_key_exists) { + ret = MBED_ERROR_RBP_AUTHENTICATION_FAILED; + } + goto end; + } + + create_flags = ih->metadata.create_flags; + if (!_rbp_kv) { + create_flags &= ~ROLLBACK_PROTECT_FLAG; + } + + // Another potential attack case - key hasn't got the RBP flag set, but exists in the RBP KV + if (rbp_key_exists && !(create_flags & ROLLBACK_PROTECT_FLAG)) { + ret = MBED_ERROR_RBP_AUTHENTICATION_FAILED; + goto end; + } + + if (create_flags & AUTHENTICATE_FLAG) { + os_ret = cmac_calc_start(ih->auth_ctx, key, _scratch_buf, scratch_buf_size); + if (os_ret) { + ret = MBED_ERROR_FAILED_OPERATION; + goto end; + } + auth_started = true; + + // Although name is not part of the data, we calculate CMAC on it as well + os_ret = cmac_calc_data(ih->auth_ctx, key, strlen(key)); + if (os_ret) { + ret = MBED_ERROR_FAILED_OPERATION; + goto end; + } + os_ret = cmac_calc_data(ih->auth_ctx, &ih->metadata, sizeof(record_metadata_t)); + if (os_ret) { + ret = MBED_ERROR_FAILED_OPERATION; + goto end; + } + } + + if (create_flags & ENCRYPT_FLAG) { + os_ret = encrypt_decrypt_start(ih->enc_ctx, ih->metadata.iv, key, ih->ctr_buf, _scratch_buf, + scratch_buf_size); + if (os_ret) { + ret = MBED_ERROR_FAILED_OPERATION; + goto end; + } + enc_started = true; + } + + data_size = ih->metadata.data_size; + actual_data_size = std::min((uint32_t) buffer_size, data_size - offset); + current_offset = 0; + enc_lead_size = 0; + + while (data_size) { + // Make sure we read to the user buffer only between offset and offset + actual_data_size + if ((current_offset >= offset) && (current_offset < offset + actual_data_size)) { + dest_buf = (static_cast (buffer)) + enc_lead_size; + chunk_size = actual_data_size - enc_lead_size; + enc_lead_size = 0; + } else { + dest_buf = _scratch_buf; + if (current_offset < offset) { + chunk_size = std::min(scratch_buf_size, offset - current_offset); + // A special case: encrypted user data starts at a middle of an encryption block. + // In this case, we need to read entire block into our scratch buffer, and copy + // the encrypted lead size to the user buffer start + if ((create_flags & ENCRYPT_FLAG) && + (chunk_size % enc_block_size)) { + enc_lead_size = std::min(enc_block_size - chunk_size % enc_block_size, actual_data_size); + chunk_size += enc_lead_size; + } + } else { + chunk_size = std::min(scratch_buf_size, data_size); + enc_lead_size = 0; + } + } + + ret = _underlying_kv->get(key, dest_buf, chunk_size, 0, + ih->metadata.metadata_size + current_offset); + if (ret != MBED_SUCCESS) { + goto end; + } + + if (create_flags & AUTHENTICATE_FLAG) { + os_ret = cmac_calc_data(ih->auth_ctx, dest_buf, chunk_size); + if (os_ret) { + ret = MBED_ERROR_FAILED_OPERATION; + goto end; + } + } + + if (create_flags & ENCRYPT_FLAG) { + // Decrypt data in place + os_ret = encrypt_decrypt_data(ih->enc_ctx, dest_buf, dest_buf, chunk_size, ih->ctr_buf, + aes_offs); + if (os_ret) { + ret = MBED_ERROR_FAILED_OPERATION; + goto end; + } + + if (enc_lead_size) { + // Now copy decrypted lead size to user buffer start + memcpy(buffer, dest_buf + chunk_size - enc_lead_size, enc_lead_size); + } + } + + current_offset += chunk_size; + data_size -= chunk_size; + } + + if (actual_size) { + *actual_size = actual_data_size; + } + + if (create_flags & AUTHENTICATE_FLAG) { + uint8_t calc_cmac[cmac_size], read_cmac[cmac_size]; + os_ret = cmac_calc_finish(ih->auth_ctx, calc_cmac); + if (os_ret) { + ret = MBED_ERROR_FAILED_OPERATION; + goto end; + } + + // Check with record CMAC + ret = _underlying_kv->get(key, read_cmac, cmac_size, 0, + ih->metadata.metadata_size + ih->metadata.data_size); + if (ret) { + goto end; + } + if (memcmp(calc_cmac, read_cmac, cmac_size) != 0) { + ret = MBED_ERROR_AUTHENTICATION_FAILED; + goto end; + } + + // If rollback protect, check also CMAC stored in RBP store + if (create_flags & ROLLBACK_PROTECT_FLAG) { + if (!rbp_key_exists) { + ret = MBED_ERROR_RBP_AUTHENTICATION_FAILED; + goto end; + } + if (memcmp(calc_cmac, rbp_cmac, cmac_size) != 0) { + ret = MBED_ERROR_RBP_AUTHENTICATION_FAILED; + goto end; + } + } + } + + if (info) { + info->flags = ih->metadata.create_flags; + info->size = ih->metadata.data_size; + } + +end: + ih->metadata.metadata_size = 0; + + if (enc_started) { + mbedtls_aes_free(&ih->enc_ctx); + } + + if (auth_started) { + mbedtls_cipher_free(&ih->auth_ctx); + } + + return ret; +} + +int SecureStore::get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size, + size_t offset) +{ + _mutex.lock(); + int ret = do_get(key, buffer, buffer_size, actual_size, offset); + _mutex.unlock(); + + return ret; +} + +int SecureStore::get_info(const char *key, info_t *info) +{ + _mutex.lock(); + int ret = do_get(key, 0, 0, 0, 0, info); + _mutex.unlock(); + + return ret; +} + + +int SecureStore::init() +{ + int ret = MBED_SUCCESS; + + MBED_ASSERT(!(scratch_buf_size % enc_block_size)); + + _mutex.lock(); + + _entropy = new mbedtls_entropy_context; + mbedtls_entropy_init(static_cast(_entropy)); + + _scratch_buf = new uint8_t[scratch_buf_size]; + _inc_set_handle = new inc_set_handle_t; + + ret = _underlying_kv->init(); + if (ret) { + goto fail; + } + + if (_rbp_kv) { + ret = _rbp_kv->init(); + if (ret) { + goto fail; + } + } + + _is_initialized = true; + +fail: + _mutex.unlock(); + return ret; +} + +int SecureStore::deinit() +{ + _mutex.lock(); + if (_is_initialized) { + mbedtls_entropy_free(static_cast(_entropy)); + delete static_cast(_entropy); + delete static_cast(_inc_set_handle); + delete _scratch_buf; + // TODO: Deinit member KVs? + } + + _is_initialized = false; + _mutex.unlock(); + + return MBED_SUCCESS; +} + + +int SecureStore::reset() +{ + int ret; + + if (!_is_initialized) { + return MBED_ERROR_NOT_READY; + } + + _mutex.lock(); + ret = _underlying_kv->reset(); + if (ret) { + goto end; + } + + if (_rbp_kv) { + ret = _rbp_kv->reset(); + if (ret) { + goto end; + } + } + +end: + _mutex.unlock(); + return ret; +} + +int SecureStore::iterator_open(iterator_t *it, const char *prefix) +{ + key_iterator_handle_t *handle; + + if (!_is_initialized) { + return MBED_ERROR_NOT_READY; + } + + if (!it) { + return MBED_ERROR_INVALID_ARGUMENT; + } + + handle = new key_iterator_handle_t; + *it = reinterpret_cast(handle); + + return _underlying_kv->iterator_open(&handle->underlying_it, prefix); +} + +int SecureStore::iterator_next(iterator_t it, char *key, size_t key_size) +{ + key_iterator_handle_t *handle; + + if (!_is_initialized) { + return MBED_ERROR_NOT_READY; + } + + handle = reinterpret_cast(it); + + return _underlying_kv->iterator_next(handle->underlying_it, key, key_size); +} + +int SecureStore::iterator_close(iterator_t it) +{ + key_iterator_handle_t *handle; + int ret; + + if (!_is_initialized) { + return MBED_ERROR_NOT_READY; + } + + handle = reinterpret_cast(it); + + ret = _underlying_kv->iterator_close(handle->underlying_it); + + delete handle; + + return ret; +} + diff --git a/features/storage/kvstore/securestore/SecureStore.h b/features/storage/kvstore/securestore/SecureStore.h new file mode 100644 index 00000000000..876ccef06e9 --- /dev/null +++ b/features/storage/kvstore/securestore/SecureStore.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2018 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 MBED_SECURESTORE_H +#define MBED_SECURESTORE_H + +#include +#include +#include "KVStore.h" +#include "PlatformMutex.h" + +namespace mbed { + +/** TDBStore class + * + * Lightweight Key Value storage over a block device + */ + +class SecureStore : public KVStore { +public: + + /** + * @brief Class constructor + * + * @param[in] underlying_bd Underlying KVSore. + * @param[in] rbp_kv Rollback protect KVStore. + * + * @returns none + */ + SecureStore(KVStore *underlying_kv, KVStore *rbp_kv = 0); + + /** + * @brief Class destructor + * + * @returns none + */ + virtual ~SecureStore(); + + /** + * @brief Initialize KVStore + * + * @returns 0 on success or a negative error code on failure + */ + virtual int init(); + + /** + * @brief Deinitialize KVStore + * + * @returns 0 on success or a negative error code on failure + */ + virtual int deinit(); + + + /** + * @brief Reset KVStore contents (clear all keys) + * Warning: This function is not thread safe. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int reset(); + + /** + * @brief Set one KVStore item, given key and value. + * + * @param[in] key Key. + * @param[in] buffer Value data buffer. + * @param[in] size Value data size. + * @param[in] create_flags Flag mask. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int set(const char *key, const void *buffer, size_t size, uint32_t create_flags); + + /** + * @brief Get one KVStore item, given key. + * + * @param[in] key Key. + * @param[in] buffer Value data buffer. + * @param[in] buffer_size Value data buffer size. + * @param[out] actual_size Actual read size. + * @param[in] offset Offset to read from in data. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size = NULL, + size_t offset = 0); + + /** + * @brief Get information of a given key. + * + * @param[in] key Key. + * @param[out] info Returned information structure. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int get_info(const char *key, info_t *info); + + /** + * @brief Remove a KVStore item, given key. + * + * @param[in] key Key. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int remove(const char *key); + + + /** + * @brief Start an incremental KVStore set sequence. + * + * @param[out] handle Returned incremental set handle. + * @param[in] key Key. + * @param[in] final_data_size Final value data size. + * @param[in] create_flags Flag mask. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int set_start(set_handle_t *handle, const char *key, size_t final_data_size, uint32_t create_flags); + + /** + * @brief Add data to incremental KVStore set sequence. + * + * @param[in] handle Incremental set handle. + * @param[in] value_data value data to add. + * @param[in] data_size value data size. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int set_add_data(set_handle_t handle, const void *value_data, size_t data_size); + + /** + * @brief Finalize an incremental KVStore set sequence. + * + * @param[in] handle Incremental set handle. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int set_finalize(set_handle_t handle); + + /** + * @brief Start an iteration over KVStore keys. + * + * @param[out] it Returned iterator handle. + * @param[in] prefix Key prefix (null for all keys). + * + * @returns 0 on success or a negative error code on failure + */ + virtual int iterator_open(iterator_t *it, const char *prefix = NULL); + + /** + * @brief Get next key in iteration. + * + * @param[in] it Iterator handle. + * @param[in] key Buffer for returned key. + * @param[in] key_size Key buffer size. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int iterator_next(iterator_t it, char *key, size_t key_size); + + /** + * @brief Close iteration. + * + * @param[in] it Iterator handle. + * + * @returns 0 on success or a negative error code on failure + */ + virtual int iterator_close(iterator_t it); + +private: + + PlatformMutex _mutex; + bool _is_initialized; + KVStore *_underlying_kv, *_rbp_kv; + void *_entropy; + void *_inc_set_handle; + uint8_t *_scratch_buf; + + /** + * @brief Actual get function, serving get and get_info APIs. + * + * @param[in] key Key. + * @param[in] buffer Value data buffer. + * @param[in] buffer_size Value data buffer size. + * @param[out] actual_size Actual read size. + * @param[in] offset Offset to read from in data. + * @param[out] info Returned information structure. + * + * @returns 0 on success or a negative error code on failure + */ + int do_get(const char *key, void *buffer, size_t buffer_size, size_t *actual_size = NULL, + size_t offset = 0, info_t *info = 0); + + +}; +/** @}*/ + +} // namespace mbed + +#endif diff --git a/features/storage/kvstore/securestore/mbed_lib.json b/features/storage/kvstore/securestore/mbed_lib.json new file mode 100644 index 00000000000..0718963316f --- /dev/null +++ b/features/storage/kvstore/securestore/mbed_lib.json @@ -0,0 +1,6 @@ +{ + "name": "SecureStore", + "macros": ["MBEDTLS_CIPHER_MODE_CTR", "MBEDTLS_CMAC_C"], + "config": { + } +}