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"