Add block_store common interface header file

The block_store structure defines a virtual interface that decouples
a client from any particular implementation of the inteface. block_store.h
forms the base for any concrete block_store. Also adds the generic
storage_partition structure definition that many be used by concrete
block_store implementations to describe any storage partitions presented
by the block store.

Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: I5f6ac2f3e6c2af7eae4ad99045c17c758732a183
diff --git a/components/service/block_storage/block_store/block_store.c b/components/service/block_storage/block_store/block_store.c
new file mode 100644
index 0000000..b127ea8
--- /dev/null
+++ b/components/service/block_storage/block_store/block_store.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <assert.h>
+#include "block_store.h"
+
+psa_status_t block_store_get_partition_info(struct block_store *block_store,
+	const struct uuid_octets *partition_guid,
+	struct storage_partition_info *info)
+{
+	assert(block_store);
+	assert(block_store->interface);
+
+	return block_store->interface->get_partition_info(block_store->context,
+		partition_guid,
+		info);
+}
+
+psa_status_t block_store_open(struct block_store *block_store,
+	uint32_t client_id,
+	const struct uuid_octets *partition_guid,
+	storage_partition_handle_t *handle)
+{
+	assert(block_store);
+	assert(block_store->interface);
+
+	return block_store->interface->open(block_store->context,
+		client_id,
+		partition_guid,
+		handle);
+}
+
+psa_status_t block_store_close(struct block_store *block_store,
+	uint32_t client_id,
+	storage_partition_handle_t handle)
+{
+	assert(block_store);
+	assert(block_store->interface);
+
+	return block_store->interface->close(block_store->context,
+		client_id,
+		handle);
+}
+
+psa_status_t block_store_read(struct block_store *block_store,
+	uint32_t client_id,
+	storage_partition_handle_t handle,
+	uint32_t lba,
+	size_t offset,
+	size_t buffer_size,
+	uint8_t *buffer,
+	size_t *data_len)
+{
+	assert(block_store);
+	assert(block_store->interface);
+
+	return block_store->interface->read(block_store->context,
+		client_id,
+		handle,
+		lba,
+		offset,
+		buffer_size,
+		buffer,
+		data_len);
+}
+
+psa_status_t block_store_write(struct block_store *block_store,
+	uint32_t client_id,
+	storage_partition_handle_t handle,
+	uint32_t lba,
+	size_t offset,
+	const uint8_t *data,
+	size_t data_len,
+	size_t *num_written)
+{
+	assert(block_store);
+	assert(block_store->interface);
+
+	return block_store->interface->write(block_store->context,
+		client_id,
+		handle,
+		lba,
+		offset,
+		data,
+		data_len,
+		num_written);
+}
+
+psa_status_t block_store_erase(struct block_store *block_store,
+	uint32_t client_id,
+	storage_partition_handle_t handle,
+	uint32_t begin_lba,
+	size_t num_blocks)
+{
+	assert(block_store);
+	assert(block_store->interface);
+
+	return block_store->interface->erase(block_store->context,
+		client_id,
+		handle,
+		begin_lba,
+		num_blocks);
+}
diff --git a/components/service/block_storage/block_store/block_store.h b/components/service/block_storage/block_store/block_store.h
new file mode 100644
index 0000000..341e365
--- /dev/null
+++ b/components/service/block_storage/block_store/block_store.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef BLOCK_STORE_H
+#define BLOCK_STORE_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include "psa/error.h"
+#include "common/uuid/uuid.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief Opaque storage partition handle
+ *
+ * An opaque handle to a storage partition opened with a block_store. The meaning of
+ * the handle is private to the concrete block_store that issued the handle.
+ */
+typedef uint32_t storage_partition_handle_t;
+
+/**
+ * \brief Information about a storage partition
+ *
+ * A structure that holds information about the storage partition identified
+ * by the specified unique partition GUID.
+ */
+struct storage_partition_info
+{
+	size_t num_blocks;        /* Number of contiguous blocks from LBA zero */
+	size_t block_size;        /* Block size in bytes */
+};
+
+/**
+ * \brief Base block_store interface
+ *
+ * The block_store_interface structure provides a common interface for
+ * accessing a block-level storage device. A concrete block_store provides
+ * an implementation of this interface. A block_store provides access to
+ * one or more storage partitions, each identified by a unique GUID.
+ */
+struct block_store_interface
+{
+	/**
+	 * \brief Get information about a partition
+	 *
+	 * \param[in]  context         The concrete block_store context
+	 * \param[in]  partition_guid  Identifies the storage partition
+	 * \param[out] info            The partition info structure
+	 *
+	 * \return A status indicating whether the operation succeeded or not.
+	 *
+	 * \retval PSA_SUCCESS                     Operation completed successfully
+	 * \retval PSA_ERROR_INVALID_ARGUMENT      Unrecognized partition GUID
+	 */
+	psa_status_t (*get_partition_info)(void *context,
+		const struct uuid_octets *partition_guid,
+		struct storage_partition_info *info);
+
+	/**
+	 * \brief Open a storage partition identified by its unique partition GUID.
+	 *
+	 * \param[in]  context         The concrete block_store context
+	 * \param[in]  client_id       The requesting client ID
+	 * \param[in]  partition_guid  Identifies the storage partition
+	 * \param[out] handle          The handle to use during the open partition
+	 *
+	 * \return A status indicating whether the operation succeeded or not.
+	 *
+	 * \retval PSA_SUCCESS                     Operation completed successfully
+	 * \retval PSA_ERROR_NOT_PERMITTED         Access not permitted
+	 * \retval PSA_ERROR_INVALID_ARGUMENT      At least one parameter is invalid
+	 */
+	psa_status_t (*open)(void *context,
+		uint32_t client_id,
+		const struct uuid_octets *partition_guid,
+		storage_partition_handle_t *handle);
+
+	/**
+	 * \brief Close a previously opened storage partition.
+	 *
+	 * \param[in]  context         The concrete block_store context
+	 * \param[in]  client_id       The requesting client ID
+	 * \param[in]  handle          The handle corresponding to the partition to close
+	 *
+	 * \return A status indicating whether the operation succeeded or not.
+	 *
+	 * \retval PSA_SUCCESS                     Operation completed successfully
+	 * \retval PSA_ERROR_INVALID_ARGUMENT      Invalid handle
+	 */
+	psa_status_t (*close)(void *context,
+		uint32_t client_id,
+		storage_partition_handle_t handle);
+
+	/**
+	 * \brief Read a block
+	 *
+	 * \param[in]  context         The concrete block_store context
+	 * \param[in]  client_id       The requesting client ID
+	 * \param[in]  handle          The handle corresponding to the open storage partition
+	 * \param[in]  lba             The logical block address
+	 * \param[in]  offset          Offset into the block at which to begin reading
+	 * \param[in]  buffer_size     The size of the client provided buffer
+	 * \param[in]  buffer          The buffer to land read data into
+	 * \param[out] data_len        The number of bytes read.
+	 *
+	 * \return A status indicating whether the operation succeeded or not.
+	 *
+	 * \retval PSA_SUCCESS                     Operation completed successfully
+	 * \retval PSA_ERROR_INVALID_ARGUMENT      Invalid parameter e.g. LBA is invalid
+	 * \retval PSA_ERROR_BUFFER_TOO_SMALL      Buffer not big enough
+	 */
+	psa_status_t (*read)(void *context,
+		uint32_t client_id,
+		storage_partition_handle_t handle,
+		uint32_t lba,
+		size_t offset,
+		size_t buffer_size,
+		uint8_t *buffer,
+		size_t *data_len);
+
+	/**
+	 * \brief Write a block
+	 *
+	 * \param[in]  context         The concrete block_store context
+	 * \param[in]  client_id       The requesting client ID
+	 * \param[in]  handle          The handle corresponding to the open storage partition
+	 * \param[in]  lba             The logical block address
+	 * \param[in]  offset          Offset into the block at which to begin writing
+	 * \param[in]  data            The data to write
+	 * \param[in]  data_len        The number of bytes to write.
+	 * \param[out] num_written     The number of bytes written.
+	 *
+	 * \return A status indicating whether the operation succeeded or not.
+	 *
+	 * \retval PSA_SUCCESS                     Operation completed successfully
+	 * \retval PSA_ERROR_INVALID_ARGUMENT      Invalid parameter e.g. LBA is invalid
+	 */
+	psa_status_t (*write)(void *context,
+		uint32_t client_id,
+		storage_partition_handle_t handle,
+		uint32_t lba,
+		size_t offset,
+		const uint8_t *data,
+		size_t data_len,
+		size_t *num_written);
+
+	/**
+	 * \brief Erase a set of contiguous blocks
+	 *
+	 * Erase the specified set of  contiguous blocks. If a storage technology does not explicitly
+	 * support erase but instead, performs erase on write, a concrete block_store should silently
+	 * do nothing on erase and always return PSA_SUCCESS. If an end_lba is specified that is
+	 * beyond the last block in the partition, the range of erased blocks will be clipped to
+	 * the end of the partition.
+	 *
+	 * \param[in]  context       The concrete block_store context
+	 * \param[in]  client_id     The requesting client ID
+	 * \param[in]  handle        The handle corresponding to the open storage partition
+	 * \param[in]  begin_lba     LBA of first block to erase
+	 * \param[in]  num_blocks    Number of contiguous blocks to erase
+	 *
+	 * \return A status indicating whether the operation succeeded or not.
+	 *
+	 * \retval PSA_SUCCESS                     Operation completed successfully
+	 * \retval PSA_ERROR_INVALID_ARGUMENT      Invalid parameter e.g. LBA is invalid
+	 */
+	psa_status_t (*erase)(void *context,
+		uint32_t client_id,
+		storage_partition_handle_t handle,
+		uint32_t begin_lba,
+		size_t num_blocks);
+};
+
+/**
+ * \brief Base block_store structure
+ *
+ * A concrete block store is responsible for initializing this structure to
+ * enable the block store to be accessed via the generic block_store_interface
+ * function pointers.
+ */
+struct block_store
+{
+	/**
+	 * \brief The opaque context
+	 *
+	 * This is a pointer to the instance data of a block_store specialization that
+	 * realizes the block_store interface. The concrete type of the pointer is specific
+	 * to the specialization.
+	 */
+	void *context;
+
+	/**
+	 * \brief Pointer to a concrete block_store_interface
+	 */
+	const struct block_store_interface *interface;
+};
+
+/**
+ * \brief Public interface functions for calling concrete block_store functions
+ *
+ */
+psa_status_t block_store_get_partition_info(struct block_store *block_store,
+	const struct uuid_octets *partition_guid,
+	struct storage_partition_info *info);
+
+psa_status_t block_store_open(struct block_store *block_store,
+	uint32_t client_id,
+	const struct uuid_octets *partition_guid,
+	storage_partition_handle_t *handle);
+
+psa_status_t block_store_close(struct block_store *block_store,
+	uint32_t client_id,
+	storage_partition_handle_t handle);
+
+psa_status_t block_store_read(struct block_store *block_store,
+	uint32_t client_id,
+	storage_partition_handle_t handle,
+	uint32_t lba,
+	size_t offset,
+	size_t buffer_size,
+	uint8_t *buffer,
+	size_t *data_len);
+
+psa_status_t block_store_write(struct block_store *block_store,
+	uint32_t client_id,
+	storage_partition_handle_t handle,
+	uint32_t lba,
+	size_t offset,
+	const uint8_t *data,
+	size_t data_len,
+	size_t *num_written);
+
+psa_status_t block_store_erase(struct block_store *block_store,
+	uint32_t client_id,
+	storage_partition_handle_t handle,
+	uint32_t begin_lba,
+	size_t num_blocks);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BLOCK_STORE_H */
diff --git a/components/service/block_storage/block_store/component.cmake b/components/service/block_storage/block_store/component.cmake
new file mode 100644
index 0000000..b69ca00
--- /dev/null
+++ b/components/service/block_storage/block_store/component.cmake
@@ -0,0 +1,14 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2022, 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}/block_store.c"
+	"${CMAKE_CURRENT_LIST_DIR}/storage_partition.c"
+	)
diff --git a/components/service/block_storage/block_store/storage_partition.c b/components/service/block_storage/block_store/storage_partition.c
new file mode 100644
index 0000000..5489e6b
--- /dev/null
+++ b/components/service/block_storage/block_store/storage_partition.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <string.h>
+#include "storage_partition.h"
+
+
+void storage_partition_init(
+	struct storage_partition *partition,
+	const struct uuid_octets *partition_guid,
+	size_t num_blocks,
+	size_t block_size)
+{
+	memset(partition, 0, sizeof(struct storage_partition));
+
+	partition->partition_guid = *partition_guid;
+	partition->block_size = block_size;
+	partition->num_blocks = num_blocks;
+}
+
+void storage_partition_deinit(
+	struct storage_partition *partition)
+{
+	memset(partition, 0, sizeof(struct storage_partition));
+}
+
+void storage_partition_extend_whitelist(
+	struct storage_partition *partition,
+	uint32_t client_id)
+{
+	if (partition->whitelist_len < STORAGE_PARTITION_WHITELIST_LEN) {
+
+		partition->whitelist[partition->whitelist_len] = client_id;
+		++partition->whitelist_len;
+	}
+}
+
+bool storage_partition_is_guid_matched(
+	const struct storage_partition *partition,
+	const struct uuid_octets *partition_guid)
+{
+	return (memcmp(&partition->partition_guid, partition_guid, sizeof(struct uuid_octets)) == 0);
+}
+
+bool storage_partition_is_access_permitted(
+	const struct storage_partition *partition,
+	uint32_t client_id)
+{
+	bool is_permitted = (partition->whitelist_len == 0);
+
+	for (size_t i = 0; !is_permitted && i < partition->whitelist_len; ++i)
+		is_permitted = (client_id == partition->whitelist[i]);
+
+	return is_permitted;
+}
+
+bool storage_partition_is_lba_legal(
+	const struct storage_partition *partition,
+	uint32_t lba)
+{
+	return lba < partition->num_blocks;
+}
+
+size_t storage_partition_clip_length(
+	const struct storage_partition *partition,
+	uint32_t lba,
+	size_t offset,
+	size_t req_len)
+{
+	size_t clipped_len = 0;
+
+	if (lba < partition->num_blocks) {
+
+		size_t remaining_len = (partition->num_blocks - lba) * partition->block_size;
+
+		remaining_len = (offset < remaining_len) ? remaining_len - offset : 0;
+		clipped_len = (req_len > remaining_len) ? remaining_len : req_len;
+	}
+
+	return clipped_len;
+}
+
+size_t storage_partition_clip_num_blocks(
+	const struct storage_partition *partition,
+	uint32_t lba,
+	size_t num_blocks)
+{
+	size_t clipped_num_blocks = 0;
+
+	if (lba < partition->num_blocks) {
+
+		size_t remaining_blocks = partition->num_blocks - lba;
+
+		clipped_num_blocks = (num_blocks > remaining_blocks) ? remaining_blocks : num_blocks;
+	}
+
+	return clipped_num_blocks;
+}
diff --git a/components/service/block_storage/block_store/storage_partition.h b/components/service/block_storage/block_store/storage_partition.h
new file mode 100644
index 0000000..848b060
--- /dev/null
+++ b/components/service/block_storage/block_store/storage_partition.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef STORAGE_PARTITION_H
+#define STORAGE_PARTITION_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include "common/uuid/uuid.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Default maximum configurable whitelist length */
+#ifndef STORAGE_PARTITION_WHITELIST_LEN
+#define STORAGE_PARTITION_WHITELIST_LEN    (4)
+#endif
+
+/**
+ * \brief Common storage partition structure
+ *
+ * A block store may present one or more storage partitions. This structure may
+ * be used by concrete block_store implementations to describe a storage partition
+ * in a generic way.
+ */
+struct storage_partition
+{
+	/* Unique partition GUID */
+	struct uuid_octets partition_guid;
+
+	/* Block size in bytes */
+	size_t block_size;
+
+	/* The number of contiguous blocks from LBA zero */
+	size_t num_blocks;
+
+	/* Backend storage block that corresponds to LBA zero */
+	uint32_t base_lba;
+
+	/* Whitelist of client IDs corresponding to clients that are permitted
+	 * to access the partition. A zero length list is treated as a wildcard
+	 * where any client is permitted access. */
+	size_t whitelist_len;
+	uint32_t whitelist[STORAGE_PARTITION_WHITELIST_LEN];
+};
+
+/**
+ * \brief Default storage_partition initialization function
+ *
+ * Initializes a storage_partition with an empty whitelist and a one-to-one
+ * LBA mapping to backend storage.
+ *
+ * \param[in]  partition       The subject storage_partition
+ * \param[in]  partition_guid  The unique partition GUID
+ * \param[in]  num_blocks      The number of contiguous blocks
+ * \param[in]  block_size      Block size in bytes
+ */
+void storage_partition_init(
+	struct storage_partition *partition,
+	const struct uuid_octets *partition_guid,
+	size_t num_blocks,
+	size_t block_size);
+
+/**
+ * \brief Cleans up a previously initialized storage_partition
+ *
+ * Should be called when the storage_partition is no longer needed.
+ *
+ * \param[in]  partition       The subject storage_partition
+ */
+void storage_partition_deinit(
+	struct storage_partition *partition);
+
+/**
+ * \brief Extend the whitelist
+ *
+ * \param[in]  partition    The subject storage_partition
+ * \param[in]  client_id    The client ID to add
+ */
+void storage_partition_extend_whitelist(
+	struct storage_partition *partition,
+	uint32_t client_id);
+
+/**
+ * \brief Check if unique partition GUID matches
+ *
+ * \param[in]  partition       The subject storage_partition
+ * \param[in]  partition_guid  The unique partition GUID
+ * \return     True if GUID matches the storage partition GUID
+ */
+bool storage_partition_is_guid_matched(
+	const struct storage_partition *partition,
+	const struct uuid_octets *partition_guid);
+
+/**
+ * \brief Check if access to the storage partition is permitted
+ *
+ * \param[in]  partition    The subject storage_partition
+ * \param[in]  client_id    The requesting client ID
+ * \return     True if access permitted
+ */
+bool storage_partition_is_access_permitted(
+	const struct storage_partition *partition,
+	uint32_t client_id);
+
+/**
+ * \brief Check if lba is legal for partition
+ *
+ * \param[in]  partition    The subject storage_partition
+ * \param[in]  lba          The LBA to check
+ * \return     True if legal
+ */
+bool storage_partition_is_lba_legal(
+	const struct storage_partition *partition,
+	uint32_t lba);
+
+/**
+ * \brief Clip the length if it exceeds the limits of the partition
+ *
+ * \param[in]  partition    The subject storage_partition
+ * \param[in]  lba          The start LBA
+ * \param[in]  offset       Byte off set from start of block
+ * \param[in]  req_len      Requested length
+ * \return     Clipped length if req_len exceeds limit of partition
+ */
+size_t storage_partition_clip_length(
+	const struct storage_partition *partition,
+	uint32_t lba,
+	size_t offset,
+	size_t req_len);
+
+/**
+ * \brief Clip the number of blocks if it exceeds the limits of the partition
+ *
+ * \param[in]  partition    The subject storage_partition
+ * \param[in]  lba          The start LBA
+ * \param[in]  num_blocks   Requested num_blocks
+ * \return     Clipped num_blocks if request number exceeds the limits of the partition
+ */
+size_t storage_partition_clip_num_blocks(
+	const struct storage_partition *partition,
+	uint32_t lba,
+	size_t num_blocks);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* STORAGE_PARTITION_H */