diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml
index 39e02488f..16cc2005b 100644
--- a/.github/workflows/compile-examples.yml
+++ b/.github/workflows/compile-examples.yml
@@ -76,6 +76,7 @@ jobs:
               - libraries/RTC/examples/RTC_NTPSync
               - libraries/RTC/examples/RTC_Alarm
               - libraries/SFU
+              - libraries/KVStore/examples/StartCounter
           - board:
               fqbn: "arduino-git:renesas:portenta_c33"
             additional-sketch-paths: |
@@ -91,6 +92,7 @@ jobs:
               - libraries/RTC/examples/RTC_NTPSync
               - libraries/RTC/examples/RTC_Alarm
               - libraries/SFU
+              - libraries/KVStore/examples/StartCounter
           - board:
               fqbn: "arduino:renesas_uno:unor4wifi"
             additional-sketch-paths: |
diff --git a/libraries/KVStore/.portenta_only b/libraries/KVStore/.portenta_only
new file mode 100644
index 000000000..e69de29bb
diff --git a/libraries/KVStore/examples/StartCounter/StartCounter.ino b/libraries/KVStore/examples/StartCounter/StartCounter.ino
new file mode 100644
index 000000000..41dedeba1
--- /dev/null
+++ b/libraries/KVStore/examples/StartCounter/StartCounter.ino
@@ -0,0 +1,72 @@
+/*
+ * Microcontroller startup counter example with Portenta c33 kvstore library
+ * This simple example demonstrates using the KVStore library to store how many times
+ * the microcontroller has booted. The KVStore library is based on mbed OS KVStore library
+ *
+ * This example is based on Martin Sloup (Arcao) StartCounter example for arduino-esp32
+ */
+
+#include <KVStore.h>
+#include <TDBStore.h>
+#include <MBRBlockDevice.h>
+
+auto root = BlockDevice::get_default_instance();
+MBRBlockDevice bd(root, 3);
+TDBStore kvstore(&bd);
+
+void setup() {
+  Serial.begin(115200);
+  Serial.println();
+
+  while(!Serial);
+
+  // Init KVStore
+  if (kvstore.init() != KVSTORE_SUCCESS) {
+    Serial.println("Cannot initialize kvstore");
+    while(1) {};
+  }
+
+  // Remove all values stored in the kvstore
+  // kvstore.reset();
+
+  // Or remove the counter key only
+  // kvstore.remove("counter");
+
+  // Get the counter value, if it doesn't exist it returns KVSTORE_ERROR_ITEM_NOT_FOUND
+  unsigned int counter;
+  auto res = kvstore.get("counter", (void*)&counter, sizeof(counter));
+
+  if (res == KVSTORE_ERROR_ITEM_NOT_FOUND) {
+    counter = 0;
+  } else if (res == KVSTORE_SUCCESS) {
+    // Increase counter by 1
+    counter++;
+  } else {
+    Serial.print("Error getting counter from kvstore: ");
+    Serial.println(res);
+  }
+
+  // Print the counter to Serial Monitor
+  Serial.print("Current counter value: ");
+  Serial.println(counter);
+
+  // Store the updated counter value to the kvstore
+  if (kvstore.set("counter",(void*)&counter, sizeof(counter), 0) != KVSTORE_SUCCESS) {
+    Serial.println("Error setting counter from kvstore");
+  }
+
+  // Close the kvstore
+  if (kvstore.deinit() != KVSTORE_SUCCESS) {
+    Serial.println("Cannot deinitialize kvstore");
+    while(1) {};
+  }
+
+  // Wait 10 seconds
+  Serial.println("Restarting in 10 seconds...");
+  delay(10000);
+
+  // Reset
+  NVIC_SystemReset();
+}
+
+void loop() {}
diff --git a/libraries/KVStore/library.properties b/libraries/KVStore/library.properties
new file mode 100644
index 000000000..b4320d4b6
--- /dev/null
+++ b/libraries/KVStore/library.properties
@@ -0,0 +1,9 @@
+name=KVStore
+version=1.0.0
+author=Arduino
+maintainer=Arduino <info@arduino.cc>
+sentence=KVStore for arduino core renesas
+paragraph=
+category=Storage
+url=https://github.com/arduino/ArduinoCore-renesas/tree/master/libraries/KVStore
+architectures=renesas,renesas_portenta
diff --git a/libraries/KVStore/src/KVStore.h b/libraries/KVStore/src/KVStore.h
new file mode 100644
index 000000000..08e0bae61
--- /dev/null
+++ b/libraries/KVStore/src/KVStore.h
@@ -0,0 +1,229 @@
+/*
+ * 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 <stdint.h>
+#include <string.h>
+
+#define KVSTORE_SUCCESS                        0
+#define KVSTORE_ERROR_READ_FAILED              283
+#define KVSTORE_ERROR_WRITE_FAILED             284
+#define KVSTORE_ERROR_INVALID_DATA_DETECTED    258
+#define KVSTORE_ERROR_INVALID_SIZE             261
+#define KVSTORE_ERROR_INVALID_ARGUMENT         257
+#define KVSTORE_ERROR_ITEM_NOT_FOUND           263
+#define KVSTORE_ERROR_MEDIA_FULL               267
+#define KVSTORE_ERROR_WRITE_PROTECTED          274
+#define KVSTORE_ERROR_OUT_OF_RESOURCES         288
+#define KVSTORE_ERROR_NOT_READY                270
+#define KVSTORE_ERROR_FAILED_OPERATION         271
+
+namespace mbed {
+
+/** KVStore class
+ *
+ *  Interface class for Key Value Storage
+ */
+class KVStore {
+public:
+    enum create_flags {
+        WRITE_ONCE_FLAG                     = (1 << 0),
+        REQUIRE_CONFIDENTIALITY_FLAG        = (1 << 1),
+        RESERVED_FLAG                       = (1 << 2),
+        REQUIRE_REPLAY_PROTECTION_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;
+
+    /**
+     * Holds key information
+     */
+    typedef struct info {
+        /**
+         * The key size
+         */
+        size_t size;
+        /*
+         * The Key flags, possible flags combination:
+         * WRITE_ONCE_FLAG,
+         * REQUIRE_CONFIDENTIALITY_FLAG,
+         * REQUIRE_REPLAY_PROTECTION_FLAG
+         */
+        uint32_t flags;
+    } info_t;
+
+    virtual ~KVStore() {};
+
+    /**
+     * @brief Initialize KVStore
+     *
+     * @returns KVSTORE_SUCCESS on success or an error code on failure
+     */
+    virtual int init() = 0;
+
+    /**
+     * @brief Deinitialize KVStore
+     *
+     * @returns KVSTORE_SUCCESS on success or an error code on failure
+     */
+    virtual int deinit() = 0;
+
+
+    /**
+     * @brief Reset KVStore contents (clear all keys)
+     *
+     * @returns KVSTORE_SUCCESS on success or an error code on failure
+     */
+    virtual int reset() = 0;
+
+    /**
+     * @brief Set one KVStore item, given key and value.
+     *
+     * @param[in]  key                  Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     * @param[in]  buffer               Value data buffer.
+     * @param[in]  size                 Value data size.
+     * @param[in]  create_flags         Flag mask.
+     *
+     * @returns KVSTORE_SUCCESS on success or an 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 - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     * @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 KVSTORE_SUCCESS on success or an 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 - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     * @param[out] info                 Returned information structure (NULL to pass nothing).
+     *
+     * @returns KVSTORE_SUCCESS on success or an 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 - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     *
+     * @returns KVSTORE_SUCCESS on success or an 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 - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     * @param[in]  final_data_size      Final value data size.
+     * @param[in]  create_flags         Flag mask.
+     *
+     * @returns KVSTORE_SUCCESS on success or an 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 KVSTORE_SUCCESS on success or an 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 KVSTORE_SUCCESS on success or an 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 KVSTORE_SUCCESS on success or an 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 KVSTORE_SUCCESS on success or an 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 KVSTORE_SUCCESS on success or an error code on failure
+     */
+    virtual int iterator_close(iterator_t it) = 0;
+
+    /** Convenience function for checking key validity.
+     *  Key must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     *
+     * @param[in]  key                  Key buffer.
+     *
+     * @returns KVSTORE_SUCCESS on success or an 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/libraries/KVStore/src/MbedCRC.h b/libraries/KVStore/src/MbedCRC.h
new file mode 100644
index 000000000..ce6b3f530
--- /dev/null
+++ b/libraries/KVStore/src/MbedCRC.h
@@ -0,0 +1,892 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2018 ARM Limited
+ * 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_CRC_API_H
+#define MBED_CRC_API_H
+
+// #include "cmsis.h"
+// #include "hal/crc_api.h"
+#ifdef DEVICE_CRC
+#include "device.h"
+#endif
+// #include "platform/mbed_assert.h"
+
+#ifdef __cplusplus
+
+// #include "platform/SingletonPtr.h"
+// #include "platform/PlatformMutex.h"
+
+// #include <mstd_type_traits>
+
+// namespace mbed {
+/** \addtogroup drivers-public-api */
+/** @{*/
+/**
+ * \defgroup drivers_MbedCRC MbedCRC class
+ * @{
+ */
+
+// extern SingletonPtr<PlatformMutex> mbed_crc_mutex;
+
+/** CRC Polynomial value
+ *
+ * Different polynomial values supported
+ */
+typedef enum crc_polynomial {
+    POLY_7BIT_SD     = 0x09,         ///< x7+x3+1
+    POLY_8BIT_CCITT  = 0x07,         ///< x8+x2+x+1
+    POLY_16BIT_CCITT = 0x1021,       ///< x16+x12+x5+1
+    POLY_16BIT_IBM   = 0x8005,       ///< x16+x15+x2+1
+    POLY_32BIT_ANSI  = 0x04C11DB7    ///< x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1
+} crc_polynomial_t;
+
+/** CRC mode selection
+ */
+enum class CrcMode {
+    HARDWARE,   /// Use hardware (if available), else table-based computation
+    TABLE,      /// Use table-based computation (if table available), else bitwise
+    BITWISE     /// Always use bitwise manual computation
+};
+
+#ifndef DOXYGEN_ONLY
+namespace impl {
+template<uint32_t polynomial, uint8_t width, CrcMode mode>
+class MbedCRC;
+
+constexpr bool have_crc_table(uint32_t polynomial, uint8_t width)
+{
+#if MBED_CRC_TABLE_SIZE > 0
+    return (polynomial == POLY_32BIT_ANSI && width == 32) ||
+           (polynomial == POLY_16BIT_IBM && width == 16) ||
+           (polynomial == POLY_16BIT_CCITT && width == 16) ||
+           (polynomial == POLY_8BIT_CCITT && width == 8) ||
+           (polynomial == POLY_7BIT_SD && width == 7);
+#else
+    return false;
+#endif
+}
+
+constexpr CrcMode choose_crc_mode(uint32_t polynomial, uint8_t width, CrcMode mode_limit)
+{
+    return
+#if DEVICE_CRC
+        mode_limit == CrcMode::HARDWARE && HAL_CRC_IS_SUPPORTED(polynomial, width) ? CrcMode::HARDWARE :
+#endif
+        mode_limit <= CrcMode::TABLE && have_crc_table(polynomial, width) ? CrcMode::TABLE :
+        CrcMode::BITWISE;
+}
+#endif // DOXYGEN_ONLY
+
+} // namespace impl
+
+/** CRC object provides CRC generation through hardware or software
+ *
+ *  CRC sums can be generated using three different methods: hardware, software ROM tables
+ *  and bitwise computation. The mode used is normally selected automatically based on required
+ *  polynomial and hardware capabilities. Any polynomial in standard form (`x^3 + x + 1`)
+ *  can be used for computation, but custom ones can affect the performance.
+ *
+ *  First choice is the hardware mode. The supported polynomials are hardware specific, and
+ *  you need to consult your MCU manual to discover them. Next, ROM polynomial tables
+ *  are tried (you can find list of supported polynomials here ::crc_polynomial). If the selected
+ *  configuration is supported, it will accelerate the software computations. If ROM tables
+ *  are not available for the selected polynomial, then CRC is computed at run time bit by bit
+ *  for all data input.
+ *
+ *  If desired, the mode can be manually limited for a given instance by specifying the mode_limit
+ *  template parameter. This might be appropriate to ensure a table is not pulled in for a
+ *  non-speed-critical CRC, or to avoid the hardware set-up overhead if you know you will be
+ *  calling `compute` with very small data sizes.
+ *
+ *  @note Synchronization level: Thread safe
+ *
+ *  @tparam  polynomial CRC polynomial value in hex
+ *  @tparam  width      CRC polynomial width
+ *  @tparam  mode_limit Maximum amount of acceleration to use
+ *
+ * Example: Compute CRC data
+ * @code
+ *
+ *  #include "mbed.h"
+ *
+ *  int main() {
+ *      MbedCRC<POLY_32BIT_ANSI, 32> ct;
+ *
+ *      char  test[] = "123456789";
+ *      uint32_t crc = 0;
+ *
+ *      printf("\nPolynomial = 0x%lx  Width = %d \n", ct.get_polynomial(), ct.get_width());
+ *
+ *      ct.compute((void *)test, strlen((const char*)test), &crc);
+ *
+ *      printf("The CRC of data \"123456789\" is : 0x%lx\n", crc);
+ *      return 0;
+ *  }
+ * @endcode
+ * Example: Compute CRC with data available in parts
+ * @code
+ *
+ *  #include "mbed.h"
+ *  int main() {
+ *      MbedCRC<POLY_32BIT_ANSI, 32> ct;
+ *
+ *      char  test[] = "123456789";
+ *      uint32_t crc = 0;
+ *
+ *      printf("\nPolynomial = 0x%lx  Width = %d \n", ct.get_polynomial(), ct.get_width());
+ *      ct.compute_partial_start(&crc);
+ *      ct.compute_partial((void *)&test, 4, &crc);
+ *      ct.compute_partial((void *)&test[4], 5, &crc);
+ *      ct.compute_partial_stop(&crc);
+ *      printf("The CRC of data \"123456789\" is : 0x%lx\n", crc);
+ *      return 0;
+ *  }
+ * @endcode
+ */
+template <uint32_t polynomial = POLY_32BIT_ANSI, uint8_t width = 32, CrcMode mode_limit = CrcMode::HARDWARE>
+class MbedCRC  {
+    impl::MbedCRC<polynomial, width, impl::choose_crc_mode(polynomial, width, mode_limit)> crc_impl;
+
+public:
+    /* Backwards compatibility */
+    enum CrcMode {
+#if DEVICE_CRC
+        HARDWARE    = int(::CrcMode::HARDWARE),
+#endif
+        TABLE       = int(::CrcMode::TABLE),
+        BITWISE     = int(::CrcMode::BITWISE)
+    };
+
+    typedef size_t crc_data_size_t;
+
+    /** Lifetime of CRC object
+     *
+     *  @param  initial_xor  Initial value/seed to Xor
+     *  @param  final_xor  Final Xor value
+     *  @param  reflect_data
+     *  @param  reflect_remainder
+     *  @note   Default constructor without any arguments is valid only for supported CRC polynomials. :: crc_polynomial_t
+     *          MbedCRC <POLY_7BIT_SD, 7> ct; --- Valid POLY_7BIT_SD
+     *          MbedCRC <0x1021, 16> ct; --- Valid POLY_16BIT_CCITT
+     *          MbedCRC <POLY_16BIT_CCITT, 32> ct; --- Invalid, compilation error
+     *          MbedCRC <POLY_16BIT_CCITT, 32> ct (i,f,rd,rr) Constructor can be used for not supported polynomials
+     *          MbedCRC<POLY_16BIT_CCITT, 16> sd(0, 0, false, false); Constructor can also be used for supported
+     *             polynomials with different initial/final/reflect values
+     *
+     */
+    constexpr
+    MbedCRC(uint32_t initial_xor, uint32_t final_xor, bool reflect_data, bool reflect_remainder) :
+        crc_impl(initial_xor, final_xor, reflect_data, reflect_remainder)
+    {
+    }
+
+    /* Default values for different types of polynomials
+    */
+    // *INDENT-OFF*
+    template<uint32_t poly = polynomial, std::enable_if_t<poly == POLY_32BIT_ANSI && width == 32, int> = 0>
+    constexpr MbedCRC() : MbedCRC(0xFFFFFFFF, 0xFFFFFFFF, true, true)
+    {
+    }
+
+    template<uint32_t poly = polynomial, std::enable_if_t<poly == POLY_16BIT_IBM && width == 16, int> = 0>
+    constexpr MbedCRC() : MbedCRC(0, 0, true, true)
+    {
+    }
+
+    template<uint32_t poly = polynomial, std::enable_if_t<poly == POLY_16BIT_CCITT && width == 16, int> = 0>
+    constexpr MbedCRC() : MbedCRC(0xFFFF, 0, false, false)
+    {
+    }
+
+    template<uint32_t poly = polynomial, std::enable_if_t<poly == POLY_7BIT_SD && width == 7, int> = 0>
+    constexpr MbedCRC() : MbedCRC(0, 0, false, false)
+    {
+    }
+
+    template<uint32_t poly = polynomial, std::enable_if_t<poly == POLY_8BIT_CCITT && width == 8, int> = 0>
+    constexpr MbedCRC() : MbedCRC(0, 0, false, false)
+    {
+    }
+    // *INDENT-ON*
+
+    /** Compute CRC for the data input
+     *  Compute CRC performs the initialization, computation and collection of
+     *  final CRC.
+     *
+     *  @param  buffer  Data bytes
+     *  @param  size  Size of data
+     *  @param  crc  CRC is the output value
+     *  @return  0 on success, negative error code on failure
+     */
+    int32_t compute(const void *buffer, crc_data_size_t size, uint32_t *crc)
+    {
+        return crc_impl.compute(buffer, size, crc);
+    }
+
+    /** Compute partial CRC for the data input.
+     *
+     *  CRC data if not available fully, CRC can be computed in parts with available data.
+     *
+     *  In case of hardware, intermediate values and states are saved by hardware. Mutex
+     *  locking is used to serialize access to hardware CRC.
+     *
+     *  In case of software CRC, previous CRC output should be passed as argument to the
+     *  current compute_partial call. Please note the intermediate CRC value is maintained by
+     *  application and not the driver.
+     *
+     *  @pre: Call `compute_partial_start` to start the partial CRC calculation.
+     *  @post: Call `compute_partial_stop` to get the final CRC value.
+     *
+     *  @param  buffer  Data bytes
+     *  @param  size  Size of data
+     *  @param  crc  CRC value is intermediate CRC value filled by API.
+     *  @return  0  on success or a negative error code on failure
+     *  @note: CRC as output in compute_partial is not final CRC value, call `compute_partial_stop`
+     *         to get final correct CRC value.
+     */
+    int32_t compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc)
+    {
+        return crc_impl.compute_partial(buffer, size, crc);
+    }
+
+    /** Compute partial start, indicate start of partial computation.
+     *
+     *  This API should be called before performing any partial computation
+     *  with compute_partial API.
+     *
+     *  @param  crc  Initial CRC value set by the API
+     *  @return  0  on success or a negative in case of failure
+     *  @note: CRC is an out parameter and must be reused with compute_partial
+     *         and `compute_partial_stop` without any modifications in application.
+     */
+    int32_t compute_partial_start(uint32_t *crc)
+    {
+        return crc_impl.compute_partial_start(crc);
+    }
+
+    /** Get the final CRC value of partial computation.
+     *
+     *  CRC value available in partial computation is not correct CRC, as some
+     *  algorithms require remainder to be reflected and final value to be XORed
+     *  This API is used to perform final computation to get correct CRC value.
+     *
+     *  @param crc  CRC result
+     *  @return  0  on success or a negative in case of failure.
+     */
+    int32_t compute_partial_stop(uint32_t *crc)
+    {
+        return crc_impl.compute_partial_stop(crc);
+    }
+
+    /** Get the current CRC polynomial.
+     *
+     * @return  Polynomial value
+     */
+    static constexpr uint32_t get_polynomial()
+    {
+        return polynomial;
+    }
+
+    /** Get the current CRC width
+     *
+     * @return  CRC width
+     */
+    static constexpr uint8_t get_width()
+    {
+        return width;
+    }
+};
+
+#if !defined(DOXYGEN_ONLY)
+/* Internal implementation - basically same as public, but actual mode locked in */
+namespace impl {
+
+template <uint32_t polynomial, uint8_t width, CrcMode mode>
+class MbedCRC {
+public:
+    typedef size_t crc_data_size_t;
+
+    constexpr
+    MbedCRC(uint32_t initial_xor, uint32_t final_xor, bool reflect_data, bool reflect_remainder) :
+        _initial_value(adjust_initial_value(initial_xor, reflect_data)),
+        _final_xor(final_xor),
+        _reflect_data(reflect_data),
+        _reflect_remainder(reflect_remainder)
+    {
+        static_assert(width <= 32, "Max 32-bit CRC supported");
+    }
+
+    /** Compute CRC for the data input
+     *  Compute CRC performs the initialization, computation and collection of
+     *  final CRC.
+     *
+     *  @param  buffer  Data bytes
+     *  @param  size  Size of data
+     *  @param  crc  CRC is the output value
+     *  @return  0 on success, negative error code on failure
+     */
+    int32_t compute(const void *buffer, crc_data_size_t size, uint32_t *crc)
+    {
+        int32_t status;
+
+        status = compute_partial_start(crc);
+        if (0 != status) {
+            return status;
+        }
+
+        status = compute_partial(buffer, size, crc);
+        if (0 != status) {
+            return status;
+        }
+
+        status = compute_partial_stop(crc);
+        return status;
+    }
+
+    /** Compute partial CRC for the data input.
+     *
+     *  CRC data if not available fully, CRC can be computed in parts with available data.
+     *
+     *  In case of hardware, intermediate values and states are saved by hardware. Mutex
+     *  locking is used to serialize access to hardware CRC.
+     *
+     *  In case of software CRC, previous CRC output should be passed as argument to the
+     *  current compute_partial call. Please note the intermediate CRC value is maintained by
+     *  application and not the driver.
+     *
+     *  @pre: Call `compute_partial_start` to start the partial CRC calculation.
+     *  @post: Call `compute_partial_stop` to get the final CRC value.
+     *
+     *  @param  buffer  Data bytes
+     *  @param  size  Size of data
+     *  @param  crc  CRC value is intermediate CRC value filled by API.
+     *  @return  0  on success or a negative error code on failure
+     *  @note: CRC as output in compute_partial is not final CRC value, call `compute_partial_stop`
+     *         to get final correct CRC value.
+     */
+    int32_t compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc)
+    {
+        const uint8_t *data = static_cast<const uint8_t *>(buffer);
+        return do_compute_partial(data, size, crc);
+    }
+
+    /** Compute partial start, indicate start of partial computation.
+     *
+     *  This API should be called before performing any partial computation
+     *  with compute_partial API.
+     *
+     *  @param  crc  Initial CRC value set by the API
+     *  @return  0  on success or a negative in case of failure
+     *  @note: CRC is an out parameter and must be reused with compute_partial
+     *         and `compute_partial_stop` without any modifications in application.
+     */
+    int32_t compute_partial_start(uint32_t *crc)
+    {
+#if DEVICE_CRC
+        if (mode == CrcMode::HARDWARE) {
+            lock();
+            crc_mbed_config_t config;
+            config.polynomial  = polynomial;
+            config.width       = width;
+            config.initial_xor = _initial_value;
+            config.final_xor   = _final_xor;
+            config.reflect_in  = _reflect_data;
+            config.reflect_out = _reflect_remainder;
+
+            hal_crc_compute_partial_start(&config);
+        }
+#endif
+
+        *crc = _initial_value;
+        return 0;
+    }
+
+    /** Get the final CRC value of partial computation.
+     *
+     *  CRC value available in partial computation is not correct CRC, as some
+     *  algorithms require remainder to be reflected and final value to be XORed
+     *  This API is used to perform final computation to get correct CRC value.
+     *
+     *  @param crc  CRC result
+     *  @return  0  on success or a negative in case of failure.
+     */
+    int32_t compute_partial_stop(uint32_t *crc)
+    {
+#if DEVICE_CRC
+        if (mode == CrcMode::HARDWARE) {
+            *crc = hal_crc_get_result();
+            unlock();
+            return 0;
+        }
+#endif
+        uint_fast32_t p_crc = *crc;
+        if (mode == CrcMode::BITWISE) {
+            if (_reflect_data) {
+                /* CRC has MSB in bottom bit of register */
+                if (!_reflect_remainder) {
+                    p_crc = reflect_crc(p_crc);
+                }
+            } else {
+                /* CRC has MSB in top bit of register */
+                p_crc = _reflect_remainder ? reflect(p_crc) : shift_right(p_crc);
+            }
+        } else { // TABLE
+            /* CRC has MSB in bottom bit of register */
+            if (!_reflect_remainder) {
+                p_crc = reflect_crc(p_crc);
+            }
+        }
+
+        p_crc ^= _final_xor;
+        p_crc &= get_crc_mask();
+        *crc = p_crc;
+
+        return 0;
+    }
+
+private:
+    /** Guaranteed constexpr reflection (all toolchains)
+     *
+     * @note   This should never be run-time evaluated - very inefficient
+     * @param  Register value to be reflected (full 32-bit value)
+     * @return Reflected value (full 32-bit value)
+     */
+    static constexpr uint32_t reflect_constant(uint32_t data)
+    {
+        /* Doing this hard way to keep it C++11 constexpr and hence ARM C 5 compatible */
+        return ((data & 0x00000001) << 31) |
+               ((data & 0x00000002) << 29) |
+               ((data & 0x00000004) << 27) |
+               ((data & 0x00000008) << 25) |
+               ((data & 0x00000010) << 23) |
+               ((data & 0x00000020) << 21) |
+               ((data & 0x00000040) << 19) |
+               ((data & 0x00000080) << 17) |
+               ((data & 0x00000100) << 15) |
+               ((data & 0x00000200) << 13) |
+               ((data & 0x00000400) << 11) |
+               ((data & 0x00000800) <<  9) |
+               ((data & 0x00001000) <<  7) |
+               ((data & 0x00002000) <<  5) |
+               ((data & 0x00004000) <<  3) |
+               ((data & 0x00008000) <<  1) |
+               ((data & 0x00010000) >>  1) |
+               ((data & 0x00020000) >>  3) |
+               ((data & 0x00040000) >>  5) |
+               ((data & 0x00080000) >>  7) |
+               ((data & 0x00100000) >>  9) |
+               ((data & 0x00200000) >> 11) |
+               ((data & 0x00400000) >> 13) |
+               ((data & 0x00800000) >> 15) |
+               ((data & 0x01000000) >> 17) |
+               ((data & 0x02000000) >> 19) |
+               ((data & 0x04000000) >> 21) |
+               ((data & 0x08000000) >> 23) |
+               ((data & 0x10000000) >> 25) |
+               ((data & 0x20000000) >> 27) |
+               ((data & 0x40000000) >> 29) |
+               ((data & 0x80000000) >> 31);
+    }
+
+    /** General reflection
+     *
+     * @note This is used when we may need to perform run-time computation, so
+     * we need the possibility to produce the optimal run-time RBIT instruction. But
+     * if the compiler doesn't treat RBIT as a built-in, it's useful to have a C fallback
+     * for the constant case, avoiding runtime RBIT(0) computations. This is an
+     * optimization only available for some toolchains; others will always use runtime
+     * RBIT. If we require a constant expression, use reflect_constant instead.
+     *
+     * @param  Register value to be reflected (full 32-bit value)
+     * @return Reflected value (full 32-bit value)
+     */
+#ifdef MSTD_HAS_IS_CONSTANT_EVALUATED
+    static constexpr uint32_t reflect(uint32_t data)
+    {
+        return mstd::is_constant_evaluated() ? reflect_constant(data) : __RBIT(data);
+    }
+#else
+    static uint32_t reflect(uint32_t data)
+    {
+        return __RBIT(data);
+    }
+#endif
+
+    /** Data bytes may need to be reflected.
+     *
+     * @param  data value to be reflected (bottom 8 bits)
+     * @return Reflected value (bottom 8 bits)
+     */
+    static
+    uint_fast32_t reflect_byte(uint_fast32_t data)
+    {
+        return reflect(data) >> 24;
+    }
+
+    /** Get the current CRC polynomial, reflected at bottom of register.
+     *
+     * @return  Reflected polynomial value (so x^width term would be at bit -1)
+     */
+    static constexpr uint32_t get_reflected_polynomial()
+    {
+        return shift_right(reflect_constant(polynomial));
+    }
+
+    /** Get the current CRC polynomial, at top of register.
+     *
+     * @return  Shifted polynomial value (so x^width term would be at bit 32)
+     */
+    static constexpr uint32_t get_top_polynomial()
+    {
+        return shift_left(polynomial);
+    }
+
+    const uint32_t _initial_value;
+    const uint32_t _final_xor;
+    const bool _reflect_data;
+    const bool _reflect_remainder;
+
+    // *INDENT-OFF*
+    using crc_table_t = std::conditional_t<width <= 8,  uint8_t,
+                        std::conditional_t<width <= 16, uint16_t,
+                                                        uint32_t
+                                          >>;
+    // *INDENT-ON*
+
+#if MBED_CRC_TABLE_SIZE > 0
+    /* Tables only actually defined for mode == TABLE, and certain polynomials - see below */
+    static const crc_table_t _crc_table[MBED_CRC_TABLE_SIZE];
+#endif
+
+    static constexpr uint32_t adjust_initial_value(uint32_t initial_xor, bool reflect_data)
+    {
+        if (mode == CrcMode::BITWISE) {
+            /* For bitwise calculation, CRC register is reflected if data is, to match input.
+             * (MSB at bottom of register). If not reflected, it is at the top of the register
+             * (MSB at top of register).
+             */
+            return reflect_data ? reflect_crc(initial_xor) : shift_left(initial_xor);
+        } else if (mode == CrcMode::TABLE) {
+            /* For table calculation, CRC value is reflected, to match tables.
+             * (MSB at bottom of register). */
+            return reflect_crc(initial_xor);
+        } else { // CrcMode::HARDWARE
+            return initial_xor;
+        }
+    }
+
+    /** Acquire exclusive access to CRC hardware/software.
+     */
+    static void lock()
+    {
+// #if DEVICE_CRC
+//         if (mode == CrcMode::HARDWARE) {
+//             mbed_crc_mutex->lock();
+//         }
+// #endif
+    }
+
+    /** Release exclusive access to CRC hardware/software.
+     */
+    static void unlock()
+    {
+// #if DEVICE_CRC
+//         if (mode == CrcMode::HARDWARE) {
+//             mbed_crc_mutex->unlock();
+//         }
+// #endif
+    }
+
+    /** Get the CRC data mask.
+     *
+     * @return  CRC data mask is generated based on current CRC width
+     */
+    static constexpr uint32_t get_crc_mask()
+    {
+        return (uint32_t)((uint32_t)2U << (width - 1)) - 1U;
+    }
+
+    /** CRC values may need to be reflected.
+     *
+     * @param  CRC value to be reflected (width bits at bottom of 32-bit word)
+     * @return Reflected value (still at bottom of 32-bit word)
+     */
+    static
+    uint32_t reflect_crc(uint32_t data)
+    {
+        return reflect(data) >> (32 - width);
+    }
+
+    /** Register values may need to be shifted left.
+     *
+     * @param  Register value to be shifted up (in bottom width bits)
+     * @return Shifted value (in top width bits)
+     */
+    static constexpr uint32_t shift_left(uint32_t data)
+    {
+        return data << (32 - width);
+    }
+
+    /** Register values may need to be shifted right.
+     *
+     * @param  Register value to be shifted right (in top width bits)
+     * @return  Shifted value (in bottom width bits)
+     */
+    static constexpr uint32_t shift_right(uint32_t data)
+    {
+        return data >> (32 - width);
+    }
+
+    /* Check to see if we can do assembler optimizations */
+#if (defined __GNUC__ || defined __clang__) && \
+    (defined __arm__ || defined __ARM_ARCH)
+#if (__ARM_ARCH_7M__      == 1U) || \
+    (__ARM_ARCH_7EM__     == 1U) || \
+    (__ARM_ARCH_8M_MAIN__ == 1U) || \
+    (__ARM_ARCH_8_1M_MAIN__ == 1U) || \
+    (__ARM_ARCH_7A__      == 1U)
+    /* ARM that has Thumb-2 - same unified assembly is good for either ARM or Thumb state (LSRS; IT CS; EORCS reg/imm) */
+#define MBED_CRC_ARM_THUMB2     1
+#define MBED_CRC_THUMB1         0
+#elif (__ARM_ARCH_6M__      == 1U) || \
+      (__ARM_ARCH_8M_BASE__ == 1U)
+    /* Thumb-1-only ARM-M device - use Thumb-1 compatible assembly with branch (LSRS; BCC; EORS reg) */
+#define MBED_CRC_ARM_THUMB2     0
+#define MBED_CRC_THUMB1         1
+#else // __ARM_ARCH_xxx
+#error "Unknown ARM architecture for CRC optimization"
+#endif // __ARM_ARCH_xxx
+#else // __arm__ || defined __ICC_ARM__ || defined __ARM_ARCH
+    /* Seem to be compiling for non-ARM, or an unsupported toolchain, so stick with C implementations */
+#define MBED_CRC_ARM_THUMB2     0
+#define MBED_CRC_THUMB1         0
+#endif
+
+    // *INDENT-OFF*
+    /** Process 1 bit of non-reflected CRC
+     *
+     * Shift the p_crc register left 1 bit - if a one is shifted
+     * out, exclusive-or with the polynomial mask.
+     *
+     * Assembler optimizations can be applied here, to make
+     * use of the CPU's carry output from shifts.
+     *
+     * @param  p_crc input register value
+     * @return updated register value
+     */
+    static uint_fast32_t do_1_bit_normal(uint_fast32_t p_crc)
+    {
+#if MBED_CRC_ARM_THUMB2
+        __asm(".syntax unified\n\t"
+              "LSLS"  "\t%[p_crc], %[p_crc], #1\n\t"
+              "IT"    "\tCS\n\t"
+              "EORCS" "\t%[p_crc], %[poly]"
+              : [p_crc] "+&r" (p_crc)
+              : [poly] "rI" (get_top_polynomial())
+              : "cc");
+#elif MBED_CRC_THUMB1
+        __asm(".syntax unified\n\t"
+              "LSLS"  "\t%[p_crc], %[p_crc], #1\n\t"
+              "BCC"   "\t%=f\n\t"
+              "EORS"  "\t%[p_crc], %[poly]\n"
+              "%=:"
+              : [p_crc] "+&l" (p_crc)
+              : [poly] "l" (get_top_polynomial())
+              : "cc");
+#else
+        if (p_crc & 0x80000000) {
+            p_crc = (p_crc << 1) ^ get_top_polynomial();
+        } else {
+            p_crc = (p_crc << 1);
+        }
+#endif
+        return p_crc;
+    }
+
+    /** Process 1 bit of reflected CRC
+     *
+     * Shift the p_crc register right 1 bit - if a one is shifted
+     * out, exclusive-or with the polynomial mask.
+     *
+     * Assembler optimizations can be applied here, to make
+     * use of the CPU's carry output from shifts.
+     *
+     * @param  p_crc input register value
+     * @return updated register value
+     */
+    static uint_fast32_t do_1_bit_reflected(uint_fast32_t p_crc)
+    {
+#if MBED_CRC_ARM_THUMB2
+        __asm(".syntax unified\n\t"
+              "LSRS"  "\t%[p_crc], %[p_crc], #1\n\t"
+              "IT"    "\tCS\n\t"
+              "EORCS" "\t%[p_crc], %[poly]"
+              : [p_crc] "+&r" (p_crc)
+              : [poly] "rI" (get_reflected_polynomial())
+              : "cc");
+#elif MBED_CRC_THUMB1
+        __asm(".syntax unified\n\t"
+              "LSRS"  "\t%[p_crc], %[p_crc], #1\n\t"
+              "BCC"   "\t%=f\n\t"
+              "EORS"  "\t%[p_crc], %[poly]\n"
+              "%=:"
+              : [p_crc] "+&l" (p_crc)
+              : [poly] "l" (get_reflected_polynomial())
+              : "cc");
+#else
+        if (p_crc & 1) {
+            p_crc = (p_crc >> 1) ^ get_reflected_polynomial();
+        } else {
+            p_crc = (p_crc >> 1);
+        }
+#endif
+        return p_crc;
+    }
+    // *INDENT-ON*
+
+    /** Bitwise CRC computation.
+     *
+     * @param  buffer  data buffer
+     * @param  size  size of the data
+     * @param  crc  CRC value is filled in, but the value is not the final
+     * @return  0  on success or a negative error code on failure
+     */
+    template<CrcMode mode_ = mode>
+    std::enable_if_t<mode_ == CrcMode::BITWISE, int32_t>
+    do_compute_partial(const uint8_t *data, crc_data_size_t size, uint32_t *crc) const
+    {
+        uint_fast32_t p_crc = *crc;
+
+        if (_reflect_data) {
+            /* Everything is reflected to match data - MSB of polynomial at bottom of 32-bit register */
+            for (crc_data_size_t byte = 0; byte < size; byte++) {
+                p_crc ^= data[byte];
+
+                // Perform modulo-2 division, a bit at a time
+                for (unsigned int bit = 8; bit > 0; --bit) {
+                    p_crc = do_1_bit_reflected(p_crc);
+                }
+            }
+        } else {
+            /* Polynomial is shifted to put MSB of polynomial at top of 32-bit register */
+            for (crc_data_size_t byte = 0; byte < size; byte++) {
+                p_crc ^= (uint_fast32_t) data[byte] << 24;
+
+                // Perform modulo-2 division, a bit at a time
+                for (unsigned int bit = 8; bit > 0; --bit) {
+                    p_crc = do_1_bit_normal(p_crc);
+                }
+            }
+        }
+
+        *crc = p_crc;
+
+        return 0;
+    }
+
+#if MBED_CRC_TABLE_SIZE > 0
+    /** CRC computation using ROM tables.
+    *
+    * @param  buffer  data buffer
+    * @param  size  size of the data
+    * @param  crc  CRC value is filled in, but the value is not the final
+    * @return  0  on success or a negative error code on failure
+    */
+    template<CrcMode mode_ = mode>
+    std::enable_if_t<mode_ == CrcMode::TABLE, int32_t>
+    do_compute_partial(const uint8_t *data, crc_data_size_t size, uint32_t *crc) const
+    {
+        uint_fast32_t p_crc = *crc;
+        // GCC has been observed to not hoist the load of _reflect_data out of the loop
+        // Note the inversion because table and CRC are reflected - data must be
+        bool reflect = !_reflect_data;
+
+        for (crc_data_size_t byte = 0; byte < size; byte++) {
+            uint_fast32_t data_byte = data[byte];
+            if (reflect) {
+                data_byte = reflect_byte(data_byte);
+            }
+#if MBED_CRC_TABLE_SIZE == 16
+            p_crc = _crc_table[(data_byte ^ p_crc) & 0xF] ^ (p_crc >> 4);
+            data_byte >>= 4;
+            p_crc = _crc_table[(data_byte ^ p_crc) & 0xF] ^ (p_crc >> 4);
+#else
+            p_crc = _crc_table[(data_byte ^ p_crc) & 0xFF] ^ (p_crc >> 8);
+#endif
+        }
+        *crc = p_crc;
+        return 0;
+    }
+#endif
+
+#ifdef DEVICE_CRC
+    /** Hardware CRC computation.
+     *
+     * @param  buffer  data buffer
+     * @param  size  size of the data
+     * @return  0  on success or a negative error code on failure
+     */
+    template<CrcMode mode_ = mode>
+    std::enable_if_t<mode_ == CrcMode::HARDWARE, int32_t>
+    do_compute_partial(const uint8_t *data, crc_data_size_t size, uint32_t *) const
+    {
+        hal_crc_compute_partial(data, size);
+        return 0;
+    }
+#endif
+
+};
+
+#if MBED_CRC_TABLE_SIZE > 0
+/* Declarations of the tables we provide. (Not strictly needed, but compilers
+ * can warn if they see us using the template without a generic definition, so
+ * let it know we have provided these specialisations.)
+ */
+template<>
+const uint8_t MbedCRC<POLY_7BIT_SD, 7, CrcMode::TABLE>::_crc_table[MBED_CRC_TABLE_SIZE];
+
+template<>
+const uint8_t MbedCRC<POLY_8BIT_CCITT, 8, CrcMode::TABLE>::_crc_table[MBED_CRC_TABLE_SIZE];
+
+template<>
+const uint16_t MbedCRC<POLY_16BIT_CCITT, 16, CrcMode::TABLE>::_crc_table[MBED_CRC_TABLE_SIZE];
+
+template<>
+const uint16_t MbedCRC<POLY_16BIT_IBM, 16, CrcMode::TABLE>::_crc_table[MBED_CRC_TABLE_SIZE];
+
+template<>
+const uint32_t MbedCRC<POLY_32BIT_ANSI, 32, CrcMode::TABLE>::_crc_table[MBED_CRC_TABLE_SIZE];
+
+#endif // MBED_CRC_TABLE_SIZE > 0
+
+} // namespace impl
+
+#endif // !defined(DOXYGEN_ONLY)
+
+/** @}*/
+/** @}*/
+
+// } // namespace mbed
+
+#endif // __cplusplus
+
+/* Internal helper for mbed_error.c crash recovery */
+#ifdef __cplusplus
+extern "C"
+#endif
+uint32_t mbed_tiny_compute_crc32(const void *data, int datalen);
+
+#endif
diff --git a/libraries/KVStore/src/TDBStore.cpp b/libraries/KVStore/src/TDBStore.cpp
new file mode 100644
index 000000000..ab28ec12e
--- /dev/null
+++ b/libraries/KVStore/src/TDBStore.cpp
@@ -0,0 +1,1512 @@
+/*
+ * 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 <algorithm>
+#include <string.h>
+#include <stdio.h>
+#include "MbedCRC.h"
+
+using namespace mbed;
+
+// --------------------------------------------------------- Definitions ----------------------------------------------------------
+
+static const uint32_t delete_flag = (1UL << 31);
+static const uint32_t internal_flags = delete_flag;
+// Only write once flag is supported, other two are kept in storage but ignored
+static const uint32_t supported_flags = KVStore::WRITE_ONCE_FLAG | KVStore::REQUIRE_CONFIDENTIALITY_FLAG | KVStore::REQUIRE_REPLAY_PROTECTION_FLAG;
+
+namespace {
+
+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;
+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_ERASED,
+    TDBSTORE_AREA_STATE_INVALID,
+    TDBSTORE_AREA_STATE_VALID,
+} area_state_e;
+
+typedef struct {
+    uint16_t trailer_size;
+    uint16_t data_size;
+    uint32_t crc;
+} reserved_trailer_t;
+
+static const size_t min_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 {
+    int iterator_num;
+    uint32_t ram_table_ind;
+    char *prefix;
+} key_iterator_handle_t;
+
+} // anonymous namespace
+
+
+// -------------------------------------------------- Local Functions Declaration ----------------------------------------------------
+
+// -------------------------------------------------- Functions Implementation ----------------------------------------------------
+
+static inline uint32_t align_up(uint32_t val, uint32_t size)
+{
+    return (((val - 1) / size) + 1) * size;
+}
+
+static inline uint32_t align_down(uint64_t val, uint64_t size)
+{
+    return (((val) / size)) * size;
+}
+
+
+static uint32_t calc_crc(uint32_t init_crc, uint32_t data_size, const void *data_buf)
+{
+    uint32_t crc;
+    MbedCRC<POLY_32BIT_ANSI, 32> ct(init_crc, 0x0, true, false);
+    ct.compute(data_buf, data_size, &crc);
+    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),
+    _master_record_size(0), _is_initialized(false), _active_area(0), _active_area_version(0), _size(0),
+    _area_params{}, _prog_size(0), _work_buf(0), _work_buf_size(0), _key_buf(0), _inc_set_handle(0)
+{
+    for (int i = 0; i < _num_areas; i++) {
+        _area_params[i] = { 0 };
+    }
+    for (int i = 0; i < _max_open_iterators; i++) {
+        _iterator_table[i] = { 0 };
+    }
+}
+
+TDBStore::~TDBStore()
+{
+    deinit();
+}
+
+int TDBStore::read_area(uint8_t area, uint32_t offset, uint32_t size, void *buf)
+{
+    //Check that we are not crossing area boundary
+    if (offset + size > _size) {
+        return KVSTORE_ERROR_READ_FAILED;
+    }
+    int os_ret = _buff_bd->read(buf, _area_params[area].address + offset, size);
+
+    if (os_ret) {
+        return KVSTORE_ERROR_READ_FAILED;
+    }
+
+    return KVSTORE_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 KVSTORE_ERROR_WRITE_FAILED;
+    }
+
+    return KVSTORE_SUCCESS;
+}
+
+int TDBStore::erase_area(uint8_t area, uint32_t offset, uint32_t size)
+{
+    uint32_t bd_offset = _area_params[area].address + offset;
+
+    int ret = _buff_bd->erase(bd_offset, size);
+    if (ret) {
+        return ret;
+    }
+
+    if (_buff_bd->get_erase_value() == -1) {
+        // We need to simulate erase to wipe records, as our block device
+        // may not do it. Program in chunks of _work_buf_size if the minimum
+        // program size is too small (e.g. one-byte) to avoid performance
+        // issues.
+        MBED_ASSERT(_work_buf != nullptr);
+        MBED_ASSERT(_work_buf_size != 0);
+        memset(_work_buf, 0xFF, _work_buf_size);
+        while (size) {
+            uint32_t chunk = std::min<uint32_t>(_work_buf_size, size);
+            ret = _buff_bd->program(_work_buf, bd_offset, chunk);
+            if (ret) {
+                return ret;
+            }
+            size -= chunk;
+            bd_offset += chunk;
+        }
+    }
+    return KVSTORE_SUCCESS;
+}
+
+void TDBStore::calc_area_params()
+{
+    // TDBStore can't exceed 32 bits
+    bd_size_t bd_size = std::min<bd_size_t>(_bd->size(), 0x80000000L);
+
+    memset(_area_params, 0, sizeof(_area_params));
+    size_t area_0_size = 0;
+    size_t area_1_size = 0;
+
+    // The size calculations are a bit complex because we need to make sure we're
+    // always aligned to an erase block boundary (whose size may not be uniform
+    // across the address space), and we also need to make sure that the first
+    // area never goes over half of the total size even if bd_size is an odd
+    // number of sectors.
+    while (true) {
+        bd_size_t erase_unit_size = _bd->get_erase_size(area_0_size);
+        if (area_0_size + erase_unit_size <= (bd_size / 2)) {
+            area_0_size += erase_unit_size;
+        } else {
+            break;
+        }
+    }
+
+    while (true) {
+        bd_size_t erase_unit_size = _bd->get_erase_size(area_0_size + area_1_size);
+        if (area_1_size + erase_unit_size <= (bd_size / 2)) {
+            area_1_size += erase_unit_size;
+        } else {
+            break;
+        }
+    }
+
+    _area_params[0].address = 0;
+    _area_params[0].size = area_0_size;
+    _area_params[1].address = area_0_size;
+    _area_params[1].size = area_1_size;
+
+    // The areas must be of same size
+    MBED_ASSERT(_area_params[0].size == _area_params[1].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 = KVSTORE_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 KVSTORE_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 KVSTORE_ERROR_INVALID_DATA_DETECTED;
+    }
+
+    total_size = key_size + data_size;
+
+    // Make sure our read sizes didn't cause any wraparounds
+    if ((total_size < key_size) || (total_size < data_size)) {
+        return KVSTORE_ERROR_INVALID_DATA_DETECTED;
+    }
+
+    if (offset + total_size >= _size) {
+        return KVSTORE_ERROR_INVALID_DATA_DETECTED;
+    }
+
+    if (data_offset > data_size) {
+        return KVSTORE_ERROR_INVALID_SIZE;
+    }
+
+    actual_data_size = std::min<size_t>(data_buf_size, data_size - data_offset);
+
+    if (copy_data && actual_data_size && !data_buf) {
+        return KVSTORE_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (validate) {
+        // Calculate CRC on header (excluding CRC itself)
+        crc = calc_crc(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<uint8_t *>(user_key_ptr);
+                chunk_size = key_size;
+                user_key_ptr[key_size] = '\0';
+            } else {
+                dest_buf = _work_buf;
+                chunk_size = std::min<size_t>(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<size_t>(_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<uint8_t *>(data_buf);
+            } else {
+                chunk_size = std::min<size_t>(_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 = calc_crc(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 = KVSTORE_ERROR_ITEM_NOT_FOUND;
+                }
+            }
+
+            if (calc_hash) {
+                hash = calc_crc(hash, chunk_size, dest_buf);
+#ifdef KVSTORE_RAM_TABLE_NO_CRC_CHECK
+                next_offset = align_up(offset + total_size, _prog_size);
+                return ret;
+#endif /* KVSTORE_RAM_TABLE_NO_CRC_CHECK */
+            }
+
+            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 = KVSTORE_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 = KVSTORE_ERROR_ITEM_NOT_FOUND;
+    uint32_t actual_data_size;
+    uint32_t flags, dummy_hash, next_offset;
+
+
+    hash = calc_crc(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 KVSTORE_ERROR_ITEM_NOT_FOUND;
+        }
+        ret = read_record(_active_area, offset, const_cast<char *>(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 != KVSTORE_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 = 0;
+    uint32_t hash = 0, ram_table_ind = 0;
+    inc_set_handle_t *ih;
+    bool need_gc = false;
+
+    if (!is_valid_key(key)) {
+        return KVSTORE_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (create_flags & ~(supported_flags | internal_flags)) {
+        return KVSTORE_ERROR_INVALID_ARGUMENT;
+    }
+
+    *handle = reinterpret_cast<set_handle_t>(_inc_set_handle);
+    ih = reinterpret_cast<inc_set_handle_t *>(*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;
+        ram_table_ind = 0;
+        hash = 0;
+    } 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 = KVSTORE_ERROR_MEDIA_FULL;
+            goto fail;
+        }
+
+        ret = find_record(_active_area, key, offset, ram_table_ind, hash);
+
+        if (ret == KVSTORE_SUCCESS) {
+            ret = read_area(_active_area, offset, sizeof(ih->header), &ih->header);
+            if (ret) {
+                goto fail;
+            }
+            if (ih->header.flags & WRITE_ONCE_FLAG) {
+                ret = KVSTORE_ERROR_WRITE_PROTECTED;
+                goto fail;
+            }
+            ih->new_key = false;
+        } else if (ret == KVSTORE_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 = KVSTORE_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 = calc_crc(initial_crc, sizeof(record_header_t) - sizeof(ih->header.crc), &ih->header);
+    ih->header.crc = calc_crc(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) {
+        need_gc = true;
+        goto fail;
+    }
+    ih->bd_curr_offset += ih->header.key_size;
+    goto end;
+
+fail:
+    if ((need_gc) && (ih->bd_base_offset != _master_record_offset)) {
+        garbage_collection();
+    }
+    // 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 = KVSTORE_SUCCESS;
+    inc_set_handle_t *ih;
+    bool need_gc = false;
+
+    if (handle != _inc_set_handle) {
+        return KVSTORE_ERROR_INVALID_ARGUMENT;
+    }
+
+    if (!value_data && data_size) {
+        return KVSTORE_ERROR_INVALID_ARGUMENT;
+    }
+
+    _inc_set_mutex.lock();
+
+    ih = reinterpret_cast<inc_set_handle_t *>(handle);
+
+    if (!ih->header.magic) {
+        ret = KVSTORE_ERROR_INVALID_ARGUMENT;
+        goto end;
+    }
+
+    if (ih->offset_in_data + data_size > ih->header.data_size) {
+        ret = KVSTORE_ERROR_INVALID_SIZE;
+        goto end;
+    }
+
+    // Update CRC with data chunk
+    ih->header.crc = calc_crc(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) {
+        need_gc = true;
+        goto end;
+    }
+    ih->bd_curr_offset += data_size;
+    ih->offset_in_data += data_size;
+
+end:
+    if ((need_gc) && (ih->bd_base_offset != _master_record_offset)) {
+        garbage_collection();
+    }
+
+    _inc_set_mutex.unlock();
+    return ret;
+}
+
+int TDBStore::set_finalize(set_handle_t handle)
+{
+    int os_ret, ret = KVSTORE_SUCCESS;
+    inc_set_handle_t *ih;
+    ram_table_entry_t *ram_table = (ram_table_entry_t *) _ram_table;
+    ram_table_entry_t *entry;
+    bool need_gc = false;
+    uint32_t actual_data_size, hash, flags, next_offset;
+
+    if (handle != _inc_set_handle) {
+        return KVSTORE_ERROR_INVALID_ARGUMENT;
+    }
+
+    ih = reinterpret_cast<inc_set_handle_t *>(handle);
+
+    if (!ih->header.magic) {
+        return KVSTORE_ERROR_INVALID_ARGUMENT;
+    }
+
+    _inc_set_mutex.lock();
+
+    if (ih->offset_in_data != ih->header.data_size) {
+        ret = KVSTORE_ERROR_INVALID_SIZE;
+        need_gc = true;
+        goto end;
+    }
+
+    // Write header
+    ret = write_area(_active_area, ih->bd_base_offset, sizeof(record_header_t), &ih->header);
+    if (ret) {
+        need_gc = true;
+        goto end;
+    }
+
+    // Need to flush buffered BD as our record is totally written now
+    os_ret = _buff_bd->sync();
+    if (os_ret) {
+        ret = KVSTORE_ERROR_WRITE_FAILED;
+        need_gc = true;
+        goto end;
+    }
+
+    // In master record case we don't update RAM table
+    if (ih->bd_base_offset == _master_record_offset) {
+        goto end;
+    }
+
+    // Writes may fail without returning a failure (especially in flash components). Reread the record
+    // to ensure write success (this won't read the data anywhere - just use the CRC calculation).
+    ret = read_record(_active_area, ih->bd_base_offset, 0, 0, (uint32_t) -1,
+                      actual_data_size, 0, false, false, false, false,
+                      hash, flags, next_offset);
+    if (ret) {
+        need_gc = true;
+        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));
+        }
+        update_all_iterators(false, 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++;
+            update_all_iterators(true, ih->ram_table_ind);
+        }
+        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);
+
+    // Safety check: If there seems to be valid keys on the free space
+    // we should erase one sector more, just to ensure that in case of power failure
+    // next init() would not extend the scan phase to that section as well.
+    os_ret = read_record(_active_area, _free_space_offset, 0, 0, 0, actual_data_size, 0,
+                         false, false, false, false, hash, flags, next_offset);
+    if (os_ret == KVSTORE_SUCCESS) {
+        check_erase_before_write(_active_area, _free_space_offset, sizeof(record_header_t));
+    }
+
+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) {
+        if (need_gc) {
+            garbage_collection();
+        }
+        _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 KVSTORE_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 KVSTORE_ERROR_INVALID_ARGUMENT;
+    }
+
+    _mutex.lock();
+
+    ret = find_record(_active_area, key, bd_offset, ram_table_ind, hash);
+
+    if (ret != KVSTORE_SUCCESS) {
+        goto end;
+    }
+
+    ret = read_record(_active_area, bd_offset, const_cast<char *>(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 KVSTORE_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<char *>(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 + _master_record_size;
+    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);;
+
+
+    if (to_offset + total_size > _size) {
+        // We are trying to copy more that the are can hold
+        return KVSTORE_ERROR_MEDIA_FULL;
+    }
+    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);
+    // The record header takes up whole program units
+    memset(_work_buf, 0, chunk_size);
+    memcpy(_work_buf, &header, sizeof(record_header_t));
+    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;
+
+    while (total_size) {
+        chunk_size = std::min<size_t>(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 KVSTORE_SUCCESS;
+}
+
+int TDBStore::garbage_collection()
+{
+    ram_table_entry_t *ram_table = (ram_table_entry_t *) _ram_table;
+    uint32_t to_offset, to_next_offset;
+    int ret;
+    size_t ind;
+
+    // Reset the standby area
+    ret = reset_area(1 - _active_area);
+    if (ret) {
+        return ret;
+    }
+
+    to_offset = _master_record_offset + _master_record_size;
+
+    // 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;
+    }
+
+    return KVSTORE_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 = KVSTORE_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 + sizeof(record_header_t) < _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 != KVSTORE_SUCCESS) && (ret != KVSTORE_ERROR_ITEM_NOT_FOUND)) {
+            goto end;
+        }
+
+        uint32_t save_offset = offset;
+        offset = next_offset;
+
+        if (ret == KVSTORE_ERROR_ITEM_NOT_FOUND) {
+            // Key doesn't exist, need to add it to RAM table
+            ret = KVSTORE_SUCCESS;
+
+            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<void **>(&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];
+    memset(new_ram_table, 0, sizeof(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 KVSTORE_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 ret = KVSTORE_SUCCESS;
+    uint16_t versions[_num_areas];
+
+    _mutex.lock();
+
+    if (_is_initialized) {
+        goto end;
+    }
+
+    _max_keys = initial_max_keys;
+
+    ram_table = new ram_table_entry_t[_max_keys];
+    memset(ram_table, 0, sizeof(ram_table_entry_t) * _max_keys);
+    _ram_table = ram_table;
+    _num_keys = 0;
+
+    _size = (size_t) -1;
+
+    _buff_bd = new BufferedBlockDevice(_bd);
+    ret = _buff_bd->init();
+    if (ret) {
+        goto fail;
+    }
+
+    _prog_size = _bd->get_program_size();
+    _work_buf_size = std::max<size_t>(_prog_size, min_work_buf_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));
+    memset(_iterator_table, 0, sizeof(_iterator_table));
+
+    _master_record_offset = align_up(RESERVED_AREA_SIZE + sizeof(reserved_trailer_t), _prog_size);
+    _master_record_size = record_size(master_rec_key, sizeof(master_record_data_t));
+
+    calc_area_params();
+
+    /* Minimum space required by Reserved area and master record */
+    MBED_ASSERT(_bd->size()
+                >= (align_up(RESERVED_AREA_SIZE + sizeof(reserved_trailer_t), _prog_size)
+                    + record_size(master_rec_key, sizeof(master_record_data_t))));
+
+    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<char *>(master_rec_key),
+                          &master_rec, sizeof(master_rec), actual_data_size, 0, false, true, true, false,
+                          hash, flags, next_offset);
+        if ((ret != KVSTORE_SUCCESS) && (ret != KVSTORE_ERROR_INVALID_DATA_DETECTED)) {
+            // KVSTORE_ERROR(ret, "TDBSTORE: Unable to read record at init"); // FIXME
+        }
+
+        // Master record may be either corrupt or erased
+        if (ret == KVSTORE_ERROR_INVALID_DATA_DETECTED) {
+            area_state[area] = TDBSTORE_AREA_STATE_INVALID;
+            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_INVALID) && (area_state[1] == TDBSTORE_AREA_STATE_INVALID)) {
+        reset_area(0);
+        _active_area = 0;
+        _active_area_version = 1;
+        area_state[0] = TDBSTORE_AREA_STATE_ERASED;
+        ret = write_master_record(_active_area, _active_area_version, _free_space_offset);
+        if (ret) {
+            // KVSTORE_ERROR(ret, "TDBSTORE: Unable to write master record at init"); // FIXME
+        }
+        // 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).
+    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];
+    }
+
+    // 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();
+
+    // build_ram_table() scans all keys, until invalid data found.
+    // Therefore INVALID_DATA is not considered error.
+    if ((ret != KVSTORE_SUCCESS) && (ret != KVSTORE_ERROR_INVALID_DATA_DETECTED)) {
+        goto fail;
+    }
+
+end:
+    _is_initialized = true;
+    _mutex.unlock();
+    return KVSTORE_SUCCESS;
+fail:
+    delete[] ram_table;
+    delete _buff_bd;
+    delete[] _work_buf;
+    delete[] _key_buf;
+    delete reinterpret_cast<inc_set_handle_t *>(_inc_set_handle);
+    _ram_table = nullptr;
+    _buff_bd = nullptr;
+    _work_buf = nullptr;
+    _key_buf = nullptr;
+    _inc_set_handle = nullptr;
+    _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;
+        delete reinterpret_cast<inc_set_handle_t *>(_inc_set_handle);
+    }
+
+    _is_initialized = false;
+    _mutex.unlock();
+
+    return KVSTORE_SUCCESS;
+}
+
+int TDBStore::reset_area(uint8_t area)
+{
+    uint8_t buf[RESERVED_AREA_SIZE + sizeof(reserved_trailer_t)];
+    int ret;
+    bool copy_reserved_data = do_reserved_data_get(buf, sizeof(buf), 0, buf + RESERVED_AREA_SIZE) == KVSTORE_SUCCESS;
+
+    // Erase reserved area and master record
+    ret = check_erase_before_write(area, 0, _master_record_offset + _master_record_size + _prog_size, true);
+    if (ret) {
+        return ret;
+    }
+    if (copy_reserved_data) {
+        ret = write_area(area, 0, sizeof(buf), buf);
+    }
+    return ret;
+}
+
+int TDBStore::reset()
+{
+    uint8_t area;
+    int ret;
+
+    if (!_is_initialized) {
+        return KVSTORE_ERROR_NOT_READY;
+    }
+
+    _mutex.lock();
+
+    // Reset both areas
+    for (area = 0; area < _num_areas; area++) {
+        ret = check_erase_before_write(area, 0, _master_record_offset + _master_record_size + _prog_size, true);
+        if (ret) {
+            goto end;
+        }
+    }
+
+    _active_area = 0;
+    _num_keys = 0;
+    _free_space_offset = _master_record_offset;
+    _active_area_version = 1;
+    memset(_ram_table, 0, sizeof(ram_table_entry_t) * _max_keys);
+    // 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;
+    int ret = KVSTORE_SUCCESS;
+
+    if (!_is_initialized) {
+        return KVSTORE_ERROR_NOT_READY;
+    }
+
+    if (!it) {
+        return KVSTORE_ERROR_INVALID_ARGUMENT;
+    }
+
+    _mutex.lock();
+
+    int it_num;
+    for (it_num = 0; it_num < _max_open_iterators; it_num++) {
+        if (!_iterator_table[it_num]) {
+            break;
+        }
+    }
+
+    if (it_num == _max_open_iterators) {
+        ret = KVSTORE_ERROR_OUT_OF_RESOURCES;
+        goto end;
+    }
+
+    handle = new key_iterator_handle_t;
+    *it = reinterpret_cast<iterator_t>(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;
+    handle->iterator_num = it_num;
+    _iterator_table[it_num] = handle;
+
+end:
+    _mutex.unlock();
+    return ret;
+}
+
+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 KVSTORE_ERROR_NOT_READY;
+    }
+
+    _mutex.lock();
+
+    handle = reinterpret_cast<key_iterator_handle_t *>(it);
+
+    ret = KVSTORE_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 = KVSTORE_ERROR_INVALID_SIZE;
+                goto end;
+            }
+            strcpy(key, _key_buf);
+        } else {
+            ret = KVSTORE_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 KVSTORE_ERROR_NOT_READY;
+    }
+
+    _mutex.lock();
+
+    handle = reinterpret_cast<key_iterator_handle_t *>(it);
+    delete[] handle->prefix;
+    _iterator_table[handle->iterator_num] = 0;
+    delete handle;
+
+    _mutex.unlock();
+
+    return KVSTORE_SUCCESS;
+}
+
+void TDBStore::update_all_iterators(bool added, uint32_t ram_table_ind)
+{
+    for (int it_num = 0; it_num < _max_open_iterators; it_num++) {
+        key_iterator_handle_t *handle = static_cast <key_iterator_handle_t *>(_iterator_table[it_num]);
+        if (!handle) {
+            continue;
+        }
+
+        if (ram_table_ind >= handle->ram_table_ind) {
+            continue;
+        }
+
+        if (added) {
+            handle->ram_table_ind++;
+        } else {
+            handle->ram_table_ind--;
+        }
+    }
+}
+
+int TDBStore::reserved_data_set(const void *reserved_data, size_t reserved_data_buf_size)
+{
+    reserved_trailer_t trailer;
+    int ret;
+
+    if (reserved_data_buf_size > RESERVED_AREA_SIZE) {
+        return KVSTORE_ERROR_INVALID_SIZE;
+    }
+
+    _mutex.lock();
+
+    ret = do_reserved_data_get(0, 0);
+    if (ret == KVSTORE_SUCCESS) {
+        ret = KVSTORE_ERROR_WRITE_FAILED;
+        goto end;
+    }
+
+    trailer.trailer_size = sizeof(trailer);
+    trailer.data_size = reserved_data_buf_size;
+    trailer.crc = calc_crc(initial_crc, reserved_data_buf_size, reserved_data);
+
+    // Erase the header of non-active area, just to make sure that we can write to it
+    // In case garbage collection has not yet been run, the area can be un-erased
+    ret = reset_area(1 - _active_area);
+    if (ret) {
+        goto end;
+    }
+
+    /*
+     * Write to both areas
+     * Both must success, as they are required to be erased when TDBStore initializes
+     * its area
+     */
+    for (int i = 0; i < _num_areas; ++i) {
+        ret = write_area(i, 0, reserved_data_buf_size, reserved_data);
+        if (ret) {
+            goto end;
+        }
+        ret = write_area(i, RESERVED_AREA_SIZE, sizeof(trailer), &trailer);
+        if (ret) {
+            goto end;
+        }
+        ret = _buff_bd->sync();
+        if (ret) {
+            goto end;
+        }
+    }
+    ret = KVSTORE_SUCCESS;
+end:
+    _mutex.unlock();
+    return ret;
+}
+
+int TDBStore::do_reserved_data_get(void *reserved_data, size_t reserved_data_buf_size, size_t *actual_data_size, void *copy_trailer)
+{
+    reserved_trailer_t trailer;
+    uint8_t buf[RESERVED_AREA_SIZE];
+    int ret;
+    uint32_t crc;
+
+    /*
+     * Try to keep reserved data identical on both areas, therefore
+     * we can return any of these data, if the checmsum is correct.
+     */
+    for (int i = 0; i < _num_areas; ++i) {
+        ret = read_area(i, RESERVED_AREA_SIZE, sizeof(trailer), &trailer);
+        if (ret) {
+            return ret;
+        }
+
+        // First validy check: is the trailer header size correct
+        if (trailer.trailer_size != sizeof(trailer)) {
+            continue;
+        }
+        // Second validy check: Is the data too big (corrupt header)
+        if (trailer.data_size > RESERVED_AREA_SIZE) {
+            continue;
+        }
+
+        // Next, verify the checksum
+        ret = read_area(i, 0, trailer.data_size, buf);
+        if (ret) {
+            return ret;
+        }
+        crc = calc_crc(initial_crc, trailer.data_size, buf);
+        if (crc == trailer.crc) {
+            // Correct data, copy it and return to caller
+            if (reserved_data) {
+                if (reserved_data_buf_size < trailer.data_size) {
+                    return KVSTORE_ERROR_INVALID_SIZE;
+                }
+                memcpy(reserved_data, buf, trailer.data_size);
+            }
+            if (actual_data_size) {
+                *actual_data_size = trailer.data_size;
+            }
+            if (copy_trailer) {
+                memcpy(copy_trailer, &trailer, sizeof(trailer));
+            }
+            return KVSTORE_SUCCESS;
+        }
+    }
+
+    return KVSTORE_ERROR_ITEM_NOT_FOUND;
+}
+
+int TDBStore::reserved_data_get(void *reserved_data, size_t reserved_data_buf_size, size_t *actual_data_size)
+{
+    _mutex.lock();
+    int ret = do_reserved_data_get(reserved_data, reserved_data_buf_size, actual_data_size);
+    _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;
+
+    // The parameter of `BlockDevice::get_erase_size(bd_addr_t addr)`
+    // does not need to be aligned.
+    uint32_t erase_unit = _buff_bd->get_erase_size(bd_offset);
+
+    // Even on a flash device with multiple regions, the start address of
+    // an erase unit is aligned to the current region's unit size.
+    offset_from_start = bd_offset % erase_unit;
+    dist_to_end = erase_unit - offset_from_start;
+}
+
+int TDBStore::check_erase_before_write(uint8_t area, uint32_t offset, uint32_t size, bool force_check)
+{
+    // In order to save init time, we don't check that the entire area is erased.
+    // Instead, whenever reaching an erase unit start erase it.
+    bool erase = false;
+    uint32_t start_offset;
+    uint32_t end_offset;
+    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 == 0 || force_check) {
+            if (!erase) {
+                erase = true;
+                start_offset = offset - offset_from_start;
+            }
+            end_offset = offset + dist;
+        }
+        offset += chunk;
+        size -= chunk;
+    }
+
+    if (erase) {
+        int ret = erase_area(area, start_offset, end_offset - start_offset);
+        if (ret != KVSTORE_SUCCESS) {
+            return KVSTORE_ERROR_WRITE_FAILED;
+        }
+    }
+
+    return KVSTORE_SUCCESS;
+}
diff --git a/libraries/KVStore/src/TDBStore.h b/libraries/KVStore/src/TDBStore.h
new file mode 100644
index 000000000..ab5fdc441
--- /dev/null
+++ b/libraries/KVStore/src/TDBStore.h
@@ -0,0 +1,548 @@
+/*
+ * 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 <stdint.h>
+#include "KVStore.h"
+#include <BlockDevice.h>
+#include <BufferedBlockDevice.h>
+
+// namespace mbed {
+
+/** TDBStore class
+ *
+ *  Lightweight Key Value storage over a block device
+ */
+
+class TDBStore : public mbed::KVStore {
+public:
+
+    static const uint32_t RESERVED_AREA_SIZE = 64;
+
+    /**
+     * @brief Class constructor
+     *
+     * @param[in]  bd                   Underlying block device.
+     *
+     * @returns none
+     */
+    TDBStore(BlockDevice *bd);
+
+    /**
+     * @brief Class destructor
+     *
+     * @returns none
+     */
+    virtual ~TDBStore();
+
+    /**
+     * @brief Initialize TDBStore. If data exists, TDBStore will check the data integrity
+     *        on initialize. If the integrity checks fails, the TDBStore will use GC to collect
+     *        the available data and clean corrupted and erroneous records.
+     *
+     * @returns KVSTORE_SUCCESS                        Success.
+     * @returns Negative error code on failure.
+     */
+    virtual int init();
+
+    /**
+     * @brief Deinitialize TDBStore, release and free resources.
+     *
+     * @returns KVSTORE_SUCCESS                        Success.
+     */
+    virtual int deinit();
+
+
+    /**
+     * @brief Reset TDBStore contents (clear all keys) and reserved data
+     *
+     * @returns KVSTORE_SUCCESS                        Success.
+     *          KVSTORE_ERROR_NOT_READY                Not initialized.
+     *          KVSTORE_ERROR_READ_FAILED              Unable to read from media.
+     *          KVSTORE_ERROR_WRITE_FAILED             Unable to write to media.
+     */
+    virtual int reset();
+
+    /**
+     * @brief Set one TDBStore item, given key and value.
+     *
+     * @param[in]  key                  Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     * @param[in]  buffer               Value data buffer.
+     * @param[in]  size                 Value data size.
+     * @param[in]  create_flags         Flag mask.
+     *
+     * @returns KVSTORE_SUCCESS                        Success.
+     *          KVSTORE_ERROR_NOT_READY                Not initialized.
+     *          KVSTORE_ERROR_READ_FAILED              Unable to read from media.
+     *          KVSTORE_ERROR_WRITE_FAILED             Unable to write to media.
+     *          KVSTORE_ERROR_INVALID_ARGUMENT         Invalid argument given in function arguments.
+     *          KVSTORE_ERROR_INVALID_SIZE             Invalid size given in function arguments.
+     *          KVSTORE_ERROR_MEDIA_FULL               Not enough room on media.
+     *          KVSTORE_ERROR_WRITE_PROTECTED          Already stored with "write once" flag.
+     */
+    virtual int set(const char *key, const void *buffer, size_t size, uint32_t create_flags);
+
+    /**
+     * @brief Get one TDBStore item by given key.
+     *
+     * @param[in]  key                  Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     * @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 KVSTORE_SUCCESS                        Success.
+     *          KVSTORE_ERROR_NOT_READY                Not initialized.
+     *          KVSTORE_ERROR_READ_FAILED              Unable to read from media.
+     *          KVSTORE_ERROR_INVALID_ARGUMENT         Invalid argument given in function arguments.
+     *          KVSTORE_ERROR_INVALID_SIZE             Invalid size given in function arguments.
+     *          KVSTORE_ERROR_INVALID_DATA_DETECTED    Data is corrupt.
+     *          KVSTORE_ERROR_ITEM_NOT_FOUND           No such key.
+     */
+    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. The returned info contains size and flags
+     *
+     * @param[in]  key                  Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     * @param[out] info                 Returned information structure.
+     *
+     * @returns KVSTORE_SUCCESS                        Success.
+     *          KVSTORE_ERROR_NOT_READY                Not initialized.
+     *          KVSTORE_ERROR_READ_FAILED              Unable to read from media.
+     *          KVSTORE_ERROR_INVALID_ARGUMENT         Invalid argument given in function arguments.
+     *          KVSTORE_ERROR_INVALID_DATA_DETECTED    Data is corrupt.
+     *          KVSTORE_ERROR_ITEM_NOT_FOUND           No such key.
+     */
+    virtual int get_info(const char *key, info_t *info);
+
+    /**
+     * @brief Remove a TDBStore item by given key.
+     *
+     * @param[in]  key                  Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     *
+     * @returns KVSTORE_SUCCESS                        Success.
+     *          KVSTORE_ERROR_NOT_READY                Not initialized.
+     *          KVSTORE_ERROR_READ_FAILED              Unable to read from media.
+     *          KVSTORE_ERROR_WRITE_FAILED             Unable to write to media.
+     *          KVSTORE_ERROR_INVALID_ARGUMENT         Invalid argument given in function arguments.
+     *          KVSTORE_ERROR_MEDIA_FULL               Not enough room on media.
+     *          KVSTORE_ERROR_ITEM_NOT_FOUND           No such key.
+     *          KVSTORE_ERROR_WRITE_PROTECTED          Already stored with "write once" flag.
+     */
+    virtual int remove(const char *key);
+
+
+    /**
+     * @brief Start an incremental TDBStore set sequence. This operation is blocking other operations.
+     *        Any get/set/remove/iterator operation will be blocked until set_finalize is called.
+     *
+     * @param[out] handle               Returned incremental set handle.
+     * @param[in]  key                  Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     * @param[in]  final_data_size      Final value data size.
+     * @param[in]  create_flags         Flag mask.
+     *
+     * @returns KVSTORE_SUCCESS                        Success.
+     *          KVSTORE_ERROR_NOT_READY                Not initialized.
+     *          KVSTORE_ERROR_READ_FAILED              Unable to read from media.
+     *          KVSTORE_ERROR_WRITE_FAILED             Unable to write to media.
+     *          KVSTORE_ERROR_INVALID_ARGUMENT         Invalid argument given in function arguments.
+     *          KVSTORE_ERROR_INVALID_SIZE             Invalid size given in function arguments.
+     *          KVSTORE_ERROR_MEDIA_FULL               Not enough room on media.
+     *          KVSTORE_ERROR_WRITE_PROTECTED          Already stored with "write once" flag.
+     */
+    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 TDBStore set sequence. This operation is blocking other operations.
+     *        Any get/set/remove operation will be blocked until set_finalize will be called.
+     *
+     * @param[in]  handle               Incremental set handle.
+     * @param[in]  value_data           Value data to add.
+     * @param[in]  data_size            Value data size.
+     *
+     * @returns KVSTORE_SUCCESS                        Success.
+     *          KVSTORE_ERROR_NOT_READY                Not initialized.
+     *          KVSTORE_ERROR_WRITE_FAILED             Unable to write to media.
+     *          KVSTORE_ERROR_INVALID_ARGUMENT         Invalid argument given in function arguments.
+     *          KVSTORE_ERROR_INVALID_SIZE             Invalid size given in function arguments.
+     */
+    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 KVSTORE_SUCCESS                        Success.
+     *          KVSTORE_ERROR_NOT_READY                Not initialized.
+     *          KVSTORE_ERROR_WRITE_FAILED             Unable to write to media.
+     *          KVSTORE_ERROR_INVALID_ARGUMENT         Invalid argument given in function arguments.
+     */
+    virtual int set_finalize(set_handle_t handle);
+
+    /**
+     * @brief Start an iteration over KVStore keys.
+     *        There are no issues with any other operations while iterator is open.
+     *
+     * @param[out] it                   Returned iterator handle.
+     * @param[in]  prefix               Key prefix (null for all keys).
+     *
+     * @returns KVSTORE_SUCCESS                        Success.
+     *          KVSTORE_ERROR_NOT_READY                Not initialized.
+     *          KVSTORE_ERROR_INVALID_ARGUMENT         Invalid argument given in function arguments.
+     */
+    virtual int iterator_open(iterator_t *it, const char *prefix = NULL);
+
+    /**
+     * @brief Get next key in iteration.
+     *        There are no issues with any other operations while iterator is open.
+     *
+     * @param[in]  it                   Iterator handle.
+     * @param[in]  key                  Buffer for returned key.
+     * @param[in]  key_size             Key buffer size.
+     *
+     * @returns KVSTORE_SUCCESS                        Success.
+     *          KVSTORE_ERROR_NOT_READY                Not initialized.
+     *          KVSTORE_ERROR_READ_FAILED              Unable to read from block device.
+     *          KVSTORE_ERROR_INVALID_ARGUMENT         Invalid argument given in function arguments.
+     *          KVSTORE_ERROR_INVALID_SIZE             Invalid size given in function arguments.
+     *          KVSTORE_ERROR_INVALID_DATA_DETECTED    Data is corrupt.
+     *          KVSTORE_ERROR_ITEM_NOT_FOUND           No more keys found.
+     */
+    virtual int iterator_next(iterator_t it, char *key, size_t key_size);
+
+    /**
+     * @brief Close iteration.
+     *
+     * @param[in]  it                   Iterator handle.
+     *
+     * @returns KVSTORE_SUCCESS                        Success.
+     *          KVSTORE_ERROR_NOT_READY                Not initialized.
+     *          KVSTORE_ERROR_INVALID_ARGUMENT         Invalid argument given in function arguments.
+     */
+    virtual int iterator_close(iterator_t it);
+
+    /**
+     * @brief Set data in reserved area, which is a special location for special data, such as ROT.
+     *        The data written to reserved area can't be overwritten.
+     *
+     * @param[in]  reserved_data        Reserved data buffer.
+     * @param[in]  reserved_data_buf_size
+     *                                  Reserved data buffer size.
+     *
+     * @returns KVSTORE_SUCCESS                        Success.
+     *          KVSTORE_ERROR_NOT_READY                Not initialized.
+     *          KVSTORE_ERROR_READ_FAILED              Unable to read from media.
+     *          KVSTORE_ERROR_WRITE_FAILED             Unable to write to media.
+     *          KVSTORE_ERROR_INVALID_ARGUMENT         Invalid argument given in function arguments.
+     *          KVSTORE_ERROR_INVALID_SIZE             Invalid size given in function arguments.
+     */
+    virtual int reserved_data_set(const void *reserved_data, size_t reserved_data_buf_size);
+
+    /**
+     * @brief Get data from reserved area, which is a special location for special data, such as ROT.
+     *
+     * @param[in]  reserved_data        Reserved data buffer.
+     * @param[in]  reserved_data_buf_size
+     *                                  Reserved data buffer size.
+     * @param[in]  actual_data_size     Return data size.
+     *
+     * @returns KVSTORE_SUCCESS                        Success.
+     *          KVSTORE_ERROR_NOT_READY                Not initialized.
+     *          KVSTORE_ERROR_READ_FAILED              Unable to read from media.
+     *          KVSTORE_ERROR_INVALID_ARGUMENT         Invalid argument given in function arguments.
+     *          KVSTORE_ERROR_INVALID_DATA_DETECTED    Data is corrupt.
+     *          KVSTORE_ERROR_ITEM_NOT_FOUND           No reserved data was written.
+     */
+    virtual int reserved_data_get(void *reserved_data, size_t reserved_data_buf_size,
+                                  size_t *actual_data_size = 0);
+
+#if !defined(DOXYGEN_ONLY)
+private:
+
+    typedef struct {
+        uint32_t address;
+        size_t   size;
+    } tdbstore_area_data_t;
+
+    static const int _num_areas = 2;
+    static const int _max_open_iterators = 16;
+
+    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;
+    uint32_t _master_record_size;
+    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;
+    size_t _work_buf_size;
+    char *_key_buf;
+    void *_inc_set_handle;
+    void *_iterator_table[_max_open_iterators];
+
+    /**
+     * @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).
+     *        This erases master record, but preserves the
+     *        reserved area data.
+     *
+     * @param[in]  area                   Area.
+     *
+     * @returns 0 for success, nonzero for failure.
+     */
+    int reset_area(uint8_t area);
+
+    /**
+     * @brief Erase an area.
+     *
+     * @param[in]  area                   Area.
+     * @param[in]  offset                 Offset in area.
+     * @param[in]  size                   Number of bytes to erase.
+     *
+     * @returns 0 for success, nonzero for failure.
+     */
+    int erase_area(uint8_t area, uint32_t offset, uint32_t size);
+
+    /**
+     * @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[out] key                    Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     * @param[out] 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 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 - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     * @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 - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     * @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 (also covers all other get APIs).
+     *
+     * @param[in]  key                  Key - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     * @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 - must not include '*' '/' '?' ':' ';' '\' '"' '|' ' ' '<' '>' '\'.
+     * @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 maximum 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 Before writing a record, check whether you are crossing an erase unit.
+     *        If you 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.
+     * @param[in]  force_check           Force checking.
+     *
+     * @returns 0 for success, nonzero for failure.
+     */
+    int check_erase_before_write(uint8_t area, uint32_t offset, uint32_t size,
+                                 bool force_check = false);
+
+    /**
+     * @brief Get data from reserved area - worker function.
+     *        This verifies that reserved data on both areas have
+     *        correct checksums. If given pointer is not NULL, also
+     *        write the reserved data to buffer. If checksums are not
+     *        valid, return error code, and don't write anything to any
+     *        pointers.
+     *
+     * @param[out] reserved_data        Reserved data buffer (NULL to return nothing).
+     * @param[in]  reserved_data_buf_size
+     *                                  Reserved data buffer size.
+     * @param[out] actual_data_size     If not NULL, return actual data size.
+     * @param[out] copy_trailer         If not NULL, copy the trailer content to given buffer.
+     *
+     * @returns 0 on success or a negative error code on failure
+     */
+    int do_reserved_data_get(void *reserved_data, size_t reserved_data_buf_size,
+                             size_t *actual_data_size = 0, void *copy_trailer = 0);
+
+    /**
+     * @brief Update all iterators after adding or deleting of keys.
+     *
+     * @param[in]  added                True if added, false if deleted.
+     * @param[in]  ram_table_ind        RAM table index.
+     *
+     * @returns none
+     */
+    void update_all_iterators(bool added, uint32_t ram_table_ind);
+
+#endif
+
+};
+/** @}*/
+
+// } // namespace mbed
+
+#endif
diff --git a/libraries/Storage/examples/QSPIFormat/QSPIFormat.ino b/libraries/Storage/examples/QSPIFormat/QSPIFormat.ino
index 26af854d4..9af2f8598 100644
--- a/libraries/Storage/examples/QSPIFormat/QSPIFormat.ino
+++ b/libraries/Storage/examples/QSPIFormat/QSPIFormat.ino
@@ -53,7 +53,8 @@ void setup() {
 
   if (true == waitResponse()) {
     MBRBlockDevice::partition(root, 1, 0x0B, 0, 5 * 1024 * 1024);
-    MBRBlockDevice::partition(root, 2, 0x0B, 5 * 1024 * 1024, 16 * 1024 * 1024);
+    MBRBlockDevice::partition(root, 2, 0x0B, 5 * 1024 * 1024, 15 * 1024 * 1024);
+    MBRBlockDevice::partition(root, 3, 0x0B, 15 * 1024 * 1024, 16 * 1024 * 1024);
 
     int err = sys_fs.reformat(&sys_bd);
     if (err) {