Add partitioned_block_store component
The partitioned_block_store is a block_store that can be stacked
over an underlying block store to allow it to be presented as a
set of separate storage partitions, each with their own partition
GUID and access permissions.
Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: Ia6e3118fe49689f69f11279e0e1ca09ac5b07ff8
diff --git a/components/service/block_storage/block_store/partitioned/component.cmake b/components/service/block_storage/block_store/partitioned/component.cmake
new file mode 100644
index 0000000..0eb6a73
--- /dev/null
+++ b/components/service/block_storage/block_store/partitioned/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# 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}/partitioned_block_store.c"
+ )
diff --git a/components/service/block_storage/block_store/partitioned/partitioned_block_store.c b/components/service/block_storage/block_store/partitioned/partitioned_block_store.c
new file mode 100644
index 0000000..3c5c1fd
--- /dev/null
+++ b/components/service/block_storage/block_store/partitioned/partitioned_block_store.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stddef.h>
+#include "partitioned_block_store.h"
+
+
+static psa_status_t find_by_partition_guid(
+ const struct partitioned_block_store *partitioned_block_store,
+ const struct uuid_octets *partition_guid,
+ size_t *index)
+{
+ psa_status_t status = PSA_ERROR_INVALID_ARGUMENT;
+
+ for (size_t i = 0; i < partitioned_block_store->num_partitions; i++) {
+
+ if (storage_partition_is_guid_matched(
+ &partitioned_block_store->storage_partition[i],
+ partition_guid)) {
+
+ *index = i;
+ status = PSA_SUCCESS;
+ break;
+ }
+ }
+
+ return status;
+}
+
+static psa_status_t validate_partition_request(
+ const struct partitioned_block_store *partitioned_block_store,
+ uint32_t client_id,
+ storage_partition_handle_t handle,
+ const struct storage_partition **partition)
+{
+ size_t index = (size_t)handle;
+
+ if (index >= PARTITIONED_BLOCK_STORE_MAX_PARTITIONS)
+ return PSA_ERROR_INVALID_ARGUMENT;
+
+ const struct storage_partition *requested_partition =
+ &partitioned_block_store->storage_partition[index];
+
+ if (!storage_partition_is_access_permitted(requested_partition, client_id))
+ return PSA_ERROR_NOT_PERMITTED;
+
+ /* Access permitted to requested partition */
+ *partition = requested_partition;
+
+ return PSA_SUCCESS;
+}
+
+static psa_status_t partitioned_block_store_get_partition_info(void *context,
+ const struct uuid_octets *partition_guid,
+ struct storage_partition_info *info)
+{
+ const struct partitioned_block_store *partitioned_block_store =
+ (struct partitioned_block_store*)context;
+
+ size_t partition_index = 0;
+ psa_status_t status = find_by_partition_guid(
+ partitioned_block_store,
+ partition_guid,
+ &partition_index);
+
+ if (status == PSA_SUCCESS) {
+
+ const struct storage_partition *partition =
+ &partitioned_block_store->storage_partition[partition_index];
+
+ info->block_size = partition->block_size;
+ info->num_blocks = partition->num_blocks;
+ }
+
+ return status;
+}
+
+static psa_status_t partitioned_block_store_open(void *context,
+ uint32_t client_id,
+ const struct uuid_octets *partition_guid,
+ storage_partition_handle_t *handle)
+{
+ const struct partitioned_block_store *partitioned_block_store =
+ (struct partitioned_block_store*)context;
+
+ size_t partition_index = 0;
+ psa_status_t status = find_by_partition_guid(
+ partitioned_block_store,
+ partition_guid,
+ &partition_index);
+
+ if (status == PSA_SUCCESS) {
+
+ const struct storage_partition *partition =
+ &partitioned_block_store->storage_partition[partition_index];
+
+ if (storage_partition_is_access_permitted(partition, client_id)) {
+
+ *handle = (storage_partition_handle_t)partition_index;
+ }
+ else {
+
+ status = PSA_ERROR_NOT_PERMITTED;
+ }
+ }
+
+ return status;
+}
+
+static psa_status_t partitioned_block_store_close(void *context,
+ uint32_t client_id,
+ storage_partition_handle_t handle)
+{
+ const struct partitioned_block_store *partitioned_block_store =
+ (struct partitioned_block_store*)context;
+
+ const struct storage_partition *partition = NULL;
+
+ psa_status_t status = validate_partition_request(
+ partitioned_block_store,
+ client_id,
+ handle,
+ &partition);
+
+ return status;
+}
+
+static psa_status_t partitioned_block_store_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)
+{
+ const struct partitioned_block_store *partitioned_block_store =
+ (struct partitioned_block_store*)context;
+
+ const struct storage_partition *partition = NULL;
+
+ psa_status_t status = validate_partition_request(
+ partitioned_block_store,
+ client_id,
+ handle,
+ &partition);
+
+ if (status == PSA_SUCCESS) {
+
+ if (storage_partition_is_lba_legal(partition, lba)) {
+
+ size_t clipped_read_len = storage_partition_clip_length(
+ partition,
+ lba, offset,
+ buffer_size);
+
+ /* Read from underlying back store */
+ status = block_store_read(
+ partitioned_block_store->back_store,
+ partitioned_block_store->local_client_id,
+ partitioned_block_store->back_store_handle,
+ partition->base_lba + lba,
+ offset,
+ clipped_read_len,
+ buffer,
+ data_len);
+ }
+ else {
+
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ }
+ }
+
+ return status;
+}
+
+static psa_status_t partitioned_block_store_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)
+{
+ const struct partitioned_block_store *partitioned_block_store =
+ (struct partitioned_block_store*)context;
+
+ const struct storage_partition *partition = NULL;
+
+ psa_status_t status = validate_partition_request(
+ partitioned_block_store,
+ client_id,
+ handle,
+ &partition);
+
+ if (status == PSA_SUCCESS) {
+
+ if (storage_partition_is_lba_legal(partition, lba)) {
+
+ size_t clipped_data_len = storage_partition_clip_length(
+ partition, lba, offset,
+ data_len);
+
+ /* Write to underlying back store */
+ status = block_store_write(
+ partitioned_block_store->back_store,
+ partitioned_block_store->local_client_id,
+ partitioned_block_store->back_store_handle,
+ partition->base_lba + lba,
+ offset,
+ data,
+ clipped_data_len,
+ num_written);
+ }
+ else {
+
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ }
+ }
+
+ return status;
+}
+
+static psa_status_t partitioned_block_store_erase(void *context,
+ uint32_t client_id,
+ storage_partition_handle_t handle,
+ uint32_t begin_lba,
+ size_t num_blocks)
+{
+ const struct partitioned_block_store *partitioned_block_store =
+ (struct partitioned_block_store*)context;
+
+ const struct storage_partition *partition = NULL;
+
+ psa_status_t status = validate_partition_request(
+ partitioned_block_store,
+ client_id,
+ handle,
+ &partition);
+
+ if (status == PSA_SUCCESS) {
+
+ if (storage_partition_is_lba_legal(partition, begin_lba)) {
+
+ size_t clipped_num_blocks = storage_partition_clip_num_blocks(
+ partition, begin_lba,
+ num_blocks);
+
+ status = block_store_erase(
+ partitioned_block_store->back_store,
+ partitioned_block_store->local_client_id,
+ partitioned_block_store->back_store_handle,
+ partition->base_lba + begin_lba,
+ clipped_num_blocks);
+ }
+ else {
+
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ }
+ }
+
+ return status;
+}
+
+struct block_store *partitioned_block_store_init(
+ struct partitioned_block_store *partitioned_block_store,
+ uint32_t local_client_id,
+ const struct uuid_octets *back_store_guid,
+ struct block_store *back_store)
+{
+ /* Define concrete block store interface */
+ static const struct block_store_interface interface =
+ {
+ partitioned_block_store_get_partition_info,
+ partitioned_block_store_open,
+ partitioned_block_store_close,
+ partitioned_block_store_read,
+ partitioned_block_store_write,
+ partitioned_block_store_erase
+ };
+
+ /* Initialize base block_store */
+ partitioned_block_store->base_block_store.context = partitioned_block_store;
+ partitioned_block_store->base_block_store.interface = &interface;
+
+ /* Note the local client ID - this corresponds to the local environment */
+ partitioned_block_store->local_client_id = local_client_id;
+
+ /* Initially no partitions. */
+ partitioned_block_store->num_partitions = 0;
+
+ /* Stack over provided back store */
+ partitioned_block_store->back_store = back_store;
+
+ /* Get information about the underlying back store */
+ struct storage_partition_info info;
+ psa_status_t status = block_store_get_partition_info(partitioned_block_store->back_store,
+ back_store_guid,
+ &info);
+
+ if (status != PSA_SUCCESS)
+ return NULL;
+
+ partitioned_block_store->back_store_block_size = info.block_size;
+ partitioned_block_store->back_store_num_blocks = info.num_blocks;
+
+ /* Open underlying block store */
+ status = block_store_open(partitioned_block_store->back_store,
+ partitioned_block_store->local_client_id,
+ back_store_guid,
+ &partitioned_block_store->back_store_handle);
+
+ if (status != PSA_SUCCESS)
+ return NULL;
+
+ return &partitioned_block_store->base_block_store;
+}
+
+void partitioned_block_store_deinit(
+ struct partitioned_block_store *partitioned_block_store)
+{
+ block_store_close(partitioned_block_store->back_store,
+ partitioned_block_store->local_client_id,
+ partitioned_block_store->back_store_handle);
+
+ for (size_t i = 0; i < partitioned_block_store->num_partitions; ++i)
+ storage_partition_deinit(&partitioned_block_store->storage_partition[i]);
+}
+
+bool partitioned_block_store_add_partition(
+ struct partitioned_block_store *partitioned_block_store,
+ const struct uuid_octets *partition_guid,
+ uint32_t starting_lba,
+ uint32_t ending_lba,
+ uint64_t attributes,
+ const char *owner)
+{
+ (void)attributes;
+ (void)owner;
+
+ if (partitioned_block_store->num_partitions >= PARTITIONED_BLOCK_STORE_MAX_PARTITIONS)
+ return false;
+
+ /* Check partition blocks lie within limits of the back store */
+ if ((starting_lba > ending_lba) ||
+ (ending_lba >= partitioned_block_store->back_store_num_blocks))
+ return false;
+
+ /* Initialise a new storage_partition structure */
+ struct storage_partition *storage_partition =
+ &partitioned_block_store->storage_partition[partitioned_block_store->num_partitions];
+
+ storage_partition_init(
+ storage_partition,
+ partition_guid,
+ ending_lba - starting_lba + 1,
+ partitioned_block_store->back_store_block_size);
+
+ storage_partition->base_lba = starting_lba;
+
+ ++partitioned_block_store->num_partitions;
+
+ return true;
+}
diff --git a/components/service/block_storage/block_store/partitioned/partitioned_block_store.h b/components/service/block_storage/block_store/partitioned/partitioned_block_store.h
new file mode 100644
index 0000000..adf5fd8
--- /dev/null
+++ b/components/service/block_storage/block_store/partitioned/partitioned_block_store.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef PARTITIONED_BLOCK_STORE_H
+#define PARTITIONED_BLOCK_STORE_H
+
+#include <stdbool.h>
+#include "service/block_storage/block_store/block_store.h"
+#include "service/block_storage/block_store/storage_partition.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef PARTITIONED_BLOCK_STORE_MAX_PARTITIONS
+#define PARTITIONED_BLOCK_STORE_MAX_PARTITIONS (8)
+#endif
+
+/**
+ * \brief partitioned_block_store structure
+ *
+ * A partitioned_block_store is a stackable block_store that enables an underlying
+ * block store to be presented as a set of independent storage partitions, each identified
+ * by its own GUID. Storage partition attributes will have been defined by platform
+ * configuration data e.g. read from a GPT. The method for obtaining partition configuration
+ * is outside of the scope of the partitioned_block_store.
+ */
+struct partitioned_block_store
+{
+ struct block_store base_block_store;
+ uint32_t local_client_id;
+ size_t num_partitions;
+ struct storage_partition storage_partition[PARTITIONED_BLOCK_STORE_MAX_PARTITIONS];
+ storage_partition_handle_t back_store_handle;
+ struct block_store *back_store;
+ size_t back_store_block_size;
+ size_t back_store_num_blocks;
+};
+
+/**
+ * \brief Initialize a partitioned_block_store
+ *
+ * After initialization, no storage partitions are defined. Partitions must be added,
+ * one a time, by calling partitioned_block_store_add_partition.
+ *
+ * \param[in] partitioned_block_store The subject partitioned_block_store
+ * \param[in] local_client_id Client ID corresponding to the current environment
+ * \param[in] back_store_guid The partition GUID to use in the underlying back store
+ * \param[in] back_store The associated back store
+ *
+ * \return Pointer to block_store or NULL on failure
+ */
+struct block_store *partitioned_block_store_init(
+ struct partitioned_block_store *partitioned_block_store,
+ uint32_t local_client_id,
+ const struct uuid_octets *back_store_guid,
+ struct block_store *back_store);
+
+/**
+ * \brief De-initialize a partitioned_block_store
+ *
+ * Frees resource allocated during call to partitioned_block_store_init().
+ *
+ * \param[in] partitioned_block_store The subject partitioned_block_store
+ */
+void partitioned_block_store_deinit(
+ struct partitioned_block_store *partitioned_block_store);
+
+/**
+ * \brief Add a storage partition
+ *
+ * Called for each partition defined in platform configuration. Parameters
+ * correspond to partition parameters defined in the UEFI specification for
+ * GUID Partition Tables (GPT).
+ *
+ * \param[in] partitioned_block_store The subject partitioned_block_store
+ * \param[in] partition_guid The unique partition GUID
+ * \param[in] starting_lba The back store lba that corresponds to the partition start
+ * \param[in] ending_lba The back store lba that corresponds to the final block in the partition
+ * \param[in] attributes Partition attributes bitmap
+ * \param[in] owner Partition owner ID string
+ *
+ * \return True if successful
+ */
+bool partitioned_block_store_add_partition(
+ struct partitioned_block_store *partitioned_block_store,
+ const struct uuid_octets *partition_guid,
+ uint32_t starting_lba,
+ uint32_t ending_lba,
+ uint64_t attributes,
+ const char *owner);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PARTITIONED_BLOCK_STORE_H */
diff --git a/components/service/block_storage/block_store/partitioned/test/component.cmake b/components/service/block_storage/block_store/partitioned/test/component.cmake
new file mode 100644
index 0000000..7e55d6f
--- /dev/null
+++ b/components/service/block_storage/block_store/partitioned/test/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# 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}/partitioned_block_store_tests.cpp"
+ )
diff --git a/components/service/block_storage/block_store/partitioned/test/partitioned_block_store_tests.cpp b/components/service/block_storage/block_store/partitioned/test/partitioned_block_store_tests.cpp
new file mode 100644
index 0000000..3bafb1b
--- /dev/null
+++ b/components/service/block_storage/block_store/partitioned/test/partitioned_block_store_tests.cpp
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cstring>
+#include "common/uuid/uuid.h"
+#include "service/block_storage/block_store/device/ram/ram_block_store.h"
+#include "service/block_storage/block_store/partitioned/partitioned_block_store.h"
+#include "CppUTest/TestHarness.h"
+
+TEST_GROUP(PartitionedBlockStoreTests)
+{
+ void setup()
+ {
+ /* Initialize a ram_block_store to use as the back store */
+ struct uuid_octets back_store_guid;
+ memset(&back_store_guid, 0, sizeof(back_store_guid));
+
+ struct block_store *back_store = ram_block_store_init(
+ &m_ram_store,
+ &back_store_guid,
+ BACK_STORE_NUM_BLOCKS,
+ BACK_STORE_BLOCK_SIZE);
+
+ CHECK_TRUE(back_store);
+
+ /* Stack a partitioned_block_store over the back store */
+ m_block_store = partitioned_block_store_init(
+ &m_partitioned_store,
+ LOCAL_CLIENT_ID,
+ &back_store_guid,
+ back_store);
+
+ CHECK_TRUE(back_store);
+
+ /* Configure partition 1 */
+ uuid_parse_to_octets("18ae7d62-0974-4dd3-8d2e-e9c166554165",
+ m_partition_1_guid.octets, sizeof(m_partition_1_guid.octets));
+
+ CHECK_TRUE(partitioned_block_store_add_partition(
+ &m_partitioned_store,
+ &m_partition_1_guid,
+ PARTITION_1_STARTING_LBA,
+ PARTITION_1_ENDING_LBA,
+ 0, NULL));
+
+ /* Configure partition 2 */
+ uuid_parse_to_octets("7924fda7-4666-41d7-887a-91a913712b93",
+ m_partition_2_guid.octets, sizeof(m_partition_2_guid.octets));
+
+ CHECK_TRUE(partitioned_block_store_add_partition(
+ &m_partitioned_store,
+ &m_partition_2_guid,
+ PARTITION_2_STARTING_LBA,
+ PARTITION_2_ENDING_LBA,
+ 0, NULL));
+ }
+
+ void teardown()
+ {
+ ram_block_store_deinit(&m_ram_store);
+ partitioned_block_store_deinit(&m_partitioned_store);
+ }
+
+ /* Back store configuration */
+ static const size_t BACK_STORE_NUM_BLOCKS = 100;
+ static const size_t BACK_STORE_BLOCK_SIZE = 512;
+
+ /* Partition 1 configuration */
+ static const size_t PARTITION_1_STARTING_LBA = 5;
+ static const size_t PARTITION_1_ENDING_LBA = 49;
+
+ /* Partition 2 configuration */
+ static const size_t PARTITION_2_STARTING_LBA = 60;
+ static const size_t PARTITION_2_ENDING_LBA = 99;
+
+ static const uint32_t LOCAL_CLIENT_ID = 11;
+
+ struct block_store *m_block_store;
+ struct ram_block_store m_ram_store;
+ struct partitioned_block_store m_partitioned_store;
+ struct uuid_octets m_partition_1_guid;
+ struct uuid_octets m_partition_2_guid;
+};
+
+TEST(PartitionedBlockStoreTests, getPartitionInfo)
+{
+ struct storage_partition_info info;
+
+ /* Check partition info for partition 1 */
+ psa_status_t status = block_store_get_partition_info(
+ m_block_store, &m_partition_1_guid, &info);
+
+ LONGS_EQUAL(PSA_SUCCESS, status);
+ LONGS_EQUAL(PARTITION_1_ENDING_LBA - PARTITION_1_STARTING_LBA + 1, info.num_blocks);
+ LONGS_EQUAL(BACK_STORE_BLOCK_SIZE, info.block_size);
+
+ /* Check partition info for partition 2 */
+ status = block_store_get_partition_info(
+ m_block_store, &m_partition_2_guid, &info);
+
+ LONGS_EQUAL(PSA_SUCCESS, status);
+ LONGS_EQUAL(PARTITION_2_ENDING_LBA - PARTITION_2_STARTING_LBA + 1, info.num_blocks);
+ LONGS_EQUAL(BACK_STORE_BLOCK_SIZE, info.block_size);
+}
+
+TEST(PartitionedBlockStoreTests, openClose)
+{
+ storage_partition_handle_t handle_1;
+ storage_partition_handle_t handle_2;
+
+ psa_status_t status = block_store_open(
+ m_block_store, LOCAL_CLIENT_ID, &m_partition_1_guid, &handle_1);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ status = block_store_open(
+ m_block_store, LOCAL_CLIENT_ID, &m_partition_1_guid, &handle_2);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ status = block_store_close(m_block_store, LOCAL_CLIENT_ID, handle_1);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ status = block_store_close(m_block_store, LOCAL_CLIENT_ID, handle_2);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+}
+
+TEST(PartitionedBlockStoreTests, writeReadEraseBlock)
+{
+ storage_partition_handle_t handle_1;
+ storage_partition_handle_t handle_2;
+ uint8_t write_buffer[BACK_STORE_BLOCK_SIZE];
+ uint8_t read_buffer[BACK_STORE_BLOCK_SIZE];
+ size_t data_len = 0;
+ size_t num_written = 0;
+ uint32_t lba_1 = 10;
+ uint32_t lba_2 = 7;
+
+ /* Open sessions associated with both partitions */
+ psa_status_t status = block_store_open(
+ m_block_store, LOCAL_CLIENT_ID, &m_partition_1_guid, &handle_1);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ status = block_store_open(
+ m_block_store, LOCAL_CLIENT_ID, &m_partition_2_guid, &handle_2);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ /* Write different data to both partitions - expect both to work */
+ memset(write_buffer, 0xaa, sizeof(write_buffer));
+ status = block_store_write(
+ m_block_store, LOCAL_CLIENT_ID, handle_1, lba_1,
+ 0, write_buffer, sizeof(write_buffer), &num_written);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+ UNSIGNED_LONGS_EQUAL(sizeof(write_buffer), num_written);
+
+ memset(write_buffer, 0xbb, sizeof(write_buffer));
+ status = block_store_write(
+ m_block_store, LOCAL_CLIENT_ID, handle_2, lba_2,
+ 0, write_buffer, sizeof(write_buffer), &num_written);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+ UNSIGNED_LONGS_EQUAL(sizeof(write_buffer), num_written);
+
+ /* Expect to read back the same data from both partitions*/
+ memset(write_buffer, 0xaa, sizeof(write_buffer));
+ memset(read_buffer, 0, sizeof(read_buffer));
+ status = block_store_read(
+ m_block_store, LOCAL_CLIENT_ID, handle_1, lba_1, 0,
+ sizeof(read_buffer), read_buffer, &data_len);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+ UNSIGNED_LONGS_EQUAL(sizeof(write_buffer), data_len);
+ MEMCMP_EQUAL(write_buffer, read_buffer, sizeof(write_buffer));
+
+ memset(write_buffer, 0xbb, sizeof(write_buffer));
+ memset(read_buffer, 0, sizeof(read_buffer));
+ status = block_store_read(
+ m_block_store, LOCAL_CLIENT_ID, handle_2, lba_2,
+ 0, sizeof(read_buffer), read_buffer, &data_len);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+ UNSIGNED_LONGS_EQUAL(sizeof(write_buffer), data_len);
+ MEMCMP_EQUAL(write_buffer, read_buffer, sizeof(write_buffer));
+
+ /* Erase the block in the partition 1 */
+ status = block_store_erase(m_block_store, LOCAL_CLIENT_ID, handle_1, lba_1, 1);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ /* Write to an erased block in partition 1 should work as block was erase */
+ status = block_store_write(
+ m_block_store, LOCAL_CLIENT_ID, handle_1, lba_1,
+ 0, write_buffer, sizeof(write_buffer), &num_written);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+ UNSIGNED_LONGS_EQUAL(sizeof(write_buffer), num_written);
+
+ /* Write to the previously written block in partition 2 should fail as it hasn't been erased */
+ status = block_store_write(
+ m_block_store, LOCAL_CLIENT_ID, handle_2, lba_2,
+ 0, write_buffer, sizeof(write_buffer), &num_written);
+ LONGS_EQUAL(PSA_ERROR_STORAGE_FAILURE, status);
+
+ /* Erase the block in the partition 2 */
+ status = block_store_erase(m_block_store, LOCAL_CLIENT_ID, handle_2, lba_2, 1);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ /* Now the write to partition 2 should work */
+ status = block_store_write(
+ m_block_store, LOCAL_CLIENT_ID, handle_2, lba_2,
+ 0, write_buffer, sizeof(write_buffer), &num_written);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+ UNSIGNED_LONGS_EQUAL(sizeof(write_buffer), num_written);
+
+ /* Expect to successfully close both sessions */
+ status = block_store_close(m_block_store, LOCAL_CLIENT_ID, handle_1);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ status = block_store_close(m_block_store, LOCAL_CLIENT_ID, handle_2);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+}
+
+TEST(PartitionedBlockStoreTests, checkEraseLimits)
+{
+ storage_partition_handle_t handle_1;
+ storage_partition_handle_t handle_2;
+ uint8_t write_buffer[BACK_STORE_BLOCK_SIZE];
+ uint8_t read_buffer[BACK_STORE_BLOCK_SIZE];
+ size_t data_len = 0;
+ size_t num_written = 0;
+
+ /* Open sessions associated with both partitions */
+ psa_status_t status = block_store_open(
+ m_block_store, LOCAL_CLIENT_ID, &m_partition_1_guid, &handle_1);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ status = block_store_open(
+ m_block_store, LOCAL_CLIENT_ID, &m_partition_2_guid, &handle_2);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ /* Write same data to the beginning of both partitions */
+ memset(write_buffer, 0xaa, sizeof(write_buffer));
+ status = block_store_write(
+ m_block_store, LOCAL_CLIENT_ID, handle_1, 0,
+ 0, write_buffer, sizeof(write_buffer), &num_written);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+ UNSIGNED_LONGS_EQUAL(sizeof(write_buffer), num_written);
+
+ status = block_store_write(
+ m_block_store, LOCAL_CLIENT_ID, handle_2, 0,
+ 0, write_buffer, sizeof(write_buffer), &num_written);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+ UNSIGNED_LONGS_EQUAL(sizeof(write_buffer), num_written);
+
+ /* Expect to read back the same data from both partitions*/
+ memset(read_buffer, 0, sizeof(read_buffer));
+ status = block_store_read(
+ m_block_store, LOCAL_CLIENT_ID, handle_1, 0, 0,
+ sizeof(read_buffer), read_buffer, &data_len);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+ UNSIGNED_LONGS_EQUAL(sizeof(write_buffer), data_len);
+ MEMCMP_EQUAL(write_buffer, read_buffer, sizeof(write_buffer));
+
+ memset(read_buffer, 0, sizeof(read_buffer));
+ status = block_store_read(
+ m_block_store, LOCAL_CLIENT_ID, handle_2, 0,
+ 0, sizeof(read_buffer), read_buffer, &data_len);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+ UNSIGNED_LONGS_EQUAL(sizeof(write_buffer), data_len);
+ MEMCMP_EQUAL(write_buffer, read_buffer, sizeof(write_buffer));
+
+ /* Erase partition 1 with a block count that exceeds the limits of the partition */
+ status = block_store_erase(m_block_store, LOCAL_CLIENT_ID, handle_1, 0, 60);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ /* Still expect data in partition 2 to be intact */
+ memset(read_buffer, 0, sizeof(read_buffer));
+ status = block_store_read(
+ m_block_store, LOCAL_CLIENT_ID, handle_2, 0,
+ 0, sizeof(read_buffer), read_buffer, &data_len);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+ UNSIGNED_LONGS_EQUAL(sizeof(write_buffer), data_len);
+ MEMCMP_EQUAL(write_buffer, read_buffer, sizeof(write_buffer));
+
+ /* Expect to successfully close both sessions */
+ status = block_store_close(m_block_store, LOCAL_CLIENT_ID, handle_1);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ status = block_store_close(m_block_store, LOCAL_CLIENT_ID, handle_2);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+}
+
+
+TEST(PartitionedBlockStoreTests, writeToInvalidBlock)
+{
+ storage_partition_handle_t handle_1;
+ storage_partition_handle_t handle_2;
+ uint8_t write_buffer[BACK_STORE_BLOCK_SIZE];
+ size_t num_written = 0;
+ struct storage_partition_info info_1;
+ struct storage_partition_info info_2;
+
+ /* Choose LBAs that lie outside of limits of partitions */
+ psa_status_t status = block_store_get_partition_info(
+ m_block_store, &m_partition_1_guid, &info_1);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ status = block_store_get_partition_info(
+ m_block_store, &m_partition_2_guid, &info_2);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ uint32_t lba_1 = info_1.num_blocks;
+ uint32_t lba_2 = info_2.num_blocks + 1000;
+
+ status = block_store_open(
+ m_block_store, LOCAL_CLIENT_ID, &m_partition_1_guid, &handle_1);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ status = block_store_open(
+ m_block_store, LOCAL_CLIENT_ID, &m_partition_2_guid, &handle_2);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ /* Write to block beyond the limits of the storage partition should fail */
+ memset(write_buffer, 0xaa, sizeof(write_buffer));
+ status = block_store_write(
+ m_block_store, LOCAL_CLIENT_ID, handle_1, lba_1,
+ 0, write_buffer, sizeof(write_buffer), &num_written);
+ LONGS_EQUAL(PSA_ERROR_INVALID_ARGUMENT, status);
+
+ status = block_store_write(
+ m_block_store, LOCAL_CLIENT_ID, handle_2, lba_2,
+ 0, write_buffer, sizeof(write_buffer), &num_written);
+ LONGS_EQUAL(PSA_ERROR_INVALID_ARGUMENT, status);
+
+ /* Close the sessions */
+ status = block_store_close(m_block_store, LOCAL_CLIENT_ID, handle_1);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+
+ status = block_store_close(m_block_store, LOCAL_CLIENT_ID, handle_2);
+ LONGS_EQUAL(PSA_SUCCESS, status);
+}
diff --git a/deployments/component-test/component-test.cmake b/deployments/component-test/component-test.cmake
index ca2985e..c1b0317 100644
--- a/deployments/component-test/component-test.cmake
+++ b/deployments/component-test/component-test.cmake
@@ -80,6 +80,8 @@
"components/service/block_storage/block_store/device"
"components/service/block_storage/block_store/device/ram"
"components/service/block_storage/block_store/device/ram/test"
+ "components/service/block_storage/block_store/partitioned"
+ "components/service/block_storage/block_store/partitioned/test"
"components/service/crypto/client/cpp"
"components/service/crypto/client/cpp/protocol/protobuf"
"components/service/crypto/client/cpp/protocol/packed-c"