Add OP-TEE secure storage based storage backend

Implement storage backend for OP-TEE SEL1 SP which uses the existing
secure storage system of OP-TEE with RPMB and REE FS support.

Signed-off-by: Imre Kis <imre.kis@arm.com>
Change-Id: I8300467fc2b4b59d74069a900915cc7e74d5d585
diff --git a/components/service/secure_storage/backend/optee_storage/optee_storage_backend.c b/components/service/secure_storage/backend/optee_storage/optee_storage_backend.c
new file mode 100644
index 0000000..175c14b
--- /dev/null
+++ b/components/service/secure_storage/backend/optee_storage/optee_storage_backend.c
@@ -0,0 +1,608 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2021-2022, Arm Limited.
+ */
+
+#include "optee_storage_backend.h"
+#include "tee/tee_svc_cryp.h"
+#include "tee/tee_svc_storage.h"
+#include <string.h>
+#include <trace.h>
+
+struct optee_storage_context {
+	/* Storing storage ID with select RPMB or REE as the NWd backend */
+	unsigned long storage_id;
+	uint8_t *shared_buffer;
+	size_t shared_buffer_size;
+	size_t shared_buffer_used;
+};
+
+/**
+ * @brief The unique identifier of a storage object is make of the identifier
+ * passed as the function argument and the client's ID.
+ *
+ */
+struct optee_storage_id {
+	uint16_t client_id;
+	uint64_t uid;
+} __packed;
+
+struct optee_storage_header {
+	uint64_t capacity;
+	uint64_t size;
+	uint32_t flags;
+	uint8_t reserved[12];
+} __packed;
+
+#define VALID_FLAGS                                                            \
+	(PSA_STORAGE_FLAG_WRITE_ONCE | PSA_STORAGE_FLAG_NO_CONFIDENTIALITY |   \
+	 PSA_STORAGE_FLAG_NO_REPLAY_PROTECTION)
+#define IS_VALID_FLAG(flag) (((flag) & ~VALID_FLAGS) == 0)
+
+#define TEE_DATA_FLAG_ACCESS_RW                                                \
+	(TEE_DATA_FLAG_ACCESS_READ | TEE_DATA_FLAG_ACCESS_WRITE)
+
+/**
+ * This is the maximum size that should be allocated from the shared memory.
+ */
+#define SHARED_BUFFER_SHADOW_SIZE                                              \
+	(sizeof(uint32_t) + sizeof(struct optee_storage_id) +                  \
+	 sizeof(struct optee_storage_header))
+
+static struct storage_backend optee_storage_backend;
+static struct optee_storage_context optee_storage_context;
+static uint8_t shared_buffer_shadow[SHARED_BUFFER_SHADOW_SIZE];
+
+static psa_status_t get_psa_status(TEE_Result tee_result)
+{
+	switch (tee_result) {
+	case TEE_SUCCESS:
+		return PSA_SUCCESS;
+
+	case TEE_ERROR_OVERFLOW:
+		return PSA_ERROR_INVALID_ARGUMENT;
+
+	case TEE_ERROR_ITEM_NOT_FOUND:
+		return PSA_ERROR_DOES_NOT_EXIST;
+
+	case TEE_ERROR_STORAGE_NOT_AVAILABLE:
+		return PSA_ERROR_STORAGE_FAILURE;
+
+	case TEE_ERROR_CORRUPT_OBJECT:
+		return PSA_ERROR_DATA_CORRUPT;
+
+	case TEE_ERROR_ACCESS_DENIED:
+		return PSA_ERROR_NOT_PERMITTED;
+
+	case TEE_ERROR_OUT_OF_MEMORY:
+		return PSA_ERROR_INSUFFICIENT_STORAGE;
+
+	case TEE_ERROR_ACCESS_CONFLICT:
+		return PSA_ERROR_ALREADY_EXISTS;
+
+	default:
+		EMSG("Unknown storage error: 0x%x", tee_result);
+		return PSA_ERROR_GENERIC_ERROR;
+	}
+}
+
+/**
+ * OP-TEE storage functions need some variables to be accessible from SEL0.
+ * These functions allocated memory from the shared buffer so these checks can
+ * pass.
+ */
+static void *shared_mem_alloc(struct optee_storage_context *ctx, size_t size)
+{
+	size_t used = ctx->shared_buffer_used;
+	size_t end = used + size;
+
+	if (ctx->shared_buffer_size < end || sizeof(shared_buffer_shadow) < end)
+		return NULL;
+
+	memcpy(shared_buffer_shadow + used, ctx->shared_buffer + used, size);
+	ctx->shared_buffer_used += size;
+	memset(ctx->shared_buffer + used, 0x00, size);
+
+	return ctx->shared_buffer + used;
+}
+
+static void shared_mem_cleanup(struct optee_storage_context *ctx)
+{
+	memcpy(ctx->shared_buffer, shared_buffer_shadow,
+	       ctx->shared_buffer_used);
+	ctx->shared_buffer_used = 0;
+}
+
+static void *shared_mem_get_addr_by_offset(struct optee_storage_context *ctx,
+					   size_t offset, size_t size)
+{
+	/* Align offset to n*size */
+	offset = ROUNDUP(offset, size);
+
+	if (ctx->shared_buffer_size < offset + size)
+		return NULL;
+
+	return ctx->shared_buffer + offset;
+}
+
+static TEE_Result open_storage_object(void *context, uint32_t client_id,
+				      uint64_t uid, unsigned long flags,
+				      uint32_t *obj)
+{
+	struct optee_storage_context *ctx =
+		(struct optee_storage_context *)context;
+	struct optee_storage_id *object_id = NULL;
+	uint32_t *obj_internal = NULL;
+	TEE_Result res = TEE_SUCCESS;
+
+	object_id = (struct optee_storage_id *)shared_mem_alloc(ctx, sizeof(*object_id));
+	if (!object_id)
+		return TEE_ERROR_OUT_OF_MEMORY;
+
+	obj_internal = (uint32_t *)shared_mem_alloc(ctx, sizeof(*obj_internal));
+	if (!obj_internal) {
+		res = TEE_ERROR_OUT_OF_MEMORY;
+		goto return_error;
+	}
+
+	object_id->uid = uid;
+	object_id->client_id = client_id;
+
+	res = syscall_storage_obj_open(ctx->storage_id, object_id,
+				       sizeof(*object_id), flags, obj_internal);
+
+	if (res == TEE_SUCCESS)
+		*obj = *obj_internal;
+
+return_error:
+	shared_mem_cleanup(ctx);
+
+	return res;
+}
+
+static TEE_Result create_storage_object(void *context, uint32_t client_id,
+					uint64_t uid, unsigned long flags,
+					void *data, size_t data_length,
+					uint32_t *obj)
+{
+	struct optee_storage_context *ctx =
+		(struct optee_storage_context *)context;
+	struct optee_storage_id *object_id = NULL;
+	uint32_t *obj_internal = NULL;
+	void *data_internal = NULL;
+	TEE_Result res = TEE_SUCCESS;
+
+	object_id =
+		(struct optee_storage_id *)shared_mem_alloc(ctx, sizeof(struct optee_storage_id));
+	if (!object_id)
+		return TEE_ERROR_OUT_OF_MEMORY;
+
+	obj_internal = (uint32_t *)shared_mem_alloc(ctx, sizeof(*obj_internal));
+	if (!obj_internal) {
+		res = TEE_ERROR_OUT_OF_MEMORY;
+		goto return_error;
+	};
+
+	data_internal = shared_mem_alloc(ctx, data_length);
+	if (!data_internal) {
+		res = TEE_ERROR_OUT_OF_MEMORY;
+		goto return_error;
+	}
+
+	object_id->uid = uid;
+	object_id->client_id = client_id;
+
+	memcpy(data_internal, data, data_length);
+
+	res = syscall_storage_obj_create(ctx->storage_id, object_id,
+					 sizeof(*object_id), flags,
+					 TEE_HANDLE_NULL, data_internal,
+					 data_length, obj_internal);
+	if (res == TEE_SUCCESS)
+		*obj = *obj_internal;
+
+return_error:
+	shared_mem_cleanup(ctx);
+
+	return res;
+}
+
+static TEE_Result get_header(void *context, uint32_t obj,
+			     struct optee_storage_header *header)
+{
+	struct optee_storage_context *ctx =
+		(struct optee_storage_context *)context;
+	TEE_Result res = TEE_SUCCESS;
+	uint64_t *count = NULL;
+	struct optee_storage_header *header_internal = NULL;
+
+	header_internal =
+		(struct optee_storage_header *)shared_mem_alloc(ctx, sizeof(*header_internal));
+	if (!header_internal)
+		return TEE_ERROR_OUT_OF_MEMORY;
+
+	count = (uint64_t *)shared_mem_alloc(ctx, sizeof(*count));
+	if (!count) {
+		res = TEE_ERROR_OUT_OF_MEMORY;
+		goto return_error;
+	}
+
+	res = syscall_storage_obj_seek(obj, 0, TEE_DATA_SEEK_SET);
+	if (res != TEE_SUCCESS)
+		goto return_error;
+
+	res = syscall_storage_obj_read(obj, header_internal,
+				       sizeof(*header_internal), count);
+	if (res != TEE_SUCCESS)
+		goto return_error;
+
+	if (*count != sizeof(*header))
+		res = TEE_ERROR_GENERIC;
+
+	memcpy(header, header_internal, sizeof(*header));
+
+return_error:
+	shared_mem_cleanup(ctx);
+
+	return res;
+}
+
+static TEE_Result set_header(void *context, uint32_t obj,
+			     struct optee_storage_header *header)
+{
+	struct optee_storage_context *ctx =
+		(struct optee_storage_context *)context;
+	TEE_Result res = TEE_SUCCESS;
+	struct optee_storage_header *header_internal = NULL;
+
+	header_internal =
+		(struct optee_storage_header *)shared_mem_alloc(ctx, sizeof(*header_internal));
+	if (!header_internal)
+		return TEE_ERROR_OUT_OF_MEMORY;
+
+	memcpy(header_internal, header, sizeof(*header_internal));
+
+	res = syscall_storage_obj_seek(obj, 0, TEE_DATA_SEEK_SET);
+	if (res != TEE_SUCCESS)
+		goto return_error;
+
+	res = syscall_storage_obj_write(obj, header_internal,
+					sizeof(*header_internal));
+
+return_error:
+	shared_mem_cleanup(ctx);
+
+	return res;
+}
+
+static psa_status_t optee_storage_set(void *context, uint32_t client_id,
+				      uint64_t uid, size_t data_length,
+				      const void *p_data, uint32_t create_flags)
+{
+	TEE_Result res = TEE_SUCCESS;
+	TEE_Result res_close = TEE_SUCCESS;
+	uint32_t obj = 0;
+	struct optee_storage_header storage_header = { 0 };
+
+	if (uid == 0)
+		return PSA_ERROR_INVALID_ARGUMENT;
+
+	if (!IS_VALID_FLAG(create_flags))
+		return PSA_ERROR_INVALID_ARGUMENT;
+
+	res = open_storage_object(context, client_id, uid,
+				  TEE_DATA_FLAG_ACCESS_RW, &obj);
+	if (res == TEE_SUCCESS) {
+		/* The object exists, read the header */
+		res = get_header(context, obj, &storage_header);
+		if (res != TEE_SUCCESS)
+			goto close_and_return_result;
+
+		if (storage_header.flags & PSA_STORAGE_FLAG_WRITE_ONCE) {
+			res = TEE_ERROR_ACCESS_DENIED;
+			goto close_and_return_result;
+		}
+
+		storage_header.capacity = data_length;
+		storage_header.size = data_length;
+		storage_header.flags = create_flags;
+
+		res = set_header(context, obj, &storage_header);
+	} else if (res == TEE_ERROR_ITEM_NOT_FOUND) {
+		/* The object does not exist, create a new one. */
+		storage_header.capacity = data_length;
+		storage_header.size = data_length;
+		storage_header.flags = create_flags;
+
+		res = create_storage_object(context, client_id, uid,
+					    TEE_DATA_FLAG_ACCESS_RW,
+					    &storage_header,
+					    sizeof(storage_header), &obj);
+	}
+
+	if (res != TEE_SUCCESS) {
+		/* Fatal error happened during open or create */
+		goto return_result;
+	}
+
+	res = syscall_storage_obj_seek(obj, sizeof(storage_header),
+				       TEE_DATA_SEEK_SET);
+	if (res != TEE_SUCCESS)
+		goto close_and_return_result;
+
+	res = syscall_storage_obj_write(obj, (void *)p_data, data_length);
+
+close_and_return_result:
+	res_close = syscall_cryp_obj_close(obj);
+
+return_result:
+	return get_psa_status(res != TEE_SUCCESS ? res : res_close);
+}
+
+static psa_status_t optee_storage_get(void *context, uint32_t client_id,
+				      uint64_t uid, size_t data_offset,
+				      size_t data_size, void *p_data,
+				      size_t *p_data_length)
+{
+	struct optee_storage_context *ctx =
+		(struct optee_storage_context *)context;
+	TEE_Result res = TEE_SUCCESS;
+	TEE_Result res_close = TEE_SUCCESS;
+	uint32_t obj = 0;
+	struct optee_storage_header storage_header = { 0 };
+	int32_t data_start_index = 0;
+	size_t *p_data_length_internal = NULL;
+
+	if (uid == 0)
+		return PSA_ERROR_INVALID_ARGUMENT;
+
+	res = open_storage_object(context, client_id, uid,
+				  TEE_DATA_FLAG_ACCESS_READ, &obj);
+	if (res != TEE_SUCCESS)
+		goto return_result;
+
+	res = get_header(context, obj, &storage_header);
+	if (res != TEE_SUCCESS)
+		goto close_and_return_result;
+
+	/* Validating data_offset */
+	if (data_offset > storage_header.size ||
+	    ADD_OVERFLOW(data_offset, sizeof(storage_header),
+			 &data_start_index)) {
+		res = TEE_ERROR_OVERFLOW;
+		goto close_and_return_result;
+	}
+
+	/* Limiting data_size */
+	data_size = MIN(data_size, storage_header.size - data_offset);
+
+	res = syscall_storage_obj_seek(obj, data_start_index,
+				       TEE_DATA_SEEK_SET);
+	if (res != TEE_SUCCESS)
+		goto close_and_return_result;
+
+	/**
+	 * This allocation has to be different because otherwise the data would
+	 * be overwritten by the returned data length. In this case the length
+	 * variable is allocated after the data area of the shared memory.
+	 */
+	p_data_length_internal = (size_t *)shared_mem_get_addr_by_offset(ctx, data_size,
+									 sizeof(size_t));
+	if (!p_data_length_internal) {
+		res = TEE_ERROR_OUT_OF_MEMORY;
+		goto close_and_return_result;
+	}
+
+	res = syscall_storage_obj_read(obj, p_data, data_size,
+				       p_data_length_internal);
+	if (res == TEE_SUCCESS) {
+		*p_data_length = *p_data_length_internal;
+		*p_data_length_internal = 0;
+	}
+
+close_and_return_result:
+	res_close = syscall_cryp_obj_close(obj);
+
+return_result:
+	return get_psa_status(res != TEE_SUCCESS ? res : res_close);
+}
+
+static psa_status_t optee_storage_get_info(void *context, uint32_t client_id,
+					   uint64_t uid,
+					   struct psa_storage_info_t *p_info)
+{
+	TEE_Result res = TEE_SUCCESS;
+	TEE_Result res_close = TEE_SUCCESS;
+	uint32_t obj = 0;
+	struct optee_storage_header storage_header = { 0 };
+
+	if (uid == 0)
+		return PSA_ERROR_INVALID_ARGUMENT;
+
+	res = open_storage_object(context, client_id, uid,
+				  TEE_DATA_FLAG_ACCESS_READ, &obj);
+	if (res != TEE_SUCCESS)
+		goto return_result;
+
+	res = get_header(context, obj, &storage_header);
+	if (res != TEE_SUCCESS)
+		goto close_and_return_result;
+
+	p_info->capacity = storage_header.capacity;
+	p_info->size = storage_header.size;
+	p_info->flags = storage_header.flags;
+
+close_and_return_result:
+	res_close = syscall_cryp_obj_close(obj);
+
+return_result:
+	return get_psa_status(res != TEE_SUCCESS ? res : res_close);
+}
+
+static psa_status_t optee_storage_remove(void *context, uint32_t client_id,
+					 uint64_t uid)
+{
+	TEE_Result res = TEE_SUCCESS;
+	TEE_Result res_close = TEE_SUCCESS;
+	uint32_t obj = 0;
+	struct optee_storage_header storage_header = { 0 };
+
+	if (uid == 0)
+		return PSA_ERROR_INVALID_ARGUMENT;
+
+	res = open_storage_object(context, client_id, uid,
+				  TEE_DATA_FLAG_ACCESS_READ |
+					  TEE_DATA_FLAG_ACCESS_WRITE_META,
+				  &obj);
+	if (res != TEE_SUCCESS)
+		goto return_result;
+
+	res = get_header(context, obj, &storage_header);
+	if (res != TEE_SUCCESS)
+		goto close_and_return_result;
+
+	if (storage_header.flags & PSA_STORAGE_FLAG_WRITE_ONCE) {
+		res = TEE_ERROR_ACCESS_DENIED;
+		goto close_and_return_result;
+	}
+
+	res = syscall_storage_obj_del(obj);
+	if (res == TEE_SUCCESS)
+		/* Skip close on successful delete */
+		goto return_result;
+
+close_and_return_result:
+	res_close = syscall_cryp_obj_close(obj);
+
+return_result:
+	return get_psa_status(res != TEE_SUCCESS ? res : res_close);
+}
+
+static psa_status_t optee_storage_create(void *context, uint32_t client_id,
+					 uint64_t uid, size_t capacity,
+					 uint32_t create_flags)
+{
+	TEE_Result res = TEE_SUCCESS;
+	TEE_Result res_close = TEE_SUCCESS;
+	uint32_t obj = 0;
+	struct optee_storage_header storage_header = { 0 };
+
+	if (uid == 0)
+		return PSA_ERROR_INVALID_ARGUMENT;
+
+	if (!IS_VALID_FLAG(create_flags))
+		return PSA_ERROR_INVALID_ARGUMENT;
+
+	if (create_flags & PSA_STORAGE_FLAG_WRITE_ONCE)
+		return PSA_ERROR_NOT_SUPPORTED;
+
+	res = create_storage_object(context, client_id, uid,
+				    TEE_DATA_FLAG_ACCESS_READ |
+					    TEE_DATA_FLAG_ACCESS_WRITE,
+				    NULL, 0, &obj);
+	if (res != TEE_SUCCESS)
+		goto return_result;
+
+	storage_header.capacity = capacity;
+	storage_header.size = 0;
+	storage_header.flags = create_flags;
+
+	res = set_header(context, obj, &storage_header);
+
+	res_close = syscall_cryp_obj_close(obj);
+
+return_result:
+	return get_psa_status(res != TEE_SUCCESS ? res : res_close);
+}
+
+static psa_status_t optee_storage_set_extended(void *context,
+					       uint32_t client_id, uint64_t uid,
+					       size_t data_offset,
+					       size_t data_length,
+					       const void *p_data)
+{
+	TEE_Result res = TEE_SUCCESS;
+	TEE_Result res_close = TEE_SUCCESS;
+	uint32_t obj = 0;
+	struct optee_storage_header storage_header = { 0 };
+	int32_t data_start_index = 0;
+	size_t data_end_index = 0;
+
+	if (uid == 0)
+		return PSA_ERROR_INVALID_ARGUMENT;
+
+	res = open_storage_object(context, client_id, uid,
+				  TEE_DATA_FLAG_ACCESS_RW, &obj);
+	if (res != TEE_SUCCESS)
+		goto return_result;
+
+	res = get_header(context, obj, &storage_header);
+	if (res != TEE_SUCCESS)
+		goto close_and_return_result;
+
+	if (data_offset > storage_header.size ||
+	    ADD_OVERFLOW(data_offset, data_length, &data_end_index) ||
+	    data_end_index > storage_header.capacity ||
+	    ADD_OVERFLOW(data_offset, sizeof(storage_header),
+			 &data_start_index)) {
+		res = TEE_ERROR_OVERFLOW;
+		goto close_and_return_result;
+	}
+
+	res = syscall_storage_obj_seek(obj, data_start_index,
+				       TEE_DATA_SEEK_SET);
+	if (res != TEE_SUCCESS)
+		goto close_and_return_result;
+
+	res = syscall_storage_obj_write(obj, (void *)p_data, data_length);
+	if (res != TEE_SUCCESS)
+		goto close_and_return_result;
+
+	storage_header.size = MAX(storage_header.size, data_end_index);
+
+	res = set_header(context, obj, &storage_header);
+	if (res != TEE_SUCCESS)
+		goto close_and_return_result;
+
+close_and_return_result:
+	res_close = syscall_cryp_obj_close(obj);
+
+return_result:
+	return get_psa_status(res != TEE_SUCCESS ? res : res_close);
+}
+
+static uint32_t optee_storage_get_support(void *context __unused,
+					  uint32_t client_id __unused)
+{
+	return PSA_STORAGE_SUPPORT_SET_EXTENDED;
+}
+
+static struct storage_backend_interface optee_storage_interface = {
+	.set = optee_storage_set,
+	.get = optee_storage_get,
+	.get_info = optee_storage_get_info,
+	.remove = optee_storage_remove,
+	.create = optee_storage_create,
+	.set_extended = optee_storage_set_extended,
+	.get_support = optee_storage_get_support
+};
+
+struct storage_backend *optee_storage_backend_init(unsigned long storage_id)
+{
+	optee_storage_context.storage_id = storage_id;
+	optee_storage_backend.context = &optee_storage_context;
+	optee_storage_backend.interface = &optee_storage_interface;
+
+	return &optee_storage_backend;
+}
+
+void optee_storage_backend_assign_shared_buffer(struct storage_backend *backend,
+						void *buffer,
+						size_t buffer_size)
+{
+	struct optee_storage_context *context =
+		(struct optee_storage_context *)backend->context;
+	context->shared_buffer = (uint8_t *)buffer;
+	context->shared_buffer_size = buffer_size;
+	context->shared_buffer_used = 0;
+}
diff --git a/components/service/secure_storage/backend/optee_storage/optee_storage_backend.h b/components/service/secure_storage/backend/optee_storage/optee_storage_backend.h
new file mode 100644
index 0000000..b31a7df
--- /dev/null
+++ b/components/service/secure_storage/backend/optee_storage/optee_storage_backend.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (c) 2021-2022, Arm Limited.
+ */
+
+#ifndef OPTEE_STORAGE_BACKEND_H_
+#define OPTEE_STORAGE_BACKEND_H_
+
+#include "service/secure_storage/backend/storage_backend.h"
+#include "tee_api_defines_extensions.h"
+
+/**
+ * @brief Initializes the storage backend according to the selected storage type
+ *
+ * @param storage_id TEE_STORAGE_PRIVATE_REE or TEE_STORAGE_PRIVATE_RPMB
+ * @return struct storage_backend * Storage backend descriptor
+ */
+struct storage_backend *optee_storage_backend_init(unsigned long storage_id);
+
+/**
+ * @brief Allows the caller to assign a shared buffer which can be used to
+ * allocate variables in the storage system which has access from the SEL0 SP.
+ * This is necessary to pass the MMU checks.
+ *
+ * @param buffer Address of the buffer
+ * @param buffer_size  Buffer size
+ */
+void optee_storage_backend_assign_shared_buffer(struct storage_backend *backend,
+						void *buffer,
+						size_t buffer_size);
+
+#endif /* OPTEE_STORAGE_BACKEND_H_ */