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 */