aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--components/service/common/psa/error.h49
-rw-r--r--components/service/secure_storage/client/psa/component.cmake19
-rw-r--r--components/service/secure_storage/client/psa/internal_trusted_storage.h88
-rw-r--r--components/service/secure_storage/client/psa/its/its_client.c215
-rw-r--r--components/service/secure_storage/client/psa/its/its_client.h31
-rw-r--r--components/service/secure_storage/client/psa/storage_common.h43
-rw-r--r--components/service/secure_storage/provider/mock_store/component.cmake14
-rw-r--r--components/service/secure_storage/provider/mock_store/mock_store_provider.c257
-rw-r--r--components/service/secure_storage/provider/mock_store/mock_store_provider.h46
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/component.cmake16
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/flash/component.cmake16
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash.c62
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash.h182
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_info.c67
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_ram.c73
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_ram.h45
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/flash_fs/component.cmake16
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs.c468
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs.h175
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_check_info.h106
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_dblock.c194
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_dblock.h88
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_mblock.c1074
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_mblock.h295
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/secure_flash_store.c302
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/secure_flash_store.h171
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/sfs_provider.c146
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/sfs_provider.h30
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/sfs_utils.c42
-rw-r--r--components/service/secure_storage/provider/secure_flash_store/sfs_utils.h99
-rw-r--r--components/service/secure_storage/test/component.cmake14
-rw-r--r--components/service/secure_storage/test/its_tests.cpp104
-rw-r--r--protocols/service/psa/packed-c/status.h12
-rw-r--r--protocols/service/secure_storage/packed-c/component.cmake16
-rw-r--r--protocols/service/secure_storage/packed-c/secure_storage_proto.h50
35 files changed, 4625 insertions, 0 deletions
diff --git a/components/service/common/psa/error.h b/components/service/common/psa/error.h
new file mode 100644
index 000000000..db8ce90c8
--- /dev/null
+++ b/components/service/common/psa/error.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PSA_ERROR_H
+#define PSA_ERROR_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Standard PSA error codes for the SPM and RoT Services
+ * As defined in PSA Firmware Framework v1.0
+ */
+
+typedef int32_t psa_status_t;
+
+#define PSA_SUCCESS ((psa_status_t)0)
+#define PSA_ERROR_PROGRAMMER_ERROR ((psa_status_t)-129)
+#define PSA_ERROR_CONNECTION_REFUSED ((psa_status_t)-130)
+#define PSA_ERROR_CONNECTION_BUSY ((psa_status_t)-131)
+#define PSA_ERROR_GENERIC_ERROR ((psa_status_t)-132)
+#define PSA_ERROR_NOT_PERMITTED ((psa_status_t)-133)
+#define PSA_ERROR_NOT_SUPPORTED ((psa_status_t)-134)
+#define PSA_ERROR_INVALID_ARGUMENT ((psa_status_t)-135)
+#define PSA_ERROR_INVALID_HANDLE ((psa_status_t)-136)
+#define PSA_ERROR_BAD_STATE ((psa_status_t)-137)
+#define PSA_ERROR_BUFFER_TOO_SMALL ((psa_status_t)-138)
+#define PSA_ERROR_ALREADY_EXISTS ((psa_status_t)-139)
+#define PSA_ERROR_DOES_NOT_EXIST ((psa_status_t)-140)
+#define PSA_ERROR_INSUFFICIENT_MEMORY ((psa_status_t)-141)
+#define PSA_ERROR_INSUFFICIENT_STORAGE ((psa_status_t)-142)
+#define PSA_ERROR_INSUFFICIENT_DATA ((psa_status_t)-143)
+#define PSA_ERROR_SERVICE_FAILURE ((psa_status_t)-144)
+#define PSA_ERROR_COMMUNICATION_FAILURE ((psa_status_t)-145)
+#define PSA_ERROR_STORAGE_FAILURE ((psa_status_t)-146)
+#define PSA_STATUS_HARDWARE_FAILURE ((psa_status_t)-147)
+#define PSA_ERROR_INVALID_SIGNATURE ((psa_status_t)-149)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PSA_ERROR_H */
diff --git a/components/service/secure_storage/client/psa/component.cmake b/components/service/secure_storage/client/psa/component.cmake
new file mode 100644
index 000000000..ae2518b0a
--- /dev/null
+++ b/components/service/secure_storage/client/psa/component.cmake
@@ -0,0 +1,19 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/its/its_client.c"
+ )
+
+
+target_include_directories(${TGT}
+ PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/.."
+ )
diff --git a/components/service/secure_storage/client/psa/internal_trusted_storage.h b/components/service/secure_storage/client/psa/internal_trusted_storage.h
new file mode 100644
index 000000000..da6905464
--- /dev/null
+++ b/components/service/secure_storage/client/psa/internal_trusted_storage.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PSA_INTERNAL_TRUSTED_STORAGE_H
+#define PSA_INTERNAL_TRUSTED_STORAGE_H
+
+#include <psa/error.h>
+#include <psa/storage_common.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The major version number of the PSA ITS API. It will be incremented on
+ * significant updates that may include breaking changes.
+ */
+#define PSA_ITS_API_VERSION_MAJOR 1
+
+/**
+ * The minor version number of the PSA ITS API. It will be incremented in
+ * small updates that are unlikely to include breaking changes.
+ */
+#define PSA_ITS_API_VERSION_MINOR 0
+
+/**
+ * @brief Create a new, or modify an existing, uid /value pair.
+ *
+ * @param[in] uid The identifier for the data
+ * @param[in] data_length The size in bytes of the data in p_data
+ * @param[in] p_data A buffer containing the data
+ * @param[in] create_flags The flags that the data will be stored with
+ *
+ * @return A status indicating the success/failure of the operation
+ */
+psa_status_t psa_its_set(psa_storage_uid_t uid,
+ size_t data_length,
+ const void *p_data,
+ psa_storage_create_flags_t create_flags);
+
+/**
+ * @brief Retrieve data associated with a provided UID.
+ *
+ * @param[in] uid The identifier for the data
+ * @param[in] data_offset The starting offset of the data requested
+ * @param[in] data_size The amount of data requested
+ * @param p_data On success, the buffer where the data will be
+ * placed
+ * @param p_data_length On success, this will contain size of the data
+ * placed in p_data
+ *
+ * @return A status indicating the success/failure of the operation
+ */
+psa_status_t psa_its_get(psa_storage_uid_t uid,
+ size_t data_offset,
+ size_t data_size,
+ void *p_data,
+ size_t *p_data_length);
+
+/**
+ * @brief Retrieve the metadata about the provided uid.
+ *
+ * @param[in] uid The identifier for the data
+ * @param p_info A pointer to the psa_storage_info_t struct that will
+ * be populated with the metadata
+ *
+ * @return A status indicating the success/failure of the operation
+ */
+psa_status_t psa_its_get_info(psa_storage_uid_t uid,
+ struct psa_storage_info_t *p_info);
+
+/**
+ * @brief Remove the provided key and its associated data from the storage
+ *
+ * @param[in] uid The identifier for the data
+ *
+ * @return A status indicating the success/failure of the operation
+ */
+psa_status_t psa_its_remove(psa_storage_uid_t uid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PSA_INTERNAL_TRUSTED_STORAGE_H */
diff --git a/components/service/secure_storage/client/psa/its/its_client.c b/components/service/secure_storage/client/psa/its/its_client.c
new file mode 100644
index 000000000..54f3efbbe
--- /dev/null
+++ b/components/service/secure_storage/client/psa/its/its_client.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "its_client.h"
+#include <psa/internal_trusted_storage.h>
+#include <protocols/service/secure_storage/packed-c/secure_storage_proto.h>
+#include <protocols/rpc/common/packed-c/status.h>
+#include <assert.h>
+#include <string.h>
+
+/* Variables */
+static struct rpc_caller *rpc_caller;
+
+psa_status_t psa_its_client_init(struct rpc_caller *caller)
+{
+ rpc_caller = caller;
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_its_set(psa_storage_uid_t uid,
+ size_t data_length,
+ const void *p_data,
+ psa_storage_create_flags_t create_flags)
+{
+ uint8_t *request;
+ uint8_t *response;
+ size_t request_length = 0;
+ size_t response_length = 0;
+ struct secure_storage_request_set *request_desc;
+ rpc_call_handle handle;
+ rpc_status_t rpc_status = TS_RPC_CALL_ACCEPTED;
+ psa_status_t psa_status = PSA_SUCCESS;
+
+ /* Validating input parameters */
+ if (p_data == NULL)
+ return PSA_ERROR_INVALID_ARGUMENT;
+
+ request_length = sizeof(*request_desc) + data_length;
+ if (request_length < data_length) {
+ /* size_t overflow */
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ handle = rpc_caller_begin(rpc_caller, &request, request_length);
+
+ if (handle) {
+ /* Populating request descriptor */
+ request_desc = (struct secure_storage_request_set *)request;
+ request_desc->uid = uid;
+ request_desc->data_length = data_length;
+ request_desc->create_flags = create_flags;
+ memcpy(&request_desc->p_data, p_data, data_length);
+
+ rpc_status = rpc_caller_invoke(rpc_caller, handle, TS_SECURE_STORAGE_OPCODE_SET,
+ (uint32_t *)&psa_status, &response,
+ &response_length);
+
+ if (rpc_status != TS_RPC_CALL_ACCEPTED) {
+ /* RPC failure */
+ psa_status = PSA_ERROR_GENERIC_ERROR;
+ }
+
+ rpc_caller_end(rpc_caller, handle);
+ }
+ else {
+ psa_status = PSA_ERROR_GENERIC_ERROR;
+ }
+
+ return psa_status;
+}
+
+psa_status_t psa_its_get(psa_storage_uid_t uid,
+ size_t data_offset,
+ size_t data_size,
+ void *p_data,
+ size_t *p_data_length)
+{
+ uint8_t *request;
+ uint8_t *response;
+ size_t response_length = 0;
+ struct secure_storage_request_get *request_desc;
+ rpc_call_handle handle;
+ rpc_status_t rpc_status = TS_RPC_CALL_ACCEPTED;
+ psa_status_t psa_status = PSA_SUCCESS;
+
+ /* Validating input parameters */
+ if (p_data == NULL)
+ return PSA_ERROR_INVALID_ARGUMENT;
+
+ handle = rpc_caller_begin(rpc_caller, &request, sizeof(*request_desc));
+
+ if (handle) {
+ /* Populating request descriptor */
+ request_desc = (struct secure_storage_request_get *)request;
+ request_desc->uid = uid;
+ request_desc->data_offset = data_offset;
+ request_desc->data_size = data_size;
+
+ rpc_status = rpc_caller_invoke(rpc_caller, handle, TS_SECURE_STORAGE_OPCODE_GET,
+ (uint32_t *)&psa_status, &response,
+ &response_length);
+
+ if (rpc_status != TS_RPC_CALL_ACCEPTED ) {
+ /* RPC failure */
+ psa_status = PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Filling output parameters */
+ if (psa_status == PSA_SUCCESS) {
+ *p_data_length = (response_length <= data_size) ? response_length : data_size;
+ memcpy(p_data, response, *p_data_length);
+ }
+
+ rpc_caller_end(rpc_caller, handle);
+ }
+ else {
+ psa_status = PSA_ERROR_GENERIC_ERROR;
+ }
+
+ return psa_status;
+}
+
+psa_status_t psa_its_get_info(psa_storage_uid_t uid,
+ struct psa_storage_info_t *p_info)
+{
+ uint8_t *request;
+ uint8_t *response;
+ size_t response_length = 0;
+ struct secure_storage_request_get_info *request_desc;
+ struct secure_storage_response_get_info *response_desc;
+ rpc_call_handle handle;
+ rpc_status_t rpc_status;
+ psa_status_t psa_status = PSA_ERROR_GENERIC_ERROR;
+
+ /* Validating input parameters */
+ if (p_info == NULL)
+ return PSA_ERROR_INVALID_ARGUMENT;
+
+ handle = rpc_caller_begin(rpc_caller, &request, sizeof(*request_desc));
+
+ if (handle) {
+ /* Populating request descriptor */
+ request_desc = (struct secure_storage_request_get_info *)request;
+ request_desc->uid = uid;
+
+ rpc_status = rpc_caller_invoke(rpc_caller, handle,
+ TS_SECURE_STORAGE_OPCODE_GET_INFO,
+ (uint32_t *)&psa_status, &response,
+ &response_length);
+
+ if (rpc_status != TS_RPC_CALL_ACCEPTED) {
+ /* RPC failure */
+ psa_status = PSA_ERROR_GENERIC_ERROR;
+ } else if (response_length && response_length != sizeof(*response_desc)) {
+ psa_status = PSA_ERROR_GENERIC_ERROR;
+ }
+
+ if (psa_status == PSA_SUCCESS) {
+ response_desc = (struct secure_storage_response_get_info *)response;
+ p_info->capacity = response_desc->capacity;
+ p_info->size = response_desc->size;
+ p_info->flags = response_desc->flags;
+ } else {
+ p_info->capacity = 0;
+ p_info->size = 0;
+ p_info->flags = PSA_STORAGE_FLAG_NONE;
+ }
+
+ rpc_caller_end(rpc_caller, handle);
+ }
+ else {
+ psa_status = PSA_ERROR_GENERIC_ERROR;
+ }
+
+ return psa_status;
+}
+
+psa_status_t psa_its_remove(psa_storage_uid_t uid)
+{
+ uint8_t *request;
+ uint8_t *response;
+ size_t response_length = 0;
+ struct secure_storage_request_remove *request_desc;
+ rpc_call_handle handle;
+ rpc_status_t rpc_status = TS_RPC_CALL_ACCEPTED;
+ psa_status_t psa_status = PSA_SUCCESS;
+
+ handle = rpc_caller_begin(rpc_caller, &request, sizeof(*request_desc));
+
+ if (handle) {
+ /* Populating request descriptor */
+ request_desc = (struct secure_storage_request_remove *)request;
+ request_desc->uid = uid;
+
+ rpc_status = rpc_caller_invoke(rpc_caller, handle, TS_SECURE_STORAGE_OPCODE_REMOVE,
+ (uint32_t *)&psa_status, &response,
+ &response_length);
+
+ if (rpc_status != TS_RPC_CALL_ACCEPTED) {
+ /* RPC failure */
+ psa_status = PSA_ERROR_GENERIC_ERROR;
+ }
+
+ rpc_caller_end(rpc_caller, handle);
+ }
+ else {
+ psa_status = PSA_ERROR_GENERIC_ERROR;
+ }
+
+ return psa_status;
+}
diff --git a/components/service/secure_storage/client/psa/its/its_client.h b/components/service/secure_storage/client/psa/its/its_client.h
new file mode 100644
index 000000000..b8b72090d
--- /dev/null
+++ b/components/service/secure_storage/client/psa/its/its_client.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PSA_ITS_CLIENT_H
+#define PSA_ITS_CLIENT_H
+
+#include <psa/error.h>
+#include <rpc_caller.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Assignes a concrete rpc caller to the ITS library and initialises
+ * the library state.
+ *
+ * @param[in] rpc_caller RPC caller instance
+ *
+ * @return A status indicating the success/failure of the operation
+ */
+psa_status_t psa_its_client_init(struct rpc_caller *caller);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PSA_ITS_CLIENT_H */
diff --git a/components/service/secure_storage/client/psa/storage_common.h b/components/service/secure_storage/client/psa/storage_common.h
new file mode 100644
index 000000000..4f6ba2a7d
--- /dev/null
+++ b/components/service/secure_storage/client/psa/storage_common.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PSA_STORAGE_COMMON_H
+#define PSA_STORAGE_COMMON_H
+
+#include <psa/error.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Types */
+typedef uint64_t psa_storage_uid_t;
+typedef uint32_t psa_storage_create_flags_t;
+
+struct psa_storage_info_t {
+ size_t capacity;
+ size_t size;
+ psa_storage_create_flags_t flags;
+};
+
+/* Storage flags */
+#define PSA_STORAGE_FLAG_NONE (0u)
+#define PSA_STORAGE_FLAG_WRITE_ONCE (1u << 0)
+#define PSA_STORAGE_FLAG_NO_CONFIDENTIALITY (1u << 1)
+#define PSA_STORAGE_FLAG_NO_REPLAY_PROTECTION (1u << 2)
+#define PSA_STORAGE_SUPPORT_SET_EXTENDED (1u << 0)
+
+/* Status codes */
+#define PSA_ERROR_INVALID_SIGNATURE ((psa_status_t)-149)
+#define PSA_ERROR_DATA_CORRUPT ((psa_status_t)-152)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PSA_STORAGE_COMMON_H */
diff --git a/components/service/secure_storage/provider/mock_store/component.cmake b/components/service/secure_storage/provider/mock_store/component.cmake
new file mode 100644
index 000000000..7e0576341
--- /dev/null
+++ b/components/service/secure_storage/provider/mock_store/component.cmake
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/mock_store_provider.c"
+ )
+
diff --git a/components/service/secure_storage/provider/mock_store/mock_store_provider.c b/components/service/secure_storage/provider/mock_store/mock_store_provider.c
new file mode 100644
index 000000000..9f1ce2edd
--- /dev/null
+++ b/components/service/secure_storage/provider/mock_store/mock_store_provider.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "mock_store_provider.h"
+#include <protocols/service/secure_storage/packed-c/secure_storage_proto.h>
+#include <protocols/rpc/common/packed-c/status.h>
+#include <protocols/service/psa/packed-c/status.h>
+#include <stdlib.h>
+#include <string.h>
+
+static struct mock_store_slot *find_slot(struct mock_store_provider *context, uint32_t id);
+static struct mock_store_slot *find_empty_slot(struct mock_store_provider *context);
+static void free_slot(struct mock_store_slot *slot);
+static rpc_status_t set_handler(void *context, struct call_req* req);
+static rpc_status_t get_handler(void *context, struct call_req* req);
+static rpc_status_t get_info_handler(void *context, struct call_req* req);
+static rpc_status_t remove_handler(void *context, struct call_req* req);
+
+/* Handler mapping table for service */
+static const struct service_handler handler_table[] = {
+ {TS_SECURE_STORAGE_OPCODE_SET, set_handler},
+ {TS_SECURE_STORAGE_OPCODE_GET, get_handler},
+ {TS_SECURE_STORAGE_OPCODE_GET_INFO, get_info_handler},
+ {TS_SECURE_STORAGE_OPCODE_REMOVE, remove_handler}
+};
+
+struct call_ep *mock_store_provider_init(struct mock_store_provider *context)
+{
+ for (int i = 0; i < MOCK_STORE_NUM_SLOTS; ++i) {
+
+ context->slots[i].len = 0;
+ context->slots[i].flags = 0;
+ context->slots[i].id = (uint32_t)(-1);
+ context->slots[i].item = NULL;
+ }
+
+ service_provider_init(&context->base_provider, context,
+ handler_table, sizeof(handler_table)/sizeof(struct service_handler));
+
+ return service_provider_get_call_ep(&context->base_provider);
+}
+
+void mock_store_provider_deinit(struct mock_store_provider *context)
+{
+ mock_store_reset(context);
+}
+
+void mock_store_reset(struct mock_store_provider *context)
+{
+ for (int i = 0; i < MOCK_STORE_NUM_SLOTS; ++i)
+ free_slot(&context->slots[i]);
+}
+
+bool mock_store_exists(const struct mock_store_provider *context, uint32_t id)
+{
+ bool exists = false;
+
+ for (int i = 0; !exists && i < MOCK_STORE_NUM_SLOTS; ++i) {
+ exists = context->slots[i].item && (context->slots[i].id == id);
+ }
+
+ return exists;
+}
+
+size_t mock_store_num_items(const struct mock_store_provider *context)
+{
+ size_t count = 0;
+
+ for (int i = 0; i < MOCK_STORE_NUM_SLOTS; ++i) {
+ if (context->slots[i].item) ++count;
+ }
+
+ return count;
+}
+
+static struct mock_store_slot *find_slot(struct mock_store_provider *context, uint32_t id)
+{
+ struct mock_store_slot *slot = NULL;
+
+ for (int i = 0; i < MOCK_STORE_NUM_SLOTS; ++i) {
+ if (context->slots[i].item && (context->slots[i].id == id)) {
+ slot = &context->slots[i];
+ break;
+ }
+ }
+
+ return slot;
+}
+
+static struct mock_store_slot *find_empty_slot(struct mock_store_provider *context)
+{
+ struct mock_store_slot *slot = NULL;
+
+ for (int i = 0; i < MOCK_STORE_NUM_SLOTS; ++i) {
+ if (!context->slots[i].item) {
+ slot = &context->slots[i];
+ break;
+ }
+ }
+
+ return slot;
+}
+
+static void free_slot(struct mock_store_slot *slot)
+{
+ if (slot->item) {
+ free(slot->item);
+ slot->len = 0;
+ slot->flags = 0;
+ slot->id = (uint32_t)(-1);
+ slot->item = NULL;
+ }
+}
+
+static rpc_status_t set_handler(void *context, struct call_req *req)
+{
+ psa_status_t psa_status = PSA_ERROR_INSUFFICIENT_MEMORY;
+ struct mock_store_provider *this_context = (struct mock_store_provider*)context;
+ struct mock_store_slot *slot;
+ struct secure_storage_request_set *request_desc;
+
+ /* Checking if the descriptor fits into the request buffer */
+ if (req->req_buf.data_len < sizeof(struct secure_storage_request_set))
+ return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+ request_desc = (struct secure_storage_request_set *)(req->req_buf.data);
+
+ /* Checking for overflow */
+ if (sizeof(struct secure_storage_request_set) + request_desc->data_length < request_desc->data_length)
+ return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+ /* Checking if descriptor and data fits into the request buffer */
+ if (req->req_buf.data_len < sizeof(struct secure_storage_request_set) + request_desc->data_length)
+ return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+ /* Replace existing or add new item */
+ slot = find_slot(this_context, request_desc->uid);
+ if (slot) free_slot(slot);
+ else slot = find_empty_slot(this_context);
+
+ if (slot) {
+ slot->id = request_desc->uid;
+ slot->flags = request_desc->create_flags;
+ slot->len = request_desc->data_length;
+ slot->item = malloc(slot->len);
+ if (slot->item) {
+ memcpy(slot->item, request_desc->p_data, slot->len);
+ psa_status = PSA_SUCCESS;
+ }
+ }
+
+ call_req_set_opstatus(req, psa_status);
+
+ return TS_RPC_CALL_ACCEPTED;
+}
+
+static rpc_status_t get_handler(void *context, struct call_req *req)
+{
+ struct mock_store_provider *this_context = (struct mock_store_provider*)context;
+ struct secure_storage_request_get *request_desc;
+ psa_status_t psa_status = PSA_ERROR_DOES_NOT_EXIST;
+ struct mock_store_slot *slot;
+
+ /* Checking if the descriptor fits into the request buffer */
+ if (req->req_buf.data_len < sizeof(struct secure_storage_request_get))
+ return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+ request_desc = (struct secure_storage_request_get *)(req->req_buf.data);
+
+ /* Check if the requested data would fit into the response buffer. */
+ if (req->resp_buf.size < request_desc->data_size)
+ return TS_RPC_ERROR_INVALID_RESP_BODY;
+
+ /* Find the item */
+ slot = find_slot(this_context, request_desc->uid);
+
+ if (slot && (slot->len <= req->resp_buf.size)) {
+ memcpy(req->resp_buf.data, slot->item, slot->len);
+ req->resp_buf.data_len = slot->len;
+ psa_status = PSA_SUCCESS;
+ }
+
+ call_req_set_opstatus(req, psa_status);
+
+ return TS_RPC_CALL_ACCEPTED;
+}
+
+static rpc_status_t get_info_handler(void *context, struct call_req *req)
+{
+ struct mock_store_provider *this_context = (struct mock_store_provider*)context;
+ struct secure_storage_request_get_info *request_desc;
+ struct secure_storage_response_get_info *response_desc;
+ psa_status_t psa_status;
+ struct mock_store_slot *slot;
+
+ /* Checking if the descriptor fits into the request buffer */
+ if (req->req_buf.data_len < sizeof(struct secure_storage_request_get_info))
+ return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+ request_desc = (struct secure_storage_request_get_info *)(req->req_buf.data);
+
+ /* Checking if the response structure would fit the response buffer */
+ if (req->resp_buf.size < sizeof(struct secure_storage_response_get_info))
+ return TS_RPC_ERROR_INVALID_RESP_BODY;
+
+ response_desc = (struct secure_storage_response_get_info *)(req->resp_buf.data);
+ req->resp_buf.data_len = sizeof(struct secure_storage_response_get_info);
+
+ /* Find itemto get info about */
+ slot = find_slot(this_context, request_desc->uid);
+
+ if (slot) {
+ response_desc->capacity = slot->len;
+ response_desc->size = slot->len;
+ response_desc->flags = slot->flags;
+ psa_status = PSA_SUCCESS;
+ }
+ else {
+ response_desc->capacity = 0;
+ response_desc->size = 0;
+ response_desc->flags = 0;
+ psa_status = PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ call_req_set_opstatus(req, psa_status);
+
+ return TS_RPC_CALL_ACCEPTED;
+}
+
+static rpc_status_t remove_handler(void *context, struct call_req *req)
+{
+ struct mock_store_provider *this_context = (struct mock_store_provider*)context;
+ struct secure_storage_request_remove *request_desc;
+ psa_status_t psa_status = PSA_ERROR_DOES_NOT_EXIST;
+ struct mock_store_slot *slot;
+
+ /* Checking if the descriptor fits into the request buffer */
+ if (req->req_buf.data_len < sizeof(struct secure_storage_request_remove))
+ return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+ request_desc = (struct secure_storage_request_remove *)(req->req_buf.data);
+
+ /* Find and remove the item */
+ slot = find_slot(this_context, request_desc->uid);
+
+ if (slot) {
+ free_slot(slot);
+ psa_status = PSA_SUCCESS;
+ }
+
+ call_req_set_opstatus(req, psa_status);
+
+ return TS_RPC_CALL_ACCEPTED;
+} \ No newline at end of file
diff --git a/components/service/secure_storage/provider/mock_store/mock_store_provider.h b/components/service/secure_storage/provider/mock_store/mock_store_provider.h
new file mode 100644
index 000000000..ecb457a35
--- /dev/null
+++ b/components/service/secure_storage/provider/mock_store/mock_store_provider.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef MOCK_STORE_PROVIDER_H
+#define MOCK_STORE_PROVIDER_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <service/common/provider/service_provider.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MOCK_STORE_NUM_SLOTS (100)
+
+struct mock_store_slot
+{
+ uint64_t id;
+ uint32_t flags;
+ size_t len;
+ uint8_t *item;
+};
+
+struct mock_store_provider
+{
+ struct service_provider base_provider;
+ struct mock_store_slot slots[MOCK_STORE_NUM_SLOTS];
+};
+
+struct call_ep *mock_store_provider_init(struct mock_store_provider *context);
+void mock_store_provider_deinit(struct mock_store_provider *context);
+
+/* Test support methods */
+void mock_store_reset(struct mock_store_provider *context);
+bool mock_store_exists(const struct mock_store_provider *context, uint32_t id);
+size_t mock_store_num_items(const struct mock_store_provider *context);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* MOCK_STORE_PROVIDER_H */
diff --git a/components/service/secure_storage/provider/secure_flash_store/component.cmake b/components/service/secure_storage/provider/secure_flash_store/component.cmake
new file mode 100644
index 000000000..2e31c20f9
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/component.cmake
@@ -0,0 +1,16 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/secure_flash_store.c"
+ "${CMAKE_CURRENT_LIST_DIR}/sfs_provider.c"
+ "${CMAKE_CURRENT_LIST_DIR}/sfs_utils.c"
+ )
+
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash/component.cmake b/components/service/secure_storage/provider/secure_flash_store/flash/component.cmake
new file mode 100644
index 000000000..a2f34e73c
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash/component.cmake
@@ -0,0 +1,16 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/sfs_flash_info.c"
+ "${CMAKE_CURRENT_LIST_DIR}/sfs_flash_ram.c"
+ "${CMAKE_CURRENT_LIST_DIR}/sfs_flash.c"
+ )
+
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash.c b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash.c
new file mode 100644
index 000000000..fce796831
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2020 Cypress Semiconductor Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "sfs_flash.h"
+
+#ifndef SFS_MAX_BLOCK_DATA_COPY
+#define SFS_MAX_BLOCK_DATA_COPY 256
+#endif
+
+extern const struct sfs_flash_info_t sfs_flash_info_internal;
+
+const struct sfs_flash_info_t *sfs_flash_get_info(void)
+{
+ return &sfs_flash_info_internal;
+}
+
+psa_status_t sfs_flash_block_to_block_move(const struct sfs_flash_info_t *info,
+ uint32_t dst_block,
+ size_t dst_offset,
+ uint32_t src_block,
+ size_t src_offset,
+ size_t size)
+{
+ psa_status_t status;
+ size_t bytes_to_move;
+ uint8_t dst_block_data_copy[SFS_MAX_BLOCK_DATA_COPY];
+
+ while (size > 0) {
+ /* Calculates the number of bytes to move */
+ bytes_to_move = SFS_UTILS_MIN(size, SFS_MAX_BLOCK_DATA_COPY);
+
+ /* Reads data from source block and store it in the in-memory copy of
+ * destination content.
+ */
+ status = info->read(info, src_block, dst_block_data_copy, src_offset,
+ bytes_to_move);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ /* Writes in flash the in-memory block content after modification */
+ status = info->write(info, dst_block, dst_block_data_copy, dst_offset,
+ bytes_to_move);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ /* Updates pointers to the source and destination flash regions */
+ dst_offset += bytes_to_move;
+ src_offset += bytes_to_move;
+
+ /* Decrement remaining size to move */
+ size -= bytes_to_move;
+ }
+
+ return PSA_SUCCESS;
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash.h b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash.h
new file mode 100644
index 000000000..18361f206
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __SFS_FLASH_H__
+#define __SFS_FLASH_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <protocols/service/psa/packed-c/status.h>
+#include "../sfs_utils.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Adjust to match the size of the flash device's physical program unit */
+#define SFS_FLASH_PROGRAM_UNIT (0x1)
+
+/* Invalid block index */
+#define SFS_BLOCK_INVALID_ID 0xFFFFFFFFU
+
+/* FIXME: Duplicated from flash info */
+#if (SFS_FLASH_PROGRAM_UNIT <= 16)
+#define SFS_FLASH_ALIGNMENT SFS_FLASH_PROGRAM_UNIT
+#else
+#define SFS_FLASH_ALIGNMENT 1
+#endif
+
+/**
+ * \brief Provides a compile-time constant for the maximum program unit required
+ * by any flash device that can be accessed through this interface.
+ */
+#define SFS_FLASH_MAX_ALIGNMENT SFS_UTILS_MAX(SFS_FLASH_ALIGNMENT, \
+ 1)
+
+/**
+ * \struct sfs_flash_info_t
+ *
+ * \brief Structure containing the required information about a flash device to
+ * be used by the SFS Flash FS.
+ */
+struct sfs_flash_info_t {
+ /**
+ * \brief Initialize the Flash Interface.
+ *
+ * \param[in] info Flash device information
+ *
+ * \return Returns PSA_SUCCESS if the function is executed correctly.
+ * Otherwise, it returns PSA_ERROR_STORAGE_FAILURE.
+ */
+ psa_status_t (*init)(const struct sfs_flash_info_t *info);
+
+ /**
+ * \brief Reads block data from the position specified by block ID and
+ * offset.
+ *
+ * \param[in] info Flash device information
+ * \param[in] block_id Block ID
+ * \param[out] buff Buffer pointer to store the data read
+ * \param[in] offset Offset position from the init of the block
+ * \param[in] size Number of bytes to read
+ *
+ * \note This function assumes all input values are valid. That is, the
+ * address range, based on block_id, offset and size, is a valid range
+ * in flash.
+ *
+ * \return Returns PSA_SUCCESS if the function is executed correctly.
+ * Otherwise, it returns PSA_ERROR_STORAGE_FAILURE.
+ */
+ psa_status_t (*read)(const struct sfs_flash_info_t *info, uint32_t block_id,
+ uint8_t *buff, size_t offset, size_t size);
+
+ /**
+ * \brief Writes block data to the position specified by block ID and
+ * offset.
+ *
+ * \param[in] info Flash device information
+ * \param[in] block_id Block ID
+ * \param[in] buff Buffer pointer to the write data
+ * \param[in] offset Offset position from the init of the block
+ * \param[in] size Number of bytes to write
+ *
+ * \note This function assumes all input values are valid. That is, the
+ * address range, based on block_id, offset and size, is a valid range
+ * in flash.
+ *
+ * \return Returns PSA_SUCCESS if the function is executed correctly.
+ * Otherwise, it returns PSA_ERROR_STORAGE_FAILURE.
+ */
+ psa_status_t (*write)(const struct sfs_flash_info_t *info,
+ uint32_t block_id, const uint8_t *buff, size_t offset,
+ size_t size);
+
+ /**
+ * \brief Flushes modifications to a block to flash. Must be called after a
+ * sequence of calls to write() (including via
+ * sfs_flash_block_to_block_move()) for one block ID, before any call
+ * to the same functions for a different block ID.
+ *
+ * \param[in] info Flash device information
+ *
+ * \note It is permitted for write() to commit block updates immediately, in
+ * which case this function is a no-op.
+ *
+ * \return Returns PSA_SUCCESS if the function is executed correctly.
+ * Otherwise, it returns PSA_ERROR_STORAGE_FAILURE.
+ */
+ psa_status_t (*flush)(const struct sfs_flash_info_t *info);
+
+ /**
+ * \brief Erases block ID data.
+ *
+ * \param[in] info Flash device information
+ * \param[in] block_id Block ID
+ *
+ * \note This function assumes the input value is valid.
+ *
+ * \return Returns PSA_SUCCESS if the function is executed correctly.
+ * Otherwise, it returns PSA_ERROR_STORAGE_FAILURE.
+ */
+ psa_status_t (*erase)(const struct sfs_flash_info_t *info,
+ uint32_t block_id);
+
+ void *flash_dev; /**< Pointer to the flash device */
+ uint32_t flash_area_addr; /**< Start address of the flash area */
+ uint16_t sector_size; /**< Size of the flash device's physical erase
+ * unit
+ */
+ uint16_t block_size; /**< Size of a logical erase unit presented by the
+ * flash interface, a multiple of sector_size.
+ */
+ uint16_t num_blocks; /**< Number of logical erase blocks */
+ uint16_t program_unit; /**< Minimum size of a program operation */
+ uint16_t max_file_size; /**< Maximum file size */
+ uint16_t max_num_files; /**< Maximum number of files */
+ uint8_t erase_val; /**< Value of a byte after erase (usually 0xFF) */
+};
+
+/**
+ * \brief Gets the flash info structure for the provided flash device.
+ *
+ * \return Pointer to the flash info struct.
+ */
+const struct sfs_flash_info_t *sfs_flash_get_info(void);
+
+/**
+ * \brief Moves data from source block ID to destination block ID.
+ *
+ * \param[in] info Flash device information
+ * \param[in] dst_block Destination block ID
+ * \param[in] dst_offset Destination offset position from the init of the
+ * destination block
+ * \param[in] src_block Source block ID
+ * \param[in] src_offset Source offset position from the init of the source
+ * block
+ * \param[in] size Number of bytes to moves
+ *
+ * \note This function assumes all input values are valid. That is, the address
+ * range, based on blockid, offset and size, is a valid range in flash.
+ * It also assumes that the destination block is already erased and ready
+ * to be written.
+ *
+ * \return Returns PSA_SUCCESS if the function is executed correctly. Otherwise,
+ * it returns PSA_ERROR_STORAGE_FAILURE.
+ */
+psa_status_t sfs_flash_block_to_block_move(const struct sfs_flash_info_t *info,
+ uint32_t dst_block,
+ size_t dst_offset,
+ uint32_t src_block,
+ size_t src_offset,
+ size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SFS_FLASH_H__ */
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_info.c b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_info.c
new file mode 100644
index 000000000..7dfe803e5
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_info.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "sfs_flash.h"
+#include "sfs_flash_ram.h"
+#include "../sfs_utils.h"
+
+#define SFS_FLASH_AREA_ADDR (0x0)
+
+/* Adjust to a size that will allow all assets to fit */
+#define SFS_FLASH_AREA_SIZE (0x4000)
+
+/* Adjust to match the size of the flash device's physical erase unit */
+#define SFS_SECTOR_SIZE (0x1000)
+
+/* Adjust so that the maximum required asset size will fit in one block */
+#define SFS_SECTORS_PER_BLOCK (0x1)
+
+/* Adjust to match the size of the flash device's physical program unit */
+#define SFS_FLASH_PROGRAM_UNIT (0x1)
+
+/* The maximum asset size to be stored in the SFS area */
+#define SFS_MAX_ASSET_SIZE (4096)
+
+/* The maximum number of assets to be stored in the SFS area */
+#define SFS_NUM_ASSETS (10)
+
+/* Calculate the block layout */
+#define FLASH_INFO_BLOCK_SIZE (SFS_SECTOR_SIZE * SFS_SECTORS_PER_BLOCK)
+#define FLASH_INFO_NUM_BLOCKS (SFS_FLASH_AREA_SIZE / FLASH_INFO_BLOCK_SIZE)
+
+/* Maximum file size */
+#define FLASH_INFO_MAX_FILE_SIZE SFS_UTILS_ALIGN(SFS_MAX_ASSET_SIZE, \
+ SFS_FLASH_ALIGNMENT)
+
+/* Maximum number of files */
+#define FLASH_INFO_MAX_NUM_FILES SFS_NUM_ASSETS
+
+/* Default value of each byte in the flash when erased */
+#define FLASH_INFO_ERASE_VAL 0xFFU
+
+/* Allocate a static buffer to emulate storage in RAM */
+static uint8_t sfs_block_data[FLASH_INFO_BLOCK_SIZE * FLASH_INFO_NUM_BLOCKS];
+#define FLASH_INFO_DEV sfs_block_data
+
+const struct sfs_flash_info_t sfs_flash_info_internal = {
+ .init = sfs_flash_ram_init,
+ .read = sfs_flash_ram_read,
+ .write = sfs_flash_ram_write,
+ .flush = sfs_flash_ram_flush,
+ .erase = sfs_flash_ram_erase,
+ .flash_dev = (void *)FLASH_INFO_DEV,
+ .flash_area_addr = SFS_FLASH_AREA_ADDR,
+ .sector_size = SFS_SECTOR_SIZE,
+ .block_size = FLASH_INFO_BLOCK_SIZE,
+ .num_blocks = FLASH_INFO_NUM_BLOCKS,
+ .program_unit = SFS_FLASH_ALIGNMENT,
+ .max_file_size = FLASH_INFO_MAX_FILE_SIZE,
+ .max_num_files = FLASH_INFO_MAX_NUM_FILES,
+ .erase_val = FLASH_INFO_ERASE_VAL,
+};
+
+/* Checks at compile time that the flash device configuration is valid */
+#include "../flash_fs/sfs_flash_fs_check_info.h"
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_ram.c b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_ram.c
new file mode 100644
index 000000000..e4af6e610
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_ram.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "sfs_flash_ram.h"
+#include <string.h>
+
+/**
+ * \brief Gets physical address of the given block ID.
+ *
+ * \param[in] info Flash device information
+ * \param[in] block_id Block ID
+ * \param[in] offset Offset position from the init of the block
+ *
+ * \returns Returns physical address for the given block ID.
+ */
+static uint32_t get_phys_address(const struct sfs_flash_info_t *info,
+ uint32_t block_id, size_t offset)
+{
+ return (block_id * info->block_size) + offset;
+}
+
+psa_status_t sfs_flash_ram_init(const struct sfs_flash_info_t *info)
+{
+ /* Nothing needs to be done in case of flash emulated in RAM */
+ (void)info;
+ return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_ram_read(const struct sfs_flash_info_t *info,
+ uint32_t block_id, uint8_t *buff, size_t offset,
+ size_t size)
+{
+ uint32_t idx = get_phys_address(info, block_id, offset);
+
+ (void)memcpy(buff, (uint8_t *)info->flash_dev + idx, size);
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_ram_write(const struct sfs_flash_info_t *info,
+ uint32_t block_id, const uint8_t *buff,
+ size_t offset, size_t size)
+{
+ uint32_t idx = get_phys_address(info, block_id, offset);
+
+ (void)memcpy((uint8_t *)info->flash_dev + idx, buff, size);
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_ram_flush(const struct sfs_flash_info_t *info)
+{
+ /* Nothing needs to be done for flash emulated in RAM, as writes are
+ * commited immediately.
+ */
+ (void)info;
+ return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_ram_erase(const struct sfs_flash_info_t *info,
+ uint32_t block_id)
+{
+ uint32_t idx = get_phys_address(info, block_id, 0);
+
+ (void)memset((uint8_t *)info->flash_dev + idx, info->erase_val,
+ info->block_size);
+
+ return PSA_SUCCESS;
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_ram.h b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_ram.h
new file mode 100644
index 000000000..eecc5e57e
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash/sfs_flash_ram.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/**
+ * \file sfs_flash_ram.h
+ *
+ * \brief Implementations of the flash interface functions for an emulated flash
+ * device using RAM. See sfs_flash.h for full documentation of functions.
+ */
+
+#include "sfs_flash.h"
+
+/**
+ * \brief Initialize the Flash Interface.
+ */
+psa_status_t sfs_flash_ram_init(const struct sfs_flash_info_t *info);
+
+/**
+ * \brief Reads block data from the position specified by block ID and offset.
+ */
+psa_status_t sfs_flash_ram_read(const struct sfs_flash_info_t *info,
+ uint32_t block_id, uint8_t *buff, size_t offset,
+ size_t size);
+
+/**
+ * \brief Writes block data to the position specified by block ID and offset.
+ */
+psa_status_t sfs_flash_ram_write(const struct sfs_flash_info_t *info,
+ uint32_t block_id, const uint8_t *buff,
+ size_t offset, size_t size);
+
+/**
+ * \brief Flushes modifications to a block to flash.
+ */
+psa_status_t sfs_flash_ram_flush(const struct sfs_flash_info_t *info);
+
+/**
+ * \brief Erases block ID data.
+ */
+psa_status_t sfs_flash_ram_erase(const struct sfs_flash_info_t *info,
+ uint32_t block_id);
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/component.cmake b/components/service/secure_storage/provider/secure_flash_store/flash_fs/component.cmake
new file mode 100644
index 000000000..a48270340
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/component.cmake
@@ -0,0 +1,16 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/sfs_flash_fs_dblock.c"
+ "${CMAKE_CURRENT_LIST_DIR}/sfs_flash_fs_mblock.c"
+ "${CMAKE_CURRENT_LIST_DIR}/sfs_flash_fs.c"
+ )
+
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs.c b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs.c
new file mode 100644
index 000000000..4747e9914
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "sfs_flash_fs.h"
+
+#include "sfs_flash_fs_dblock.h"
+#include "../sfs_utils.h"
+#include <string.h>
+
+#define SFS_FLASH_FS_INIT_FILE 0
+
+static psa_status_t sfs_flash_fs_file_write_aligned_data(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ const struct sfs_block_meta_t *block_meta,
+ const struct sfs_file_meta_t *file_meta,
+ size_t offset,
+ size_t size,
+ const uint8_t *data)
+{
+#if (SFS_FLASH_MAX_ALIGNMENT != 1)
+ /* Check that the offset is aligned with the flash program unit */
+ if (!SFS_UTILS_IS_ALIGNED(offset, fs_ctx->flash_info->program_unit)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Set the size to be aligned with the flash program unit */
+ size = SFS_UTILS_ALIGN(size, fs_ctx->flash_info->program_unit);
+#endif
+
+ /* It is not permitted to create gaps in the file */
+ if (offset > file_meta->cur_size) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Check that the new data is contained within the file's max size */
+ if (sfs_utils_check_contained_in(file_meta->max_size, offset, size)
+ != PSA_SUCCESS) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return sfs_flash_fs_dblock_write_file(fs_ctx, block_meta, file_meta, offset,
+ size, data);
+}
+
+psa_status_t sfs_flash_fs_prepare(struct sfs_flash_fs_ctx_t *fs_ctx,
+ const struct sfs_flash_info_t *flash_info)
+{
+ /* Associate the flash device info with the context */
+ fs_ctx->flash_info = flash_info;
+
+ /* Initialize metadata block with the valid/active metablock */
+ return sfs_flash_fs_mblock_init(fs_ctx);
+}
+
+psa_status_t sfs_flash_fs_wipe_all(struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+ /* Clean and initialize the metadata block */
+ return sfs_flash_fs_mblock_reset_metablock(fs_ctx);
+}
+
+psa_status_t sfs_flash_fs_file_exist(struct sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid)
+{
+ int32_t err;
+ uint32_t idx;
+
+ err = sfs_flash_fs_mblock_get_file_idx(fs_ctx, fid, &idx);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_fs_file_create(struct sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid,
+ size_t max_size,
+ size_t data_size,
+ uint32_t flags,
+ const uint8_t *data)
+{
+ struct sfs_block_meta_t block_meta;
+ uint32_t cur_phys_block;
+ int32_t err;
+ uint32_t idx;
+ struct sfs_file_meta_t file_meta;
+
+#if (SFS_FLASH_MAX_ALIGNMENT != 1)
+ /* Set the max_size to be aligned with the flash program unit */
+ max_size = SFS_UTILS_ALIGN(max_size, fs_ctx->flash_info->program_unit);
+#endif
+
+ /* Check that the file's maximum size is valid */
+ if (max_size > fs_ctx->flash_info->max_file_size) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Check if file already exists */
+ err = sfs_flash_fs_mblock_get_file_idx(fs_ctx, fid, &idx);
+ if (err == PSA_SUCCESS) {
+ /* If it exists return an error as needs to be removed first */
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Try to reserve an file based on the input parameters */
+ err = sfs_flash_fs_mblock_reserve_file(fs_ctx, fid, max_size, flags, &idx,
+ &file_meta, &block_meta);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* Check if data needs to be stored in the new file */
+ if (data_size != 0) {
+ /* Write the content into scratch data block */
+ err = sfs_flash_fs_file_write_aligned_data(fs_ctx, &block_meta,
+ &file_meta,
+ SFS_FLASH_FS_INIT_FILE,
+ data_size,
+ data);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Add current size to the file metadata */
+ file_meta.cur_size = data_size;
+
+ cur_phys_block = block_meta.phy_id;
+
+ /* Cur scratch block become the active datablock */
+ block_meta.phy_id =
+ sfs_flash_fs_mblock_cur_data_scratch_id(fs_ctx, file_meta.lblock);
+
+ /* Swap the scratch data block */
+ sfs_flash_fs_mblock_set_data_scratch(fs_ctx, cur_phys_block,
+ file_meta.lblock);
+ }
+
+ /* Update metadata block information */
+ err = sfs_flash_fs_mblock_update_scratch_block_meta(fs_ctx,
+ file_meta.lblock,
+ &block_meta);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Add file metadata in the metadata block */
+ err = sfs_flash_fs_mblock_update_scratch_file_meta(fs_ctx, idx, &file_meta);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Copy rest of the file metadata entries */
+ err = sfs_flash_fs_mblock_cp_remaining_file_meta(fs_ctx, idx);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* The file data in the logical block 0 is stored in same physical block
+ * where the metadata is stored. A change in the metadata requires a
+ * swap of physical blocks. So, the file data stored in the current
+ * metadata block needs to be copied in the scratch block, if the data
+ * of the file processed is not located in the logical block 0. When an
+ * file data is located in the logical block 0, that copy has been done
+ * while processing the file data.
+ */
+ if ((file_meta.lblock != SFS_LOGICAL_DBLOCK0) || (data_size == 0)) {
+ err = sfs_flash_fs_mblock_migrate_lb0_data_to_scratch(fs_ctx);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+ }
+
+ /* Write metadata header, swap metadata blocks and erase scratch blocks */
+ return sfs_flash_fs_mblock_meta_update_finalize(fs_ctx);
+}
+
+psa_status_t sfs_flash_fs_file_get_info(struct sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid,
+ struct sfs_file_info_t *info)
+{
+ int32_t err;
+ uint32_t idx;
+ struct sfs_file_meta_t tmp_metadata;
+
+ /* Get the meta data index */
+ err = sfs_flash_fs_mblock_get_file_idx(fs_ctx, fid, &idx);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ /* Read file metadata */
+ err = sfs_flash_fs_mblock_read_file_meta(fs_ctx, idx, &tmp_metadata);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* Check if index is still referring to same file */
+ if (memcmp(fid, tmp_metadata.id, SFS_FILE_ID_SIZE)) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ info->size_max = tmp_metadata.max_size;
+ info->size_current = tmp_metadata.cur_size;
+ info->flags = tmp_metadata.flags;
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_fs_file_write(struct sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid,
+ size_t size,
+ size_t offset,
+ const uint8_t *data)
+{
+ struct sfs_block_meta_t block_meta;
+ uint32_t cur_phys_block;
+ int32_t err;
+ uint32_t idx;
+ struct sfs_file_meta_t file_meta;
+
+ /* Get the file index */
+ err = sfs_flash_fs_mblock_get_file_idx(fs_ctx, fid, &idx);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ /* Read file metadata */
+ err = sfs_flash_fs_mblock_read_file_meta(fs_ctx, idx, &file_meta);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ /* Read block metadata */
+ err = sfs_flash_fs_mblock_read_block_metadata(fs_ctx, file_meta.lblock,
+ &block_meta);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Write the content into scratch data block */
+ err = sfs_flash_fs_file_write_aligned_data(fs_ctx, &block_meta, &file_meta,
+ offset, size, data);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Update the file's current size if required */
+ if (offset + size > file_meta.cur_size) {
+ /* Update the file metadata */
+ file_meta.cur_size = offset + size;
+ }
+
+ cur_phys_block = block_meta.phy_id;
+
+ /* Cur scratch block become the active datablock */
+ block_meta.phy_id =
+ sfs_flash_fs_mblock_cur_data_scratch_id(fs_ctx, file_meta.lblock);
+
+ /* Swap the scratch data block */
+ sfs_flash_fs_mblock_set_data_scratch(fs_ctx, cur_phys_block,
+ file_meta.lblock);
+
+ /* Update block metadata in scratch metadata block */
+ err = sfs_flash_fs_mblock_update_scratch_block_meta(fs_ctx,
+ file_meta.lblock,
+ &block_meta);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Update file metadata to reflect new attributes */
+ err = sfs_flash_fs_mblock_update_scratch_file_meta(fs_ctx, idx, &file_meta);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Copy rest of the file metadata entries */
+ err = sfs_flash_fs_mblock_cp_remaining_file_meta(fs_ctx, idx);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* The file data in the logical block 0 is stored in same physical block
+ * where the metadata is stored. A change in the metadata requires a
+ * swap of physical blocks. So, the file data stored in the current
+ * metadata block needs to be copied in the scratch block, if the data
+ * of the file processed is not located in the logical block 0. When an
+ * file data is located in the logical block 0, that copy has been done
+ * while processing the file data.
+ */
+ if (file_meta.lblock != SFS_LOGICAL_DBLOCK0) {
+ err = sfs_flash_fs_mblock_migrate_lb0_data_to_scratch(fs_ctx);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+ }
+
+ /* Update the metablock header, swap scratch and active blocks,
+ * erase scratch blocks.
+ */
+ return sfs_flash_fs_mblock_meta_update_finalize(fs_ctx);
+}
+
+psa_status_t sfs_flash_fs_file_delete(struct sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid)
+{
+ size_t del_file_data_idx;
+ uint32_t del_file_lblock;
+ uint32_t del_file_idx;
+ size_t del_file_max_size;
+ psa_status_t err;
+ size_t src_offset = fs_ctx->flash_info->block_size;
+ size_t nbr_bytes_to_move = 0;
+ uint32_t idx;
+ struct sfs_file_meta_t file_meta;
+
+ /* Get the file index */
+ err = sfs_flash_fs_mblock_get_file_idx(fs_ctx, fid, &del_file_idx);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ err = sfs_flash_fs_mblock_read_file_meta(fs_ctx, del_file_idx, &file_meta);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ if (sfs_utils_validate_fid(file_meta.id) != PSA_SUCCESS) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ /* Save logical block, data_index and max_size to be used later on */
+ del_file_lblock = file_meta.lblock;
+ del_file_data_idx = file_meta.data_idx;
+ del_file_max_size = file_meta.max_size;
+
+ /* Remove file metadata */
+ file_meta = (struct sfs_file_meta_t){0};
+
+ /* Update file metadata in to the scratch block */
+ err = sfs_flash_fs_mblock_update_scratch_file_meta(fs_ctx, del_file_idx,
+ &file_meta);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* Read all file metadata */
+ for (idx = 0; idx < fs_ctx->flash_info->max_num_files; idx++) {
+ if (idx == del_file_idx) {
+ /* Skip deleted file */
+ continue;
+ }
+
+ /* Read file meta for the given file index */
+ err = sfs_flash_fs_mblock_read_file_meta(fs_ctx, idx, &file_meta);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* Check if the file is located in the same logical block and has a
+ * valid FID.
+ */
+ if ((file_meta.lblock == del_file_lblock) &&
+ (sfs_utils_validate_fid(file_meta.id) == PSA_SUCCESS)) {
+ /* If a file is located after the data to delete, this
+ * needs to be moved.
+ */
+ if (file_meta.data_idx > del_file_data_idx) {
+ /* Check if this is the position after the deleted
+ * data. This will be the first file data to move.
+ */
+ if (src_offset > file_meta.data_idx) {
+ src_offset = file_meta.data_idx;
+ }
+
+ /* Set the new file data index location in the
+ * data block.
+ */
+ file_meta.data_idx -= del_file_max_size;
+
+ /* Increase number of bytes to move */
+ nbr_bytes_to_move += file_meta.max_size;
+ }
+ }
+ /* Update file metadata in to the scratch block */
+ err = sfs_flash_fs_mblock_update_scratch_file_meta(fs_ctx, idx,
+ &file_meta);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+ }
+
+ /* Compact data block */
+ err = sfs_flash_fs_dblock_compact_block(fs_ctx, del_file_lblock,
+ del_file_max_size,
+ src_offset, del_file_data_idx,
+ nbr_bytes_to_move);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* The file data in the logical block 0 is stored in same physical block
+ * where the metadata is stored. A change in the metadata requires a
+ * swap of physical blocks. So, the file data stored in the current
+ * metadata block needs to be copied in the scratch block, if the data
+ * of the file processed is not located in the logical block 0. When an
+ * file data is located in the logical block 0, that copy has been done
+ * while processing the file data.
+ */
+ if (del_file_lblock != SFS_LOGICAL_DBLOCK0) {
+ err = sfs_flash_fs_mblock_migrate_lb0_data_to_scratch(fs_ctx);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+ }
+
+ /* Update the metablock header, swap scratch and active blocks,
+ * erase scratch blocks.
+ */
+ return sfs_flash_fs_mblock_meta_update_finalize(fs_ctx);
+}
+
+psa_status_t sfs_flash_fs_file_read(struct sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid,
+ size_t size,
+ size_t offset,
+ uint8_t *data)
+{
+ psa_status_t err;
+ uint32_t idx;
+ struct sfs_file_meta_t tmp_metadata;
+
+ /* Get the file index */
+ err = sfs_flash_fs_mblock_get_file_idx(fs_ctx, fid, &idx);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ /* Read file metadata */
+ err = sfs_flash_fs_mblock_read_file_meta(fs_ctx, idx, &tmp_metadata);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Check if index is still referring to same file */
+ if (memcmp(fid, tmp_metadata.id, SFS_FILE_ID_SIZE)) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ /* Boundary check the incoming request */
+ err = sfs_utils_check_contained_in(tmp_metadata.cur_size, offset, size);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* Read the file from flash */
+ err = sfs_flash_fs_dblock_read_file(fs_ctx, &tmp_metadata, offset, size,
+ data);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ return PSA_SUCCESS;
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs.h b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs.h
new file mode 100644
index 000000000..704c79350
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/**
+ * \file sfs_flash_fs.h
+ *
+ * \brief The purpose of this abstraction is to have the ability to plug-in
+ * other filesystems or filesystem proxies (supplicant).
+ */
+
+#ifndef __SFS_FLASH_FS_H__
+#define __SFS_FLASH_FS_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sfs_flash_fs_mblock.h"
+#include <protocols/service/psa/packed-c/status.h>
+#include "../flash/sfs_flash.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief SFS flash filesytem context type, used to maintain state across FS
+ * operations.
+ *
+ * \details The user should allocate a variable of this type, initialised to
+ * zero, before calling sfs_flash_fs_prepare, and then pass it to each
+ * subsequent FS operation. The contents are internal to the filesytem.
+ */
+typedef struct sfs_flash_fs_ctx_t sfs_flash_fs_ctx_t;
+
+/*!
+ * \struct sfs_file_info_t
+ *
+ * \brief Structure to store the file information.
+ */
+struct sfs_file_info_t {
+ size_t size_current; /*!< The current size of the flash file data */
+ size_t size_max; /*!< The maximum size of the flash file data in
+ * bytes.
+ */
+ uint32_t flags; /*!< Flags set when the file was created */
+};
+
+/**
+ * \brief Prepares the filesystem to accept operations on the files.
+ *
+ * \param[in,out] fs_ctx Filesystem context to prepare. Must have been
+ * initialised by the caller.
+ * \param[in] flash_info Struct containing information about the flash
+ * device to associate with the context.
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_prepare(sfs_flash_fs_ctx_t *fs_ctx,
+ const struct sfs_flash_info_t *flash_info);
+
+/**
+ * \brief Wipes all files from the filesystem.
+ *
+ * \param[in,out] fs_ctx Filesystem context to wipe. Must be prepared again
+ * before further use.
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_wipe_all(sfs_flash_fs_ctx_t *fs_ctx);
+
+/**
+ * \brief Checks if a file exists in the filesystem.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] fid File ID
+ *
+ * \return Returns PSA_SUCCESS if the file exists. If file does not exist, it
+ * returns PSA_ERROR_DOES_NOT_EXIST. Otherwise, it returns error code as
+ * specified in \ref psa_status_t.
+ */
+psa_status_t sfs_flash_fs_file_exist(sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid);
+
+/**
+ * \brief Creates a file in the filesystem.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] fid File ID
+ * \param[in] max_size Size of the file to be created
+ * \param[in] data_size Size of the incoming buffer. This parameter is set
+ * to 0 when the file is empty after the creation.
+ * \param[in] flags Flags of the file
+ * \param[in] data Pointer to buffer containing the initial data.
+ * This parameter is set to NULL when the file is
+ * empty after the creation.
+ *
+ * \return Returns PSA_SUCCESS if the file has been created correctly. If the
+ * fid is in use, it returns PSA_ERROR_INVALID_ARGUMENT. Otherwise, it
+ * returns error code as specified in \ref psa_status_t.
+ */
+psa_status_t sfs_flash_fs_file_create(sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid,
+ size_t max_size,
+ size_t data_size,
+ uint32_t flags,
+ const uint8_t *data);
+
+/**
+ * \brief Gets the file information referenced by the file ID.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] fid File ID
+ * \param[out] info Pointer to the information structure to store the
+ * file information values \ref sfs_file_info_t
+ *
+ * \return Returns error code specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_file_get_info(sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid,
+ struct sfs_file_info_t *info);
+
+/**
+ * \brief Writes data to an existing file.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] fid File ID
+ * \param[in] size Size of the incoming buffer
+ * \param[in] offset Offset in the file
+ * \param[in] data Pointer to buffer containing data to be written
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_file_write(sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid,
+ size_t size,
+ size_t offset,
+ const uint8_t *data);
+
+/**
+ * \brief Reads data from an existing file.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] fid File ID
+ * \param[in] size Size to be read
+ * \param[in] offset Offset in the file
+ * \param[out] data Pointer to buffer to store the data
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_file_read(sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid,
+ size_t size,
+ size_t offset,
+ uint8_t *data);
+
+/**
+ * \brief Deletes file referenced by the file ID.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] fid File ID
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_file_delete(sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SFS_FLASH_FS_H__ */
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_check_info.h b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_check_info.h
new file mode 100644
index 000000000..2ca2f101a
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_check_info.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+/**
+ * \file sfs_flash_fs_check_info.h
+ *
+ * \brief Checks at compile time that the flash device configuration is valid.
+ * Should be included after defining the flash info parameters for a
+ * flash device.
+ */
+
+#ifndef __SFS_FLASH_FS_CHECK_INFO_H__
+#define __SFS_FLASH_FS_CHECK_INFO_H__
+
+#include "sfs_flash_fs_mblock.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SFS_BLOCK_META_HEADER_SIZE sizeof(struct sfs_metadata_block_header_t)
+#define SFS_BLOCK_METADATA_SIZE sizeof(struct sfs_block_meta_t)
+#define SFS_FILE_METADATA_SIZE sizeof(struct sfs_file_meta_t)
+
+#if ((FLASH_INFO_NUM_BLOCKS < 2) || (FLASH_INFO_NUM_BLOCKS == 3))
+ /* The minimum number of blocks is 2. In this case, metadata and data are
+ * stored in the same physical block, and the other block is required for
+ * power failure safe operation.
+ * If at least 1 data block is available, 1 data scratch block is required for
+ * power failure safe operation. So, in this case, the minimum number of
+ * blocks is 4 (2 metadata block + 2 data blocks).
+ */
+ #error "Total number of blocks should be 2 or bigger than 3"
+#endif
+
+/* The numbers in the defines are physical block indexes, starting from 0,
+ * except for SFS_NUM_DEDICATED_DBLOCKS.
+ */
+#if (FLASH_INFO_NUM_BLOCKS == 2)
+ /* Metadata and data are stored in the same physical block, and the other
+ * block is required for power failure safe operation.
+ */
+
+ /* Initial position of scratch block is the scratch metadata block */
+ #define SFS_INIT_SCRATCH_DBLOCK 1
+
+ /* Metadata and data are stored in the same block */
+ #define SFS_INIT_DBLOCK_START 0
+
+ /* There are no dedicated data blocks when only two blocks are available */
+ #define SFS_NUM_DEDICATED_DBLOCKS 0
+
+#else
+
+ /* Initial position of scratch block is immediately after metadata blocks */
+ #define SFS_INIT_SCRATCH_DBLOCK 2
+
+ /* One metadata block and two scratch blocks are reserved. One scratch block
+ * for metadata operations and the other for files data operations.
+ */
+ #define SFS_INIT_DBLOCK_START 3
+
+ /* Number of blocks dedicated just for data is the number of blocks available
+ * beyond the initial datablock start index.
+ */
+ #define SFS_NUM_DEDICATED_DBLOCKS (FLASH_INFO_NUM_BLOCKS - \
+ SFS_INIT_DBLOCK_START)
+#endif /* FLASH_INFO_NUM_BLOCKS == 2 */
+
+/* Total number of datablocks is the number of dedicated datablocks plus
+ * logical datablock 0 stored in the metadata block.
+ */
+#define SFS_NUM_ACTIVE_DBLOCKS (SFS_NUM_DEDICATED_DBLOCKS + 1)
+
+#define SFS_ALL_METADATA_SIZE \
+ (SFS_BLOCK_META_HEADER_SIZE \
+ + (SFS_NUM_ACTIVE_DBLOCKS * SFS_BLOCK_METADATA_SIZE) \
+ + (FLASH_INFO_MAX_NUM_FILES * SFS_FILE_METADATA_SIZE))
+
+/* It is not required that all files fit in SFS flash area at the same time.
+ * So, it is possible that a create action fails because flash is full.
+ * However, the larger file must have enough space in the SFS flash area to be
+ * created, at least, when the SFS flash area is empty.
+ */
+/* Checks at compile time if the largest file fits in the data area */
+SFS_UTILS_BOUND_CHECK(LARGEST_SFS_FILE_NOT_FIT_IN_DATA_BLOCK,
+ FLASH_INFO_MAX_FILE_SIZE, FLASH_INFO_BLOCK_SIZE);
+
+#if (FLASH_INFO_NUM_BLOCKS == 2)
+SFS_UTILS_BOUND_CHECK(SFS_FILE_NOT_FIT_IN_DATA_AREA, FLASH_INFO_MAX_FILE_SIZE,
+ (FLASH_INFO_BLOCK_SIZE - SFS_ALL_METADATA_SIZE));
+#endif
+
+/* Checks at compile time if the metadata fits in a flash block */
+SFS_UTILS_BOUND_CHECK(SFS_METADATA_NOT_FIT_IN_METADATA_BLOCK,
+ SFS_ALL_METADATA_SIZE, FLASH_INFO_BLOCK_SIZE);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SFS_FLASH_FS_CHECK_INFO_H__ */
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_dblock.c b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_dblock.c
new file mode 100644
index 000000000..36dc33ae7
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_dblock.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "sfs_flash_fs_dblock.h"
+
+#include "../flash/sfs_flash.h"
+
+/**
+ * \brief Converts logical data block number to physical number.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] lblock Logical block number
+ *
+ * \return Return physical block number.
+ */
+static uint32_t sfs_dblock_lo_to_phy(struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t lblock)
+{
+ struct sfs_block_meta_t block_meta;
+ int32_t err;
+
+ err = sfs_flash_fs_mblock_read_block_metadata(fs_ctx, lblock, &block_meta);
+ if (err != PSA_SUCCESS) {
+ return SFS_BLOCK_INVALID_ID;
+ }
+
+ return block_meta.phy_id;
+}
+
+psa_status_t sfs_flash_fs_dblock_compact_block(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t lblock,
+ size_t free_size,
+ size_t src_offset,
+ size_t dst_offset,
+ size_t size)
+{
+ struct sfs_block_meta_t block_meta;
+ psa_status_t err;
+ uint32_t scratch_id = 0;
+
+ /* Read current block meta */
+ err = sfs_flash_fs_mblock_read_block_metadata(fs_ctx, lblock, &block_meta);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* Release data from block meta */
+ block_meta.free_size += free_size;
+
+ /* Save scratch data block physical IDs */
+ scratch_id = sfs_flash_fs_mblock_cur_data_scratch_id(fs_ctx, lblock);
+
+ /* Check if there are bytes to be compacted */
+ if (size > 0) {
+ /* Move data from source offset in current data block to scratch block
+ * destination offset.
+ */
+ err = sfs_flash_block_to_block_move(fs_ctx->flash_info, scratch_id,
+ dst_offset, block_meta.phy_id,
+ src_offset, size);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+ }
+
+ if (dst_offset > block_meta.data_start) {
+ /* Copy data from the beginning of data block until
+ * the position where the data will be reallocated later
+ */
+ err = sfs_flash_block_to_block_move(fs_ctx->flash_info, scratch_id,
+ block_meta.data_start,
+ block_meta.phy_id,
+ block_meta.data_start,
+ (dst_offset-block_meta.data_start));
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+ }
+
+ /* Swap the scratch and current data blocks. Must swap even with nothing
+ * to compact so that deleted file is left in scratch and erased as part
+ * of finalization.
+ */
+ sfs_flash_fs_mblock_set_data_scratch(fs_ctx, block_meta.phy_id, lblock);
+
+ /* Set scratch block ID as the one which contains the new data block */
+ block_meta.phy_id = scratch_id;
+
+ /* Update block metadata in scratch metadata block */
+ err = sfs_flash_fs_mblock_update_scratch_block_meta(fs_ctx, lblock,
+ &block_meta);
+ if (err != PSA_SUCCESS) {
+ /* Swap back the data block as there was an issue in the process */
+ sfs_flash_fs_mblock_set_data_scratch(fs_ctx, scratch_id, lblock);
+ return err;
+ }
+
+ /* Commit data block modifications to flash, unless the data is in logical
+ * data block 0, in which case it will be flushed at the end of the metadata
+ * block update.
+ */
+ if (lblock != SFS_LOGICAL_DBLOCK0) {
+ err = fs_ctx->flash_info->flush(fs_ctx->flash_info);
+ }
+
+ return err;
+}
+
+psa_status_t sfs_flash_fs_dblock_read_file(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ const struct sfs_file_meta_t *file_meta,
+ size_t offset,
+ size_t size,
+ uint8_t *buf)
+{
+ uint32_t phys_block;
+ size_t pos;
+
+ phys_block = sfs_dblock_lo_to_phy(fs_ctx, file_meta->lblock);
+ if (phys_block == SFS_BLOCK_INVALID_ID) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ pos = (file_meta->data_idx + offset);
+
+ return fs_ctx->flash_info->read(fs_ctx->flash_info, phys_block, buf, pos,
+ size);
+}
+
+psa_status_t sfs_flash_fs_dblock_write_file(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ const struct sfs_block_meta_t *block_meta,
+ const struct sfs_file_meta_t *file_meta,
+ size_t offset,
+ size_t size,
+ const uint8_t *data)
+{
+ psa_status_t err;
+ uint32_t scratch_id;
+ size_t pos;
+ size_t num_bytes;
+
+ scratch_id = sfs_flash_fs_mblock_cur_data_scratch_id(fs_ctx,
+ file_meta->lblock);
+
+ /* Calculate the position of the new file data in the block */
+ pos = file_meta->data_idx + offset;
+
+ /* Move data up to the new file data position */
+ err = sfs_flash_block_to_block_move(fs_ctx->flash_info,
+ scratch_id,
+ block_meta->data_start,
+ block_meta->phy_id,
+ block_meta->data_start,
+ pos - block_meta->data_start);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* Write the new file data */
+ err = fs_ctx->flash_info->write(fs_ctx->flash_info, scratch_id, data, pos,
+ size);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* Calculate the position of the end of the file */
+ pos = file_meta->data_idx + file_meta->max_size;
+
+ /* Calculate the size of the data in the block after the end of the file */
+ num_bytes = (fs_ctx->flash_info->block_size - block_meta->free_size) - pos;
+
+ /* Move data between the end of the file and the end of the block data */
+ err = sfs_flash_block_to_block_move(fs_ctx->flash_info, scratch_id, pos,
+ block_meta->phy_id, pos, num_bytes);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* Commit data block modifications to flash, unless the data is in logical
+ * data block 0, in which case it will be flushed at the end of the metadata
+ * block update.
+ */
+ if (file_meta->lblock != SFS_LOGICAL_DBLOCK0) {
+ err = fs_ctx->flash_info->flush(fs_ctx->flash_info);
+ }
+
+ return err;
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_dblock.h b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_dblock.h
new file mode 100644
index 000000000..0fc9d50f4
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_dblock.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __SFS_FLASH_FS_DBLOCK_H__
+#define __SFS_FLASH_FS_DBLOCK_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <protocols/service/psa/packed-c/status.h>
+#include "sfs_flash_fs_mblock.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Compacts block data for the given logical block.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] lblock Logical data block to compact
+ * \param[in] free_size Available data size to compact
+ * \param[in] src_offset Offset in the current data block which points to
+ * the data position to reallocate
+ * \param[in] dst_offset Offset in the scratch block which points to the
+ * data position to store the data to be reallocated
+ * \param[in] size Number of bytes to be reallocated
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_dblock_compact_block(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t lblock,
+ size_t free_size,
+ size_t src_offset,
+ size_t dst_offset,
+ size_t size);
+
+/**
+ * \brief Reads the file content.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] file_meta File metadata
+ * \param[in] offset Offset in the file
+ * \param[in] size Size to be read
+ * \param[out] buf Buffer pointer to store the data
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_dblock_read_file(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ const struct sfs_file_meta_t *file_meta,
+ size_t offset,
+ size_t size,
+ uint8_t *buf);
+
+/**
+ * \brief Writes scratch data block content with requested data and the rest of
+ * the data from the given logical block.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] block_meta Block metadata
+ * \param[in] file_meta File metadata
+ * \param[in] offset Offset in the scratch data block where to start
+ * the copy of the incoming data
+ * \param[in] size Size of the incoming data
+ * \param[in] data Pointer to data buffer to copy in the scratch data
+ * block
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_dblock_write_file(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ const struct sfs_block_meta_t *block_meta,
+ const struct sfs_file_meta_t *file_meta,
+ size_t offset,
+ size_t size,
+ const uint8_t *data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SFS_FLASH_FS_DBLOCK_H__ */
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_mblock.c b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_mblock.c
new file mode 100644
index 000000000..cb435cf38
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_mblock.c
@@ -0,0 +1,1074 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "sfs_flash_fs_mblock.h"
+#include <string.h>
+
+/* Physical ID of the two metadata blocks */
+/* NOTE: the earmarked area may not always start at block number 0.
+ * However, the flash interface can always add the required offset.
+ */
+#define SFS_METADATA_BLOCK0 0
+#define SFS_METADATA_BLOCK1 1
+
+/*!
+ * \def SFS_OTHER_META_BLOCK
+ *
+ * \brief Macro to get the the swap metadata block.
+ */
+#define SFS_OTHER_META_BLOCK(metablock) \
+(((metablock) == SFS_METADATA_BLOCK0) ? \
+(SFS_METADATA_BLOCK1) : (SFS_METADATA_BLOCK0))
+
+#define SFS_BLOCK_META_HEADER_SIZE sizeof(struct sfs_metadata_block_header_t)
+#define SFS_BLOCK_METADATA_SIZE sizeof(struct sfs_block_meta_t)
+#define SFS_FILE_METADATA_SIZE sizeof(struct sfs_file_meta_t)
+
+/* FIXME: Precompute these for each context */
+/**
+ * \brief Gets the physical block ID of the initial position of the scratch
+ * data block, for the current context.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ *
+ * \return The physical block ID
+ */
+__attribute__((always_inline))
+static inline uint32_t sfs_init_scratch_dblock(
+ struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+ /* When there are two blocks, the initial position of the scratch data block
+ * is the scratch metadata block. Otherwise, the initial position of scratch
+ * data block is immediately after the metadata blocks.
+ */
+ return fs_ctx->flash_info->num_blocks == 2 ? 1 : 2;
+}
+
+/**
+ * \brief Gets the physical block ID of the start position of the data blocks,
+ * for the current context.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ *
+ * \return The physical block ID
+ */
+__attribute__((always_inline))
+static inline uint32_t sfs_init_dblock_start(struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+ /* Metadata and data are always stored in the same block with two blocks.
+ * Otherwise, one metadata block and two scratch blocks are reserved. One
+ * scratch block for metadata operations and the other for data operations.
+ */
+ return fs_ctx->flash_info->num_blocks == 2 ? 0 : 3;
+}
+
+/**
+ * \brief Gets the number of blocks that are dedicated wholely for data, for the
+ * current context.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ *
+ * \return The number of dedicated datablocks
+ */
+static uint32_t sfs_num_dedicated_dblocks(struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+ /* There are no dedicated data blocks when only two blocks are available.
+ * Otherwise, the number of blocks dedicated just for data is the number of
+ * blocks available beyond the initial datablock start index.
+ */
+ return fs_ctx->flash_info->num_blocks == 2 ? 0 :
+ fs_ctx->flash_info->num_blocks - sfs_init_dblock_start(fs_ctx);
+}
+
+/**
+ * \brief Gets the number of active data blocks, for the current context.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ *
+ * \return The number of active datablocks
+ */
+__attribute__((always_inline))
+static inline uint32_t sfs_num_active_dblocks(struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+ /* Total number of data blocks is the number of dedicated data blocks plus
+ * logical data block 0 stored in the metadata block.
+ */
+ return sfs_num_dedicated_dblocks(fs_ctx) + 1;
+}
+
+/**
+ * \brief Gets offset of a logical block's metadata in metadata block.
+ *
+ * \param[in] lblock Logical block number
+ *
+ * \return Return offset value in metadata block
+ */
+static size_t sfs_mblock_block_meta_offset(uint32_t lblock)
+{
+ return SFS_BLOCK_META_HEADER_SIZE + (lblock * SFS_BLOCK_METADATA_SIZE);
+}
+
+/**
+ * \brief Gets offset of an file metadata in metadata block.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] idx File metadata entry index
+ *
+ * \return Return offset value in metadata block
+ */
+static size_t sfs_mblock_file_meta_offset(struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t idx)
+{
+ return SFS_BLOCK_META_HEADER_SIZE
+ + (sfs_num_active_dblocks(fs_ctx) * SFS_BLOCK_METADATA_SIZE)
+ + (idx * SFS_FILE_METADATA_SIZE);
+}
+
+/**
+ * \brief Swaps metablocks. Scratch becomes active and active becomes scratch.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ */
+static void sfs_mblock_swap_metablocks(struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+ uint32_t tmp_block;
+
+ tmp_block = fs_ctx->scratch_metablock;
+ fs_ctx->scratch_metablock = fs_ctx->active_metablock;
+ fs_ctx->active_metablock = tmp_block;
+}
+
+/**
+ * \brief Finds the potential most recent valid metablock.
+ *
+ * \param[in] h_meta0 Header metadata of meta block 0
+ * \param[in] h_meta1 Header metadata of meta block 1
+ *
+ * \return most recent metablock
+ */
+static uint8_t sfs_mblock_latest_meta_block(
+ const struct sfs_metadata_block_header_t *h_meta0,
+ const struct sfs_metadata_block_header_t *h_meta1)
+{
+ uint8_t cur_meta;
+ uint8_t meta0_swap_count = h_meta0->active_swap_count;
+ uint8_t meta1_swap_count = h_meta1->active_swap_count;
+
+ /* Logic: if the swap count is 0, then it has rolled over. The metadata
+ * block with a swap count of 0 is the latest one, unless the other block
+ * has a swap count of 1, in which case the roll over occurred in the
+ * previous update. In all other cases, the block with the highest swap
+ * count is the latest one.
+ */
+ if ((meta1_swap_count == 0) && (meta0_swap_count != 1)) {
+ /* Metadata block 1 swap count has rolled over and metadata block 0
+ * swap count has not, so block 1 is the latest.
+ */
+ cur_meta = SFS_METADATA_BLOCK1;
+
+ } else if ((meta0_swap_count == 0) && (meta1_swap_count != 1)) {
+ /* Metadata block 0 swap count has rolled over and metadata block 1
+ * swap count has not, so block 0 is the latest.
+ */
+ cur_meta = SFS_METADATA_BLOCK0;
+
+ } else if (meta1_swap_count > meta0_swap_count) {
+ /* Neither swap count has just rolled over and metadata block 1 has a
+ * higher swap count, so block 1 is the latest.
+ */
+ cur_meta = SFS_METADATA_BLOCK1;
+
+ } else {
+ /* Neither swap count has just rolled over and metadata block 0 has a
+ * higher or equal swap count, so block 0 is the latest.
+ */
+ cur_meta = SFS_METADATA_BLOCK0;
+ }
+
+ return cur_meta;
+}
+
+#ifdef SFS_VALIDATE_METADATA_FROM_FLASH
+/**
+ * \brief Validates file metadata in order to guarantee that a corruption or
+ * malicious change in stored metadata doesn't result in an invalid
+ * access.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] file_meta Pointer to file meta structure
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+__attribute__((always_inline))
+static inline psa_status_t sfs_mblock_validate_file_meta(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ const struct sfs_file_meta_t *file_meta)
+{
+ int32_t err;
+
+ /* Logical block ID can not be bigger or equal than number of
+ * active blocks.
+ */
+ if (file_meta->lblock >= sfs_num_active_dblocks(fs_ctx)) {
+ return PSA_ERROR_DATA_CORRUPT;
+ }
+
+ /* meta->id can be 0 if the file is not in use. If it is in
+ * use, check the metadata.
+ */
+ if (sfs_utils_validate_fid(file_meta->id) == PSA_SUCCESS) {
+ /* validate files values if file is in use */
+ if (file_meta->max_size > fs_ctx->flash_info->max_file_size) {
+ return PSA_ERROR_DATA_CORRUPT;
+ }
+
+ /* The current file data size must be smaller or equal than
+ * file data max size.
+ */
+ if (file_meta->cur_size > file_meta->max_size) {
+ return PSA_ERROR_DATA_CORRUPT;
+ }
+
+ if (file_meta->lblock == SFS_LOGICAL_DBLOCK0) {
+ /* In block 0, data index must be located after the metadata */
+ if (file_meta->data_idx < sfs_mblock_file_meta_offset(fs_ctx,
+ fs_ctx->flash_info->max_num_files)) {
+ return PSA_ERROR_DATA_CORRUPT;
+ }
+ }
+
+ /* Boundary check the incoming request */
+ err = sfs_utils_check_contained_in(fs_ctx->flash_info->block_size,
+ file_meta->data_idx,
+ file_meta->max_size);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_DATA_CORRUPT;
+ }
+ }
+
+ return PSA_SUCCESS;
+}
+
+/**
+ * \brief Validates block metadata in order to guarantee that a corruption or
+ * malicious change in stored metadata doesn't result in an invalid
+ * access.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] block_meta Pointer to block meta structure
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+__attribute__((always_inline))
+static inline psa_status_t sfs_mblock_validate_block_meta(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ const struct sfs_block_meta_t *block_meta)
+{
+ psa_status_t err;
+ /* Data block's data start at position 0 */
+ size_t valid_data_start_value = 0;
+
+ if (block_meta->phy_id >= fs_ctx->flash_info->num_blocks) {
+ return PSA_ERROR_DATA_CORRUPT;
+ }
+
+ /* Boundary check: block data start + free size can not be bigger
+ * than max block size.
+ */
+ err = sfs_utils_check_contained_in(fs_ctx->flash_info->block_size,
+ block_meta->data_start,
+ block_meta->free_size);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_DATA_CORRUPT;
+ }
+
+ if (block_meta->phy_id == SFS_METADATA_BLOCK0 ||
+ block_meta->phy_id == SFS_METADATA_BLOCK1) {
+
+ /* For metadata + data block, data index must start after the
+ * metadata area.
+ */
+ valid_data_start_value = sfs_mblock_file_meta_offset(fs_ctx,
+ fs_ctx->flash_info->max_num_files);
+ }
+
+ if (block_meta->data_start != valid_data_start_value) {
+ return PSA_ERROR_DATA_CORRUPT;
+ }
+
+ return PSA_SUCCESS;
+}
+#endif /* SFS_VALIDATE_METADATA_FROM_FLASH */
+
+/**
+ * \brief Gets a free file metadata table entry.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ *
+ * \return Return index of a free file meta entry
+ */
+static uint32_t sfs_get_free_file_index(struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+ int32_t err;
+ uint32_t i;
+ struct sfs_file_meta_t tmp_metadata;
+
+ for (i = 0; i < fs_ctx->flash_info->max_num_files; i++) {
+ err = sfs_flash_fs_mblock_read_file_meta(fs_ctx, i, &tmp_metadata);
+ if (err != PSA_SUCCESS) {
+ return SFS_METADATA_INVALID_INDEX;
+ }
+
+ /* Check if this entry is free by checking if ID values is an
+ * invalid ID.
+ */
+ if (sfs_utils_validate_fid(tmp_metadata.id) != PSA_SUCCESS) {
+ /* Found */
+ return i;
+ }
+ }
+
+ return SFS_METADATA_INVALID_INDEX;
+}
+
+/**
+ * \brief Erases data and meta scratch blocks.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_mblock_erase_scratch_blocks(
+ struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+ psa_status_t err;
+ uint32_t scratch_datablock;
+
+ /* For the atomicity of the data update process
+ * and power-failure-safe operation, it is necessary that
+ * metadata scratch block is erased before data block.
+ */
+ err = fs_ctx->flash_info->erase(fs_ctx->flash_info,
+ fs_ctx->scratch_metablock);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* If the number of blocks is bigger than 2, the code needs to erase the
+ * scratch block used to process any change in the data block which contains
+ * only data. Otherwise, if the number of blocks is equal to 2, it means
+ * that all data is stored in the metadata block.
+ */
+ if (fs_ctx->flash_info->num_blocks > 2) {
+ scratch_datablock =
+ sfs_flash_fs_mblock_cur_data_scratch_id(fs_ctx,
+ (SFS_LOGICAL_DBLOCK0 + 1));
+ err = fs_ctx->flash_info->erase(fs_ctx->flash_info, scratch_datablock);
+ }
+
+ return err;
+}
+
+/**
+ * \brief Updates scratch block meta.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] lblock Logical block number
+ * \param[in] block_meta Pointer to the block metadata data to write in the
+ * scratch block
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_mblock_update_scratch_block_meta(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t lblock,
+ const struct sfs_block_meta_t *block_meta)
+{
+ size_t pos;
+
+ /* Calculate the position */
+ pos = sfs_mblock_block_meta_offset(lblock);
+ return fs_ctx->flash_info->write(fs_ctx->flash_info,
+ fs_ctx->scratch_metablock,
+ (const uint8_t *)block_meta, pos,
+ SFS_BLOCK_METADATA_SIZE);
+}
+
+/**
+ * \brief Copies rest of the block metadata.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] lblock Logical block number to skip
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_mblock_copy_remaining_block_meta(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t lblock)
+{
+ struct sfs_block_meta_t block_meta;
+ psa_status_t err;
+ uint32_t meta_block;
+ size_t pos;
+ uint32_t scratch_block;
+ size_t size;
+
+ scratch_block = fs_ctx->scratch_metablock;
+ meta_block = fs_ctx->active_metablock;
+
+ if (lblock != SFS_LOGICAL_DBLOCK0) {
+ /* The file data in the logical block 0 is stored in same physical
+ * block where the metadata is stored. A change in the metadata requires
+ * a swap of physical blocks. So, the physical block ID of logical block
+ * 0 needs to be updated to reflect this change, if the file processed
+ * is not located in logical block 0. If it is located in block 0,
+ * the physical block ID has been updated while processing the file
+ * data.
+ */
+ err = sfs_flash_fs_mblock_read_block_metadata(fs_ctx,
+ SFS_LOGICAL_DBLOCK0,
+ &block_meta);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Update physical ID for logical block 0 to match with the
+ * metadata block physical ID.
+ */
+ block_meta.phy_id = scratch_block;
+ err = sfs_mblock_update_scratch_block_meta(fs_ctx, SFS_LOGICAL_DBLOCK0,
+ &block_meta);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Copy the rest of metadata blocks between logical block 0 and
+ * the logical block provided in the function.
+ */
+ if (lblock > 1) {
+ pos = sfs_mblock_block_meta_offset(SFS_LOGICAL_DBLOCK0 + 1);
+
+ size = sfs_mblock_block_meta_offset(lblock) - pos;
+
+ /* Copy rest of the block data from previous block */
+ /* Data before updated content */
+ err = sfs_flash_block_to_block_move(fs_ctx->flash_info,
+ scratch_block, pos, meta_block,
+ pos, size);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+ }
+ }
+
+ /* Move meta blocks data after updated content */
+ pos = sfs_mblock_block_meta_offset(lblock+1);
+
+ size = sfs_mblock_file_meta_offset(fs_ctx, 0) - pos;
+
+ return sfs_flash_block_to_block_move(fs_ctx->flash_info, scratch_block, pos,
+ meta_block, pos, size);
+}
+
+/**
+ * \brief Checks the validity of the metadata block's swap count.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] swap_count Swap count to validate
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+__attribute__((always_inline))
+static inline psa_status_t sfs_mblock_validate_swap_count(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint8_t swap_count)
+{
+ /* When a flash block is erased, the default value
+ * is usually 0xFF (i.e. all 1s). Since the swap count
+ * is updated last (when encryption is disabled), it is
+ * possible that due to a power failure, the swap count
+ * value in metadata header is 0xFFFF..., which mean
+ * it will appear to be most recent block. Which isn't
+ * a problem in itself, as the rest of the metadata is fully
+ * valid (as it would have been written before swap count).
+ * However, this also means that previous update process
+ * wasn't complete. So, if the value is 0xFF..., revert
+ * back to previous metablock instead.
+ */
+ return (swap_count == fs_ctx->flash_info->erase_val)
+ ? PSA_ERROR_GENERIC_ERROR
+ : PSA_SUCCESS;
+}
+
+/**
+ * \brief Checks the validity of FS version.
+ *
+ * \param[in] fs_version File system version.
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+__attribute__((always_inline))
+static inline psa_status_t sfs_mblock_validate_fs_version(uint8_t fs_version)
+{
+ /* Looks for exact version number.
+ * FIXME: backward compatibility could be considered in future revisions.
+ */
+ return (fs_version != SFS_SUPPORTED_VERSION) ? PSA_ERROR_GENERIC_ERROR
+ : PSA_SUCCESS;
+}
+
+/**
+ * \brief Validates header metadata in order to guarantee that a corruption or
+ * malicious change in stored metadata doesn't result in an invalid
+ * access and the header version is correct.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] h_meta Pointer to metadata block header
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_mblock_validate_header_meta(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ const struct sfs_metadata_block_header_t *h_meta)
+{
+ psa_status_t err;
+
+ err = sfs_mblock_validate_fs_version(h_meta->fs_version);
+ if (err == PSA_SUCCESS) {
+ err = sfs_mblock_validate_swap_count(fs_ctx, h_meta->active_swap_count);
+ }
+
+ return err;
+}
+
+/**
+ * \brief Writes the scratch metadata's header.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_mblock_write_scratch_meta_header(
+ struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+ psa_status_t err;
+
+ /* Increment the swap count */
+ fs_ctx->meta_block_header.active_swap_count += 1;
+
+ err = sfs_mblock_validate_swap_count(fs_ctx,
+ fs_ctx->meta_block_header.active_swap_count);
+ if (err != PSA_SUCCESS) {
+ /* Reset the swap count to 0 */
+ fs_ctx->meta_block_header.active_swap_count = 0;
+ }
+
+ /* Write the metadata block header */
+ return fs_ctx->flash_info->write(fs_ctx->flash_info,
+ fs_ctx->scratch_metablock,
+ (uint8_t *)(&fs_ctx->meta_block_header),
+ 0, SFS_BLOCK_META_HEADER_SIZE);
+}
+
+/**
+ * \brief Reads the active metadata block header into sfs_system_ctx.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_mblock_read_meta_header(
+ struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+ psa_status_t err;
+
+ err = fs_ctx->flash_info->read(fs_ctx->flash_info, fs_ctx->active_metablock,
+ (uint8_t *)&fs_ctx->meta_block_header, 0,
+ SFS_BLOCK_META_HEADER_SIZE);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ return sfs_mblock_validate_header_meta(fs_ctx, &fs_ctx->meta_block_header);
+}
+
+/**
+ * \brief Reserves space for an file.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] fid File ID
+ * \param[in] size Size of the file for which space is reserve
+ * \param[in] flags Flags set when the file is created
+ * \param[out] file_meta File metadata entry
+ * \param[out] block_meta Block metadata entry
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_mblock_reserve_file(struct sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid, size_t size,
+ uint32_t flags,
+ struct sfs_file_meta_t *file_meta,
+ struct sfs_block_meta_t *block_meta)
+{
+ psa_status_t err;
+ uint32_t i;
+
+ for (i = 0; i < sfs_num_active_dblocks(fs_ctx); i++) {
+ err = sfs_flash_fs_mblock_read_block_metadata(fs_ctx, i, block_meta);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ if (block_meta->free_size >= size) {
+ /* Set file metadata */
+ file_meta->lblock = i;
+ file_meta->data_idx = fs_ctx->flash_info->block_size
+ - block_meta->free_size;
+ file_meta->max_size = size;
+ memcpy(file_meta->id, fid, SFS_FILE_ID_SIZE);
+ file_meta->cur_size = 0;
+ file_meta->flags = flags;
+
+ /* Update block metadata */
+ block_meta->free_size -= size;
+ return PSA_SUCCESS;
+ }
+ }
+
+ /* No block has large enough space to fit the requested file */
+ return PSA_ERROR_INSUFFICIENT_STORAGE;
+}
+
+/**
+ * \brief Validates and find the valid-active metablock
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ *
+ * \return Returns value as specified in \ref psa_status_t
+ */
+static psa_status_t sfs_init_get_active_metablock(
+ struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+ uint32_t cur_meta_block = SFS_BLOCK_INVALID_ID;
+ psa_status_t err;
+ struct sfs_metadata_block_header_t h_meta0;
+ struct sfs_metadata_block_header_t h_meta1;
+ uint8_t num_valid_meta_blocks = 0;
+
+ /* First two blocks are reserved for metadata */
+
+ /* Read the header of both the metdata blocks. If the read succeeds, then
+ * attempt to validate the metadata header, otherwise assume that the block
+ * update was incomplete
+ */
+ err = fs_ctx->flash_info->read(fs_ctx->flash_info, SFS_METADATA_BLOCK0,
+ (uint8_t *)&h_meta0, 0,
+ SFS_BLOCK_META_HEADER_SIZE);
+ if (err == PSA_SUCCESS) {
+ if (sfs_mblock_validate_header_meta(fs_ctx, &h_meta0) == PSA_SUCCESS) {
+ num_valid_meta_blocks++;
+ cur_meta_block = SFS_METADATA_BLOCK0;
+ }
+ }
+
+ err = fs_ctx->flash_info->read(fs_ctx->flash_info, SFS_METADATA_BLOCK1,
+ (uint8_t *)&h_meta1, 0,
+ SFS_BLOCK_META_HEADER_SIZE);
+ if (err == PSA_SUCCESS) {
+ if (sfs_mblock_validate_header_meta(fs_ctx, &h_meta1) == PSA_SUCCESS) {
+ num_valid_meta_blocks++;
+ cur_meta_block = SFS_METADATA_BLOCK1;
+ }
+ }
+
+ /* If there are more than 1 potential metablocks, the previous
+ * update operation was interrupted by power failure. In which case,
+ * need to find out which one is potentially latest metablock.
+ */
+ if (num_valid_meta_blocks > 1) {
+ cur_meta_block = sfs_mblock_latest_meta_block(&h_meta0, &h_meta1);
+ } else if (num_valid_meta_blocks == 0) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ fs_ctx->active_metablock = cur_meta_block;
+ fs_ctx->scratch_metablock = SFS_OTHER_META_BLOCK(cur_meta_block);
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_fs_mblock_cp_remaining_file_meta(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t idx)
+{
+ psa_status_t err;
+ size_t end;
+ uint32_t meta_block;
+ size_t pos;
+ uint32_t scratch_block;
+
+ scratch_block = fs_ctx->scratch_metablock;
+ meta_block = fs_ctx->active_metablock;
+ /* Calculate the position */
+ pos = sfs_mblock_file_meta_offset(fs_ctx, 0);
+ /* Copy rest of the block data from previous block */
+ /* Data before updated content */
+ err = sfs_flash_block_to_block_move(fs_ctx->flash_info, scratch_block, pos,
+ meta_block, pos,
+ (idx * SFS_FILE_METADATA_SIZE));
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* Data after updated content */
+ pos = sfs_mblock_file_meta_offset(fs_ctx, idx + 1);
+
+ /* Get end of file meta position which is the position after the last
+ * byte of file meta.
+ */
+ end = sfs_mblock_file_meta_offset(fs_ctx,
+ fs_ctx->flash_info->max_num_files);
+ if (end > pos) {
+ err = sfs_flash_block_to_block_move(fs_ctx->flash_info, scratch_block,
+ pos, meta_block, pos, (end - pos));
+ }
+
+ return err;
+}
+
+uint32_t sfs_flash_fs_mblock_cur_data_scratch_id(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t lblock)
+{
+ if (lblock == SFS_LOGICAL_DBLOCK0) {
+ /* Scratch logical data block 0 physical IDs */
+ return fs_ctx->scratch_metablock;
+ }
+
+ return fs_ctx->meta_block_header.scratch_dblock;
+}
+
+psa_status_t sfs_flash_fs_mblock_get_file_idx(struct sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid,
+ uint32_t *idx)
+{
+ psa_status_t err;
+ uint32_t i;
+ struct sfs_file_meta_t tmp_metadata;
+
+ for (i = 0; i < fs_ctx->flash_info->max_num_files; i++) {
+ err = sfs_flash_fs_mblock_read_file_meta(fs_ctx, i, &tmp_metadata);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* ID with value 0x00 means end of file meta section */
+ if (!memcmp(tmp_metadata.id, fid, SFS_FILE_ID_SIZE)) {
+ /* Found */
+ *idx = i;
+ return PSA_SUCCESS;
+ }
+ }
+
+ return PSA_ERROR_DOES_NOT_EXIST;
+}
+
+psa_status_t sfs_flash_fs_mblock_init(struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+ psa_status_t err;
+
+ /* Initialize Flash Interface */
+ err = fs_ctx->flash_info->init(fs_ctx->flash_info);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ err = sfs_init_get_active_metablock(fs_ctx);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ err = sfs_mblock_read_meta_header(fs_ctx);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Erase the other scratch metadata block */
+ return sfs_mblock_erase_scratch_blocks(fs_ctx);
+}
+
+psa_status_t sfs_flash_fs_mblock_meta_update_finalize(
+ struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+ psa_status_t err;
+
+ /* Write the metadata block header to flash */
+ err = sfs_mblock_write_scratch_meta_header(fs_ctx);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* Commit metadata block modifications to flash */
+ err = fs_ctx->flash_info->flush(fs_ctx->flash_info);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* Update the running context */
+ sfs_mblock_swap_metablocks(fs_ctx);
+
+ /* Erase meta block and current scratch block */
+ return sfs_mblock_erase_scratch_blocks(fs_ctx);
+}
+
+psa_status_t sfs_flash_fs_mblock_migrate_lb0_data_to_scratch(
+ struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+ struct sfs_block_meta_t block_meta;
+ size_t data_size;
+ psa_status_t err;
+
+ err = sfs_flash_fs_mblock_read_block_metadata(fs_ctx, SFS_LOGICAL_DBLOCK0,
+ &block_meta);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* Calculate data size stored in the B0 block */
+ data_size = (fs_ctx->flash_info->block_size - block_meta.data_start)
+ - block_meta.free_size;
+
+ return sfs_flash_block_to_block_move(fs_ctx->flash_info,
+ fs_ctx->scratch_metablock,
+ block_meta.data_start,
+ fs_ctx->active_metablock,
+ block_meta.data_start,
+ data_size);
+}
+
+psa_status_t sfs_flash_fs_mblock_read_file_meta(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t idx,
+ struct sfs_file_meta_t *file_meta)
+{
+ psa_status_t err;
+ size_t offset;
+
+ offset = sfs_mblock_file_meta_offset(fs_ctx, idx);
+ err = fs_ctx->flash_info->read(fs_ctx->flash_info, fs_ctx->active_metablock,
+ (uint8_t *)file_meta, offset,
+ SFS_FILE_METADATA_SIZE);
+
+#ifdef SFS_VALIDATE_METADATA_FROM_FLASH
+ if (err == PSA_SUCCESS) {
+ err = sfs_mblock_validate_file_meta(fs_ctx, file_meta);
+ }
+#endif
+
+ return err;
+}
+
+psa_status_t sfs_flash_fs_mblock_read_block_metadata(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t lblock,
+ struct sfs_block_meta_t *block_meta)
+{
+ psa_status_t err;
+ size_t pos;
+
+ pos = sfs_mblock_block_meta_offset(lblock);
+ err = fs_ctx->flash_info->read(fs_ctx->flash_info, fs_ctx->active_metablock,
+ (uint8_t *)block_meta, pos,
+ SFS_BLOCK_METADATA_SIZE);
+
+#ifdef SFS_VALIDATE_METADATA_FROM_FLASH
+ if (err == PSA_SUCCESS) {
+ err = sfs_mblock_validate_block_meta(fs_ctx, block_meta);
+ }
+#endif
+
+ return err;
+}
+
+psa_status_t sfs_flash_fs_mblock_reserve_file(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid,
+ size_t size,
+ uint32_t flags,
+ uint32_t *idx,
+ struct sfs_file_meta_t *file_meta,
+ struct sfs_block_meta_t *block_meta)
+{
+ psa_status_t err;
+
+ err = sfs_mblock_reserve_file(fs_ctx, fid, size, flags, file_meta,
+ block_meta);
+
+ *idx = sfs_get_free_file_index(fs_ctx);
+ if ((err != PSA_SUCCESS) ||
+ (*idx == SFS_METADATA_INVALID_INDEX)) {
+ return PSA_ERROR_INSUFFICIENT_STORAGE;
+ }
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t sfs_flash_fs_mblock_reset_metablock(
+ struct sfs_flash_fs_ctx_t *fs_ctx)
+{
+ struct sfs_block_meta_t block_meta;
+ psa_status_t err;
+ uint32_t i;
+ uint32_t metablock_to_erase_first = SFS_METADATA_BLOCK0;
+ struct sfs_file_meta_t file_metadata;
+
+ /* Erase both metadata blocks. If at least one metadata block is valid,
+ * ensure that the active metadata block is erased last to prevent rollback
+ * in the case of a power failure between the two erases.
+ */
+ if (sfs_init_get_active_metablock(fs_ctx) == PSA_SUCCESS) {
+ metablock_to_erase_first = fs_ctx->scratch_metablock;
+ }
+
+ err = fs_ctx->flash_info->erase(fs_ctx->flash_info,
+ metablock_to_erase_first);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ err = fs_ctx->flash_info->erase(fs_ctx->flash_info,
+ SFS_OTHER_META_BLOCK(metablock_to_erase_first));
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ fs_ctx->meta_block_header.active_swap_count = 0;
+ fs_ctx->meta_block_header.scratch_dblock = sfs_init_scratch_dblock(fs_ctx);
+ fs_ctx->meta_block_header.fs_version = SFS_SUPPORTED_VERSION;
+ fs_ctx->scratch_metablock = SFS_METADATA_BLOCK1;
+ fs_ctx->active_metablock = SFS_METADATA_BLOCK0;
+
+ /* Fill the block metadata for logical datablock 0, which has the physical
+ * id of the active metadata block. For this datablock, the space available
+ * for data is from the end of the metadata to the end of the block.
+ */
+ block_meta.data_start =
+ sfs_mblock_file_meta_offset(fs_ctx, fs_ctx->flash_info->max_num_files);
+ block_meta.free_size = fs_ctx->flash_info->block_size
+ - block_meta.data_start;
+ block_meta.phy_id = SFS_METADATA_BLOCK0;
+ err = sfs_mblock_update_scratch_block_meta(fs_ctx, SFS_LOGICAL_DBLOCK0,
+ &block_meta);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* Fill the block metadata for the dedicated datablocks, which have logical
+ * ids beginning from 1 and physical ids initially beginning from
+ * SFS_INIT_DBLOCK_START. For these datablocks, the space available for
+ * data is the entire block.
+ */
+ block_meta.data_start = 0;
+ block_meta.free_size = fs_ctx->flash_info->block_size;
+ for (i = 0; i < sfs_num_dedicated_dblocks(fs_ctx); i++) {
+ /* If a flash error is detected, the code erases the rest
+ * of the blocks anyway to remove all data stored in them.
+ */
+ err |= fs_ctx->flash_info->erase(fs_ctx->flash_info,
+ i + sfs_init_dblock_start(fs_ctx));
+ }
+
+ /* If an error is detected while erasing the flash, then return a
+ * system error to abort core wipe process.
+ */
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+
+ for (i = 0; i < sfs_num_dedicated_dblocks(fs_ctx); i++) {
+ block_meta.phy_id = i + sfs_init_dblock_start(fs_ctx);
+ err = sfs_mblock_update_scratch_block_meta(fs_ctx, i + 1, &block_meta);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+ }
+
+ /* Initialize file metadata table */
+ (void)memset(&file_metadata, SFS_DEFAULT_EMPTY_BUFF_VAL,
+ SFS_FILE_METADATA_SIZE);
+ for (i = 0; i < fs_ctx->flash_info->max_num_files; i++) {
+ /* In the beginning phys id is same as logical id */
+ /* Update file metadata to reflect new attributes */
+ err = sfs_flash_fs_mblock_update_scratch_file_meta(fs_ctx, i,
+ &file_metadata);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+ }
+
+ err = sfs_mblock_write_scratch_meta_header(fs_ctx);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ /* Commit metadata block modifications to flash */
+ err = fs_ctx->flash_info->flush(fs_ctx->flash_info);
+ if (err != PSA_SUCCESS) {
+ return err;
+ }
+
+ /* Swap active and scratch metablocks */
+ sfs_mblock_swap_metablocks(fs_ctx);
+
+ return PSA_SUCCESS;
+}
+
+void sfs_flash_fs_mblock_set_data_scratch(struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t phy_id, uint32_t lblock)
+{
+ if (lblock != SFS_LOGICAL_DBLOCK0) {
+ fs_ctx->meta_block_header.scratch_dblock = phy_id;
+ }
+}
+
+psa_status_t sfs_flash_fs_mblock_update_scratch_block_meta(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t lblock,
+ struct sfs_block_meta_t *block_meta)
+{
+ psa_status_t err;
+
+ /* If the file is the logical block 0, then update the physical ID to the
+ * current scratch metadata block so that it is correct after the metadata
+ * blocks are swapped.
+ */
+ if (lblock == SFS_LOGICAL_DBLOCK0) {
+ block_meta->phy_id = fs_ctx->scratch_metablock;
+ }
+
+ err = sfs_mblock_update_scratch_block_meta(fs_ctx, lblock, block_meta);
+ if (err != PSA_SUCCESS) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ return sfs_mblock_copy_remaining_block_meta(fs_ctx, lblock);
+}
+
+psa_status_t sfs_flash_fs_mblock_update_scratch_file_meta(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t idx,
+ const struct sfs_file_meta_t *file_meta)
+{
+ size_t pos;
+
+ /* Calculate the position */
+ pos = sfs_mblock_file_meta_offset(fs_ctx, idx);
+ return fs_ctx->flash_info->write(fs_ctx->flash_info,
+ fs_ctx->scratch_metablock,
+ (const uint8_t *)file_meta, pos,
+ SFS_FILE_METADATA_SIZE);
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_mblock.h b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_mblock.h
new file mode 100644
index 000000000..d13a5b0db
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/flash_fs/sfs_flash_fs_mblock.h
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __SFS_FLASH_FS_MBLOCK_H__
+#define __SFS_FLASH_FS_MBLOCK_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "../flash/sfs_flash.h"
+#include "../sfs_utils.h"
+#include <protocols/service/psa/packed-c/status.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * \def SFS_SUPPORTED_VERSION
+ *
+ * \brief Defines the supported version.
+ */
+#define SFS_SUPPORTED_VERSION 0x01
+
+/*!
+ * \def SFS_METADATA_INVALID_INDEX
+ *
+ * \brief Defines the invalid index value when the metadata table is full
+ */
+#define SFS_METADATA_INVALID_INDEX 0xFFFF
+
+/*!
+ * \def SFS_LOGICAL_DBLOCK0
+ *
+ * \brief Defines logical data block 0 ID
+ */
+#define SFS_LOGICAL_DBLOCK0 0
+
+/*!
+ * \struct sfs_metadata_block_header_t
+ *
+ * \brief Structure to store the metadata block header.
+ *
+ * \note The active_swap_count must be the last member to allow it to be
+ * programmed last.
+ *
+ * \note This structure is programmed to flash, so it must be aligned to the
+ * maximum required flash program unit.
+ */
+struct __attribute__((__aligned__(SFS_FLASH_MAX_ALIGNMENT)))
+sfs_metadata_block_header_t {
+ uint32_t scratch_dblock; /*!< Physical block ID of the data
+ * section's scratch block
+ */
+ uint8_t fs_version; /*!< SFS system version */
+ uint8_t active_swap_count; /*!< Physical block ID of the data */
+};
+
+/*!
+ * \struct sfs_block_meta_t
+ *
+ * \brief Structure to store information about each physical flash memory block.
+ *
+ * \note This structure is programmed to flash, so it must be aligned to the
+ * maximum required flash program unit.
+ */
+struct __attribute__((__aligned__(SFS_FLASH_MAX_ALIGNMENT)))
+sfs_block_meta_t {
+ uint32_t phy_id; /*!< Physical ID of this logical block */
+ size_t data_start; /*!< Offset from the beginning of the block to the
+ * location where the data starts
+ */
+ size_t free_size; /*!< Number of bytes free at end of block (set during
+ * block compaction for gap reuse)
+ */
+};
+
+/*!
+ * \struct sfs_file_meta_t
+ *
+ * \brief Structure to store file metadata.
+ *
+ * \note This structure is programmed to flash, so it must be aligned to the
+ * maximum required flash program unit.
+ */
+struct __attribute__((__aligned__(SFS_FLASH_MAX_ALIGNMENT)))
+sfs_file_meta_t {
+ uint32_t lblock; /*!< Logical datablock where file is
+ * stored
+ */
+ size_t data_idx; /*!< Offset in the logical data block */
+ size_t cur_size; /*!< Size in storage system for this #
+ * fragment
+ */
+ size_t max_size; /*!< Maximum size of this file */
+ uint32_t flags; /*!< Flags set when the file was created */
+ uint8_t id[SFS_FILE_ID_SIZE]; /*!< ID of this file */
+};
+
+/**
+ * \struct sfs_flash_fs_ctx_t
+ *
+ * \brief Structure to store the SFS flash file system context.
+ */
+struct sfs_flash_fs_ctx_t {
+ const struct sfs_flash_info_t *flash_info; /**< Info for the flash device */
+ struct sfs_metadata_block_header_t meta_block_header; /**< Metadata block
+ * header
+ */
+ uint32_t active_metablock; /**< Active metadata block */
+ uint32_t scratch_metablock; /**< Scratch metadata block */
+};
+
+/**
+ * \brief Initializes metadata block with the valid/active metablock.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ *
+ * \return Returns value as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_init(struct sfs_flash_fs_ctx_t *fs_ctx);
+
+/**
+ * \brief Copies rest of the file metadata, except for the one pointed by
+ * index.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] idx File metadata entry index to skip
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_cp_remaining_file_meta(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t idx);
+
+/**
+ * \brief Gets current scratch datablock physical ID.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] lblock Logical block number
+ *
+ * \return current scratch data block
+ */
+uint32_t sfs_flash_fs_mblock_cur_data_scratch_id(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t lblock);
+
+/**
+ * \brief Gets file metadata entry index.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] fid ID of the file
+ * \param[out] idx Index of the file metadata in the file system
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_get_file_idx(struct sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid,
+ uint32_t *idx);
+
+/**
+ * \brief Finalizes an update operation.
+ * Last step when a create/write/delete is performed.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ *
+ * \return Returns offset value in metadata block
+ */
+int32_t sfs_flash_fs_mblock_meta_update_finalize(
+ struct sfs_flash_fs_ctx_t *fs_ctx);
+
+/**
+ * \brief Writes the files data area of logical block 0 into the scratch
+ * block.
+ *
+ * \note The files data in the logical block 0 is stored in same physical
+ * block where the metadata is stored. A change in the metadata requires a
+ * swap of physical blocks. So, the files data stored in the current
+ * medadata block needs to be copied in the scratch block, unless
+ * the data of the file processed is located in the logical block 0.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_migrate_lb0_data_to_scratch(
+ struct sfs_flash_fs_ctx_t *fs_ctx);
+
+/**
+ * \brief Reads specified file metadata.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] idx File metadata entry index
+ * \param[out] file_meta Pointer to file meta structure
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_read_file_meta(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t idx,
+ struct sfs_file_meta_t *file_meta);
+
+/**
+ * \brief Reads specified logical block metadata.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] lblock Logical block number
+ * \param[out] block_meta Pointer to block meta structure
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_read_block_metadata(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t lblock,
+ struct sfs_block_meta_t *block_meta);
+
+/**
+ * \brief Reserves space for a file.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] fid File ID
+ * \param[in] size Size of the file for which space is reserve
+ * \param[in] flags Flags set when the file was created
+ * \param[out] file_meta_idx File metadata entry index
+ * \param[out] file_meta File metadata entry
+ * \param[out] block_meta Block metadata entry
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_reserve_file(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ const uint8_t *fid,
+ size_t size,
+ uint32_t flags,
+ uint32_t *file_meta_idx,
+ struct sfs_file_meta_t *file_meta,
+ struct sfs_block_meta_t *block_meta);
+
+/**
+ * \brief Resets metablock by cleaning and initializing the metadatablock.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ *
+ * \return Returns value as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_reset_metablock(
+ struct sfs_flash_fs_ctx_t *fs_ctx);
+
+/**
+ * \brief Sets current data scratch block
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] phy_id Physical ID of scratch data block
+ * \param[in] lblock Logical block number
+ */
+void sfs_flash_fs_mblock_set_data_scratch(struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t phy_id, uint32_t lblock);
+
+/**
+ * \brief Puts logical block's metadata in scratch metadata block
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] lblock Logical block number
+ * \param[in] block_meta Pointer to block's metadata
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_update_scratch_block_meta(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t lblock,
+ struct sfs_block_meta_t *block_meta);
+
+/**
+ * \brief Writes a file metadata entry into scratch metadata block.
+ *
+ * \param[in,out] fs_ctx Filesystem context
+ * \param[in] idx File's index in the metadata table
+ * \param[in] file_meta Metadata pointer
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_flash_fs_mblock_update_scratch_file_meta(
+ struct sfs_flash_fs_ctx_t *fs_ctx,
+ uint32_t idx,
+ const struct sfs_file_meta_t *file_meta);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SFS_FLASH_FS_MBLOCK_H__ */
diff --git a/components/service/secure_storage/provider/secure_flash_store/secure_flash_store.c b/components/service/secure_storage/provider/secure_flash_store/secure_flash_store.c
new file mode 100644
index 000000000..a9f85bd0e
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/secure_flash_store.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "flash/sfs_flash.h"
+#include "flash_fs/sfs_flash_fs.h"
+#include "sfs_utils.h"
+#include "secure_flash_store.h"
+#include <string.h>
+
+#define SFS_MAX_ASSET_SIZE (4096) /* TODO: comes from flash layout */
+#define SFS_CREATE_FLASH_LAYOUT /* TODO: move this to a proper place */
+
+#ifndef SFS_BUF_SIZE
+/* By default, set the SFS buffer size to the max asset size so that all
+ * requests can be handled in one iteration.
+ */
+#define SFS_BUF_SIZE SFS_MAX_ASSET_SIZE
+#endif
+
+#define SFS_INVALID_UID 0 /* TODO: are there any invalid UID-s? */
+
+/* Buffer to store asset data from the caller.
+ * Note: size must be aligned to the max flash program unit to meet the
+ * alignment requirement of the filesystem.
+ */
+static uint8_t asset_data[SFS_UTILS_ALIGN(SFS_BUF_SIZE,
+ SFS_FLASH_MAX_ALIGNMENT)];
+
+static uint8_t g_fid[SFS_FILE_ID_SIZE];
+static struct sfs_file_info_t g_file_info;
+
+static sfs_flash_fs_ctx_t fs_ctx_sfs;
+
+/**
+ * \brief Maps a pair of client id and uid to a file id.
+ *
+ * \param[in] client_id Identifier of the asset's owner (client)
+ * \param[in] uid Identifier for the data
+ * \param[out] fid Identifier of the file
+ */
+static void sfs_get_fid(uint32_t client_id,
+ uint64_t uid,
+ uint8_t *fid)
+{
+ memcpy(fid, (const void *)&client_id, sizeof(client_id));
+ memcpy(fid + sizeof(client_id), (const void *)&uid, sizeof(uid));
+}
+
+psa_status_t sfs_init(void)
+{
+ psa_status_t status;
+
+ /* Initialise the SFS context */
+ status = sfs_flash_fs_prepare(&fs_ctx_sfs,
+ sfs_flash_get_info());
+#ifdef SFS_CREATE_FLASH_LAYOUT
+ /* If SFS_CREATE_FLASH_LAYOUT is set, it indicates that it is required to
+ * create a SFS flash layout. SFS service will generate an empty and valid
+ * SFS flash layout to store assets. It will erase all data located in the
+ * assigned SFS memory area before generating the SFS layout.
+ * This flag is required to be set if the SFS memory area is located in
+ * non-persistent memory.
+ * This flag can be set if the SFS memory area is located in persistent
+ * memory without a previous valid SFS flash layout in it. That is the case
+ * when it is the first time in the device life that the SFS service is
+ * executed.
+ */
+ if (status != PSA_SUCCESS) {
+ /* Remove all data in the SFS memory area and create a valid SFS flash
+ * layout in that area.
+ */
+ status = sfs_flash_fs_wipe_all(&fs_ctx_sfs);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ /* Attempt to initialise again */
+ status = sfs_flash_fs_prepare(&fs_ctx_sfs,
+ sfs_flash_get_info());
+ }
+#endif /* SFS_CREATE_FLASH_LAYOUT */
+
+
+ return status;
+}
+
+psa_status_t sfs_set(uint32_t client_id,
+ uint64_t uid,
+ size_t data_length,
+ const void *p_data,
+ uint32_t create_flags)
+{
+ psa_status_t status;
+ size_t write_size;
+ size_t offset;
+ const uint8_t *data = p_data;
+
+ data = (const uint8_t *)p_data;
+
+ /* Check that the UID is valid */
+ if (uid == SFS_INVALID_UID) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Check that the create_flags does not contain any unsupported flags */
+ if (create_flags & ~(TS_SECURE_STORAGE_FLAG_WRITE_ONCE |
+ TS_SECURE_STORAGE_FLAG_NO_CONFIDENTIALITY |
+ TS_SECURE_STORAGE_FLAG_NO_REPLAY_PROTECTION)) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ /* Set file id */
+ sfs_get_fid(client_id, uid, g_fid);
+
+ /* Read file info */
+ status = sfs_flash_fs_file_get_info(&fs_ctx_sfs, g_fid, &g_file_info);
+ if (status == PSA_SUCCESS) {
+ /* If the object exists and has the write once flag set, then it
+ * cannot be modified. Otherwise it needs to be removed.
+ */
+ if (g_file_info.flags & TS_SECURE_STORAGE_FLAG_WRITE_ONCE) {
+ return PSA_ERROR_NOT_PERMITTED;
+ } else {
+ status = sfs_flash_fs_file_delete(&fs_ctx_sfs, g_fid);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ }
+ } else if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ /* If the file does not exist, then do nothing.
+ * If other error occurred, return it
+ */
+ return status;
+ }
+
+ /* Write as much of the data as will fit in the asset_data buffer */
+ write_size = SFS_UTILS_MIN(data_length, sizeof(asset_data));
+
+ /* Read asset data from the caller */
+ memcpy(asset_data, data, write_size);
+ data += write_size;
+
+ /* Create the file in the file system */
+ status = sfs_flash_fs_file_create(&fs_ctx_sfs, g_fid, data_length,
+ write_size, (uint32_t)create_flags,
+ asset_data);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ offset = write_size;
+ data_length -= write_size;
+
+ /* Iteratively read data from the caller and write it to the filesystem, in
+ * chunks no larger than the size of the asset_data buffer.
+ */
+ while (data_length > 0) {
+ write_size = SFS_UTILS_MIN(data_length, sizeof(asset_data));
+
+ /* Read asset data from the caller */
+ memcpy(asset_data, data, write_size);
+ data += write_size;
+
+ /* Write to the file in the file system */
+ status = sfs_flash_fs_file_write(&fs_ctx_sfs, g_fid,
+ write_size, offset, asset_data);
+ if (status != PSA_SUCCESS) {
+ /* Delete the file to avoid leaving partial data */
+ (void)sfs_flash_fs_file_delete(&fs_ctx_sfs, g_fid);
+ return status;
+ }
+
+ offset += write_size;
+ data_length -= write_size;
+ }
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t sfs_get(uint32_t client_id,
+ uint64_t uid,
+ size_t data_offset,
+ size_t data_size,
+ void *p_data,
+ size_t *p_data_length)
+{
+ psa_status_t status;
+ size_t read_size;
+ uint8_t *data = p_data;
+
+ data = (uint8_t *)p_data;
+
+ /* Check that the UID is valid */
+ if (uid == SFS_INVALID_UID) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Set file id */
+ sfs_get_fid(client_id, uid, g_fid);
+
+ /* Read file info */
+ status = sfs_flash_fs_file_get_info(&fs_ctx_sfs, g_fid, &g_file_info);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ /* Boundary check the incoming request */
+ if (data_offset > g_file_info.size_current) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Copy the object data only from within the file boundary */
+ data_size = SFS_UTILS_MIN(data_size,
+ g_file_info.size_current - data_offset);
+
+ /* Update the size of the output data */
+ *p_data_length = data_size;
+
+ /* Iteratively read data from the filesystem and write it to the caller, in
+ * chunks no larger than the size of the asset_data buffer.
+ */
+ do {
+ /* Read as much of the data as will fit in the asset_data buffer */
+ read_size = SFS_UTILS_MIN(data_size, sizeof(asset_data));
+
+ /* Read file data from the filesystem */
+ status = sfs_flash_fs_file_read(&fs_ctx_sfs, g_fid, read_size,
+ data_offset, asset_data);
+ if (status != PSA_SUCCESS) {
+ *p_data_length = 0;
+ return status;
+ }
+
+ /* Write asset data to the caller */
+ memcpy(data, asset_data, read_size);
+ data += read_size;
+
+ data_offset += read_size;
+ data_size -= read_size;
+ } while (data_size > 0);
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t sfs_get_info(uint32_t client_id, uint64_t uid,
+ struct secure_storage_response_get_info *p_info)
+{
+ psa_status_t status;
+
+ /* Check that the UID is valid */
+ if (uid == SFS_INVALID_UID) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Set file id */
+ sfs_get_fid(client_id, uid, g_fid);
+
+ /* Read file info */
+ status = sfs_flash_fs_file_get_info(&fs_ctx_sfs, g_fid, &g_file_info);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ /* Copy file info to the PSA info struct */
+ p_info->capacity = g_file_info.size_current;
+ p_info->size = g_file_info.size_current;
+ p_info->flags = g_file_info.flags;
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t sfs_remove(uint32_t client_id, uint64_t uid)
+{
+ psa_status_t status;
+
+ /* Check that the UID is valid */
+ if (uid == SFS_INVALID_UID) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Set file id */
+ sfs_get_fid(client_id, uid, g_fid);
+
+ status = sfs_flash_fs_file_get_info(&fs_ctx_sfs, g_fid, &g_file_info);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ /* If the object exists and has the write once flag set, then it
+ * cannot be deleted.
+ */
+ if (g_file_info.flags & TS_SECURE_STORAGE_FLAG_WRITE_ONCE) {
+ return PSA_ERROR_NOT_PERMITTED;
+ }
+
+ /* Delete old file from the persistent area */
+ return sfs_flash_fs_file_delete(&fs_ctx_sfs, g_fid);
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/secure_flash_store.h b/components/service/secure_storage/provider/secure_flash_store/secure_flash_store.h
new file mode 100644
index 000000000..41b7aa865
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/secure_flash_store.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2019-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __SECURE_FLASH_STORE_H__
+#define __SECURE_FLASH_STORE_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <protocols/service/psa/packed-c/status.h>
+#include <protocols/service/secure_storage/packed-c/secure_storage_proto.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Initializes the internal trusted storage system.
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_STORAGE_FAILURE The operation failed because the storage
+ * system initialization has failed (fatal
+ * error)
+ * \retval PSA_ERROR_GENERIC_ERROR The operation failed because of an
+ * unspecified internal failure
+ */
+psa_status_t sfs_init(void);
+
+/**
+ * \brief Create a new, or modify an existing, uid/value pair
+ *
+ * Stores data in the internal storage.
+ *
+ * \param[in] client_id Identifier of the asset's owner (client)
+ * \param[in] uid The identifier for the data
+ * \param[in] data_length The size in bytes of the data in `p_data`
+ * \param[in] create_flags The flags that the data will be stored with
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_NOT_PERMITTED The operation failed because the
+ * provided `uid` value was already
+ * created with
+ * TS_SECURE_STORAGE_FLAG_WRITE_ONCE
+ * \retval PSA_ERROR_NOT_SUPPORTED The operation failed because one or
+ * more of the flags provided in
+ * `create_flags` is not supported or is
+ * not valid
+ * \retval PSA_ERROR_INSUFFICIENT_STORAGE The operation failed because there
+ * was insufficient space on the
+ * storage medium
+ * \retval PSA_ERROR_STORAGE_FAILURE The operation failed because the
+ * physical storage has failed (Fatal
+ * error)
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one
+ * of the provided pointers (`p_data`)
+ * is invalid, for example is `NULL` or
+ * references memory the caller cannot
+ * access
+ */
+psa_status_t sfs_set(uint32_t client_id,
+ uint64_t uid,
+ size_t data_length,
+ const void *p_data,
+ uint32_t create_flags);
+
+/**
+ * \brief Retrieve data associated with a provided UID
+ *
+ * Retrieves up to `data_size` bytes of the data associated with `uid`, starting
+ * at `data_offset` bytes from the beginning of the data. Upon successful
+ * completion, the data will be placed in the `p_data` buffer, which must be at
+ * least `data_size` bytes in size. The length of the data returned will be in
+ * `p_data_length`. If `data_size` is 0, the contents of `p_data_length` will
+ * be set to zero.
+ *
+ * \param[in] client_id Identifier of the asset's owner (client)
+ * \param[in] uid The uid value
+ * \param[in] data_offset The starting offset of the data requested
+ * \param[in] data_size The amount of data requested
+ * \param[out] p_data_length On success, this will contain size of the data
+ * placed in `p_data`.
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_DOES_NOT_EXIST The operation failed because the
+ * provided `uid` value was not found in
+ * the storage
+ * \retval PSA_ERROR_STORAGE_FAILURE The operation failed because the
+ * physical storage has failed (Fatal
+ * error)
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the
+ * provided arguments (`p_data`,
+ * `p_data_length`) is invalid, for example
+ * is `NULL` or references memory the
+ * caller cannot access. In addition, this
+ * can also happen if `data_offset` is
+ * larger than the size of the data
+ * associated with `uid`.
+ */
+psa_status_t sfs_get(uint32_t client_id,
+ uint64_t uid,
+ size_t data_offset,
+ size_t data_size,
+ void *p_data,
+ size_t *p_data_length);
+
+/**
+ * \brief Retrieve the metadata about the provided uid
+ *
+ * Retrieves the metadata stored for a given `uid` as a `secure_storage_response_get_info`
+ * structure.
+ *
+ * \param[in] client_id Identifier of the asset's owner (client)
+ * \param[in] uid The `uid` value
+ * \param[out] p_info A pointer to the `secure_storage_response_get_info` struct that will
+ * be populated with the metadata
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided
+ * uid value was not found in the storage
+ * \retval PSA_ERROR_STORAGE_FAILURE The operation failed because the physical
+ * storage has failed (Fatal error)
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one of the
+ * provided pointers(`p_info`)
+ * is invalid, for example is `NULL` or
+ * references memory the caller cannot
+ * access
+ */
+psa_status_t sfs_get_info(uint32_t client_id, uint64_t uid,
+ struct secure_storage_response_get_info *p_info);
+
+/**
+ * \brief Remove the provided uid and sfs associated data from the storage
+ *
+ * Deletes the data from internal storage.
+ *
+ * \param[in] client_id Identifier of the asset's owner (client)
+ * \param[in] uid The `uid` value
+ *
+ * \return A status indicating the success/failure of the operation
+ *
+ * \retval PSA_SUCCESS The operation completed successfully
+ * \retval PSA_ERROR_INVALID_ARGUMENT The operation failed because one or more
+ * of the given arguments were invalid (null
+ * pointer, wrong flags and so on)
+ * \retval PSA_ERROR_DOES_NOT_EXIST The operation failed because the provided
+ * uid value was not found in the storage
+ * \retval PSA_ERROR_NOT_PERMITTED The operation failed because the provided
+ * uid value was created with
+ * TS_SECURE_STORAGE_FLAG_WRITE_ONCE
+ * \retval PSA_ERROR_STORAGE_FAILURE The operation failed because the physical
+ * storage has failed (Fatal error)
+ */
+psa_status_t sfs_remove(uint32_t client_id, uint64_t uid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SECURE_FLASH_STORE_H__ */
diff --git a/components/service/secure_storage/provider/secure_flash_store/sfs_provider.c b/components/service/secure_storage/provider/secure_flash_store/sfs_provider.c
new file mode 100644
index 000000000..5c801ed67
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/sfs_provider.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "sfs_provider.h"
+#include "secure_flash_store.h"
+#include <protocols/service/secure_storage/packed-c/secure_storage_proto.h>
+#include <protocols/service/psa/packed-c/status.h>
+#include <protocols/rpc/common/packed-c/status.h>
+#include <components/rpc/common/endpoint/call_ep.h>
+
+#include <stdio.h>
+
+/* Handler mapping table for service */
+static const struct service_handler handler_table[] = {
+ {TS_SECURE_STORAGE_OPCODE_SET, sfs_set_handler},
+ {TS_SECURE_STORAGE_OPCODE_GET, sfs_get_handler},
+ {TS_SECURE_STORAGE_OPCODE_GET_INFO, sfs_get_info_handler},
+ {TS_SECURE_STORAGE_OPCODE_REMOVE, sfs_remove_handler}
+};
+
+struct call_ep *sfs_provider_init(struct sfs_provider *context)
+{
+ struct call_ep *call_ep = NULL;
+
+ if (context == NULL)
+ goto out;
+
+ if (sfs_init() != PSA_SUCCESS)
+ goto out;
+
+ service_provider_init(&context->base_provider, context, handler_table,
+ sizeof(handler_table) / sizeof(handler_table[0]));
+
+ call_ep = service_provider_get_call_ep(&context->base_provider);
+
+out:
+ return call_ep;
+}
+
+rpc_status_t sfs_set_handler(void *context, struct call_req *req)
+{
+ struct secure_storage_request_set *request_desc;
+ psa_status_t psa_status;
+
+ /* Checking if the descriptor fits into the request buffer */
+ if (req->req_buf.data_len < sizeof(struct secure_storage_request_set))
+ return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+ request_desc = (struct secure_storage_request_set *)(req->req_buf.data);
+
+ /* Checking for overflow */
+ if (sizeof(struct secure_storage_request_set) + request_desc->data_length < request_desc->data_length)
+ return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+ /* Checking if descriptor and data fits into the request buffer */
+ if (req->req_buf.data_len < sizeof(struct secure_storage_request_set) + request_desc->data_length)
+ return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+ psa_status = sfs_set(req->caller_id, request_desc->uid,
+ request_desc->data_length,
+ request_desc->p_data,
+ request_desc->create_flags);
+ call_req_set_opstatus(req, psa_status);
+
+ return TS_RPC_CALL_ACCEPTED;
+}
+
+rpc_status_t sfs_get_handler(void *context, struct call_req *req)
+{
+ struct secure_storage_request_get *request_desc;
+ psa_status_t psa_status;
+
+ /* Checking if the descriptor fits into the request buffer */
+ if (req->req_buf.data_len < sizeof(struct secure_storage_request_get))
+ return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+ request_desc = (struct secure_storage_request_get *)(req->req_buf.data);
+
+ /* Check if the requested data would fit into the response buffer. */
+ if (req->resp_buf.size < request_desc->data_size)
+ return TS_RPC_ERROR_INVALID_RESP_BODY;
+
+ psa_status = sfs_get(req->caller_id, request_desc->uid,
+ request_desc->data_offset,
+ request_desc->data_size,
+ req->resp_buf.data, &req->resp_buf.data_len);
+ call_req_set_opstatus(req, psa_status);
+
+ return TS_RPC_CALL_ACCEPTED;
+}
+
+rpc_status_t sfs_get_info_handler(void *context, struct call_req *req)
+{
+ struct secure_storage_request_get_info *request_desc;
+ struct secure_storage_response_get_info *response_desc;
+ struct secure_storage_response_get_info storage_info; //TODO: unnecessary?
+ psa_status_t psa_status;
+
+ /* Checking if the descriptor fits into the request buffer */
+ if (req->req_buf.data_len < sizeof(struct secure_storage_request_get_info))
+ return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+ request_desc = (struct secure_storage_request_get_info *)(req->req_buf.data);
+
+ /* Checking if the response structure would fit the response buffer */
+ if (req->resp_buf.size < sizeof(struct secure_storage_response_get_info))
+ return TS_RPC_ERROR_INVALID_RESP_BODY;
+
+ response_desc = (struct secure_storage_response_get_info *)(req->resp_buf.data);
+
+ psa_status = sfs_get_info(req->caller_id, request_desc->uid, &storage_info);
+ call_req_set_opstatus(req, psa_status);
+
+ if (psa_status != PSA_SUCCESS) {
+ req->resp_buf.data_len = 0;
+ }
+ else {
+ response_desc->capacity = storage_info.capacity;
+ response_desc->size = storage_info.size;
+ response_desc->flags = storage_info.flags;
+
+ req->resp_buf.data_len = sizeof(struct secure_storage_response_get_info);
+ }
+
+ return TS_RPC_CALL_ACCEPTED;
+}
+
+rpc_status_t sfs_remove_handler(void *context, struct call_req *req)
+{
+ struct secure_storage_request_remove *request_desc;
+ psa_status_t psa_status;
+
+ /* Checking if the descriptor fits into the request buffer */
+ if (req->req_buf.data_len < sizeof(struct secure_storage_request_remove))
+ return TS_RPC_ERROR_INVALID_REQ_BODY;
+
+ request_desc = (struct secure_storage_request_remove *)(req->req_buf.data);
+
+ psa_status = sfs_remove(req->caller_id, request_desc->uid);
+ call_req_set_opstatus(req, psa_status);
+
+ return TS_RPC_CALL_ACCEPTED;
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/sfs_provider.h b/components/service/secure_storage/provider/secure_flash_store/sfs_provider.h
new file mode 100644
index 000000000..82887de8a
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/sfs_provider.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SFS_HANDLERS_H
+#define SFS_HANDLERS_H
+
+#include <components/service/common/provider/service_provider.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct sfs_provider {
+ struct service_provider base_provider;
+};
+
+struct call_ep *sfs_provider_init(struct sfs_provider *context);
+rpc_status_t sfs_set_handler(void *context, struct call_req *req);
+rpc_status_t sfs_get_handler(void *context, struct call_req *req);
+rpc_status_t sfs_get_info_handler(void *context, struct call_req *req);
+rpc_status_t sfs_remove_handler(void *context, struct call_req *req);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SFS_HANDLERS_H */
diff --git a/components/service/secure_storage/provider/secure_flash_store/sfs_utils.c b/components/service/secure_storage/provider/secure_flash_store/sfs_utils.c
new file mode 100644
index 000000000..3d1627260
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/sfs_utils.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "sfs_utils.h"
+
+psa_status_t sfs_utils_check_contained_in(size_t superset_size,
+ size_t subset_offset,
+ size_t subset_size)
+{
+ /* Check that subset_offset is valid */
+ if (subset_offset > superset_size) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Check that subset_offset + subset_size fits in superset_size.
+ * The previous check passed, so we know that subset_offset <= superset_size
+ * and so the right hand side of the inequality cannot underflow.
+ */
+ if (subset_size > (superset_size - subset_offset)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t sfs_utils_validate_fid(const uint8_t *fid)
+{
+ uint32_t fid_size = SFS_FILE_ID_SIZE;
+
+ /* A file ID is valid if it is non-zero */
+ while (fid_size--) {
+ if (*fid++) {
+ return PSA_SUCCESS;
+ }
+ }
+
+ return PSA_ERROR_DOES_NOT_EXIST;
+}
diff --git a/components/service/secure_storage/provider/secure_flash_store/sfs_utils.h b/components/service/secure_storage/provider/secure_flash_store/sfs_utils.h
new file mode 100644
index 000000000..4a06d4b33
--- /dev/null
+++ b/components/service/secure_storage/provider/secure_flash_store/sfs_utils.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2017-2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef __SFS_UTILS_H__
+#define __SFS_UTILS_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <protocols/service/psa/packed-c/status.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SFS_FILE_ID_SIZE 12
+#define SFS_DEFAULT_EMPTY_BUFF_VAL 0
+
+/**
+ * \brief Macro to check, at compilation time, if data fits in data buffer
+ *
+ * \param[in] err_msg Error message which will be displayed in first
+ * instance if the error is triggered
+ * \param[in] data_size Data size to check if it fits
+ * \param[in] data_buf_size Size of the data buffer
+ *
+ * \return Triggers a compilation error if data_size is bigger than
+ * data_buf_size. The compilation error should be
+ * "... error: 'err_msg' declared as an array with a negative size"
+ */
+#define SFS_UTILS_BOUND_CHECK(err_msg, data_size, data_buf_size) \
+typedef char err_msg[(data_size <= data_buf_size)*2 - 1]
+
+/**
+ * \brief Evaluates to the minimum of the two parameters.
+ */
+#define SFS_UTILS_MIN(x, y) (((x) < (y)) ? (x) : (y))
+
+/**
+ * \brief Evaluates to the maximum of the two parameters.
+ */
+#define SFS_UTILS_MAX(x, y) (((x) > (y)) ? (x) : (y))
+
+/**
+ * \brief Aligns a value up to the provided alignment.
+ *
+ * \param[in] x Value to be aligned
+ * \param[in] a Alignment (must be a power of two)
+ *
+ * \return The least value not less than \p x that is aligned to \p a.
+ */
+#define SFS_UTILS_ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
+
+/**
+ * \brief Checks that a value is aligned to the provided alignment.
+ *
+ * \param[in] x Value to check for alignment
+ * \param[in] a Alignment (must be a power of two)
+ *
+ * \return 1 if \p x is aligned to \p a, 0 otherwise.
+ */
+#define SFS_UTILS_IS_ALIGNED(x, a) (((x) & ((a) - 1)) == 0)
+
+/**
+ * \brief Checks if a subset region is fully contained within a superset region.
+ *
+ * \param[in] superset_size Size of superset region
+ * \param[in] subset_offset Offset of start of subset region from start of
+ * superset region
+ * \param[in] subset_size Size of subset region
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ *
+ * \retval PSA_SUCCESS The subset is contained within the
+ * superset
+ * \retval PSA_ERROR_INVALID_ARGUMENT Otherwise
+ */
+psa_status_t sfs_utils_check_contained_in(size_t superset_size,
+ size_t subset_offset,
+ size_t subset_size);
+
+/**
+ * \brief Validates file ID
+ *
+ * \param[in] fid File ID
+ *
+ * \return Returns error code as specified in \ref psa_status_t
+ */
+psa_status_t sfs_utils_validate_fid(const uint8_t *fid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SFS_UTILS_H__ */
diff --git a/components/service/secure_storage/test/component.cmake b/components/service/secure_storage/test/component.cmake
new file mode 100644
index 000000000..8ea41cf9b
--- /dev/null
+++ b/components/service/secure_storage/test/component.cmake
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/its_tests.cpp"
+ )
+
diff --git a/components/service/secure_storage/test/its_tests.cpp b/components/service/secure_storage/test/its_tests.cpp
new file mode 100644
index 000000000..fbd432dd5
--- /dev/null
+++ b/components/service/secure_storage/test/its_tests.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cstring>
+#include <cstdint>
+#include <CppUTest/TestHarness.h>
+#include <rpc/direct/direct_caller.h>
+#include <service/secure_storage/client/psa/its/its_client.h>
+#include <service/secure_storage/provider/secure_flash_store/sfs_provider.h>
+#include <psa/internal_trusted_storage.h>
+#include <psa/error.h>
+
+TEST_GROUP(InternalTrustedStorageTests)
+{
+ void setup()
+ {
+ struct call_ep *storage_ep = sfs_provider_init(&m_storage_provider);
+ struct rpc_caller *storage_caller = direct_caller_init_default(&m_storage_caller, storage_ep);
+ psa_its_client_init(storage_caller);
+ }
+
+ void teardown()
+ {
+ direct_caller_deinit(&m_storage_caller);
+ }
+
+ struct sfs_provider m_storage_provider;
+ struct direct_caller m_storage_caller;
+};
+
+TEST(InternalTrustedStorageTests, storeNewItem)
+{
+ psa_status_t status;
+ psa_storage_uid_t uid = 10;
+ struct psa_storage_info_t storage_info;
+ static const size_t ITEM_SIZE = 68;
+ uint8_t item[ITEM_SIZE];
+ uint8_t read_item[ITEM_SIZE];
+
+ memset(item, 0x55, sizeof(item));
+
+ /* Probe to check item does not exist */
+ status = psa_its_get_info(uid, &storage_info);
+ CHECK_EQUAL(PSA_ERROR_DOES_NOT_EXIST, status);
+
+ /* Store the item */
+ status = psa_its_set(uid, sizeof(item), item, PSA_STORAGE_FLAG_NONE);
+ CHECK_EQUAL(PSA_SUCCESS, status);
+
+ /* Probe to check item now exists */
+ status = psa_its_get_info(uid, &storage_info);
+ CHECK_EQUAL(PSA_SUCCESS, status);
+ CHECK_EQUAL(sizeof(item), storage_info.size);
+
+ /* Get the item */
+ size_t read_len = 0;
+ status = psa_its_get(uid, 0, sizeof(read_item), read_item, &read_len);
+ CHECK_EQUAL(PSA_SUCCESS, status);
+ CHECK_EQUAL(sizeof(item), read_len);
+ CHECK(memcmp(item, read_item, sizeof(item)) == 0);
+
+ /* Remove the item */
+ status = psa_its_remove(uid);
+ CHECK_EQUAL(PSA_SUCCESS, status);
+
+ /* Expect it to have gone */
+ status = psa_its_get_info(uid, &storage_info);
+ CHECK_EQUAL(PSA_ERROR_DOES_NOT_EXIST, status);
+}
+
+TEST(InternalTrustedStorageTests, storageLimitTest)
+{
+ psa_status_t status;
+ psa_storage_uid_t uid = 10;
+ struct psa_storage_info_t storage_info;
+ static const size_t MAX_ITEM_SIZE = 10000;
+ uint8_t item[MAX_ITEM_SIZE];
+ uint8_t read_item[MAX_ITEM_SIZE];
+
+ memset(item, 0x55, sizeof(item));
+
+ /* Probe to check item does not exist */
+ status = psa_its_get_info(uid, &storage_info);
+ CHECK_EQUAL(PSA_ERROR_DOES_NOT_EXIST, status);
+
+ /* Attempt to store a reasonably big item */
+ status = psa_its_set(uid, 5000, item, PSA_STORAGE_FLAG_NONE);
+ CHECK(PSA_SUCCESS != status);
+
+ /* Attempt to store a stupidly big item */
+ status = psa_its_set(uid, static_cast<size_t>(-1), item, PSA_STORAGE_FLAG_NONE);
+ CHECK(PSA_SUCCESS != status);
+
+ /* Attempt to store a zero length item */
+ status = psa_its_set(uid, 0, item, PSA_STORAGE_FLAG_NONE);
+ CHECK_EQUAL(PSA_SUCCESS, status);
+
+ /* Remove the item */
+ status = psa_its_remove(uid);
+ CHECK_EQUAL(PSA_SUCCESS, status);
+} \ No newline at end of file
diff --git a/protocols/service/psa/packed-c/status.h b/protocols/service/psa/packed-c/status.h
new file mode 100644
index 000000000..a959088e3
--- /dev/null
+++ b/protocols/service/psa/packed-c/status.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PSA_STATUS_H
+#define PSA_STATUS_H
+
+#include <components/service/common/psa/error.h>
+
+#endif /* PSA_STATUS_H */
diff --git a/protocols/service/secure_storage/packed-c/component.cmake b/protocols/service/secure_storage/packed-c/component.cmake
new file mode 100644
index 000000000..b620b463f
--- /dev/null
+++ b/protocols/service/secure_storage/packed-c/component.cmake
@@ -0,0 +1,16 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+
+
+target_include_directories(${TGT}
+ PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}"
+ )
diff --git a/protocols/service/secure_storage/packed-c/secure_storage_proto.h b/protocols/service/secure_storage/packed-c/secure_storage_proto.h
new file mode 100644
index 000000000..1a9b8c845
--- /dev/null
+++ b/protocols/service/secure_storage/packed-c/secure_storage_proto.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SECURE_STORAGE_PROTO_H
+#define SECURE_STORAGE_PROTO_H
+
+#include <stdint.h>
+
+struct __attribute__ ((__packed__)) secure_storage_request_set {
+ uint64_t uid;
+ uint64_t data_length;
+ uint32_t create_flags;
+ uint8_t p_data[];
+};
+
+struct __attribute__ ((__packed__)) secure_storage_request_get {
+ uint64_t uid;
+ uint64_t data_offset;
+ uint64_t data_size;
+};
+
+struct __attribute__ ((__packed__)) secure_storage_request_get_info {
+ uint64_t uid;
+};
+
+struct __attribute__ ((__packed__)) secure_storage_response_get_info {
+ uint64_t capacity;
+ uint64_t size;
+ uint32_t flags;
+};
+
+struct __attribute__ ((__packed__)) secure_storage_request_remove {
+ uint64_t uid;
+};
+
+#define TS_SECURE_STORAGE_OPCODE_SET (0u)
+#define TS_SECURE_STORAGE_OPCODE_GET (1u)
+#define TS_SECURE_STORAGE_OPCODE_GET_INFO (2u)
+#define TS_SECURE_STORAGE_OPCODE_REMOVE (3u)
+
+#define TS_SECURE_STORAGE_FLAG_NONE (0u)
+#define TS_SECURE_STORAGE_FLAG_WRITE_ONCE (1u << 0)
+#define TS_SECURE_STORAGE_FLAG_NO_CONFIDENTIALITY (1u << 1)
+#define TS_SECURE_STORAGE_FLAG_NO_REPLAY_PROTECTION (1u << 2)
+#define TS_SECURE_STORAGE_SUPPORT_SET_EXTENDED (1u << 0)
+
+#endif /* SECURE_STORAGE_PROTO_H */