Add ram_block_store component

The ram_block_store is a block_store implementation intended for use
in test integrations. Rather than use real NV storage, it uses an
area of normal memory for storing blocks. Test cases added that
exercise the ram_block_store using the base block_store interface.

Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: If9c2ad287fddedf6a5b7f8c9a5c70a7111978b3a
diff --git a/components/service/block_storage/block_store/device/block_device.c b/components/service/block_storage/block_store/device/block_device.c
new file mode 100644
index 0000000..b3e74b1
--- /dev/null
+++ b/components/service/block_storage/block_store/device/block_device.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include "block_device.h"
+
+#define BLOCK_DEVICE_PARTITION_HANDLE    (0)
+
+psa_status_t block_device_check_access_permitted(
+	const struct block_device *block_device,
+	uint32_t client_id,
+	storage_partition_handle_t handle)
+{
+	if (handle != BLOCK_DEVICE_PARTITION_HANDLE)
+		return PSA_ERROR_INVALID_ARGUMENT;
+
+	if (!storage_partition_is_access_permitted(&block_device->storage_partition, client_id))
+		return PSA_ERROR_NOT_PERMITTED;
+
+	return PSA_SUCCESS;
+}
+
+psa_status_t block_device_get_partition_info(
+	struct block_device *block_device,
+	const struct uuid_octets *partition_guid,
+	struct storage_partition_info *info)
+{
+	psa_status_t status = PSA_ERROR_INVALID_ARGUMENT;
+
+	if (storage_partition_is_guid_matched(&block_device->storage_partition, partition_guid)) {
+
+		info->block_size = block_device->storage_partition.block_size;
+		info->num_blocks = block_device->storage_partition.num_blocks;
+		status = PSA_SUCCESS;
+	}
+
+	return status;
+}
+
+psa_status_t block_device_open(
+	struct block_device *block_device,
+	uint32_t client_id,
+	const struct uuid_octets *partition_guid,
+	storage_partition_handle_t *handle)
+{
+	psa_status_t status = PSA_ERROR_NOT_PERMITTED;
+
+	if (storage_partition_is_guid_matched(&block_device->storage_partition, partition_guid) &&
+		storage_partition_is_access_permitted(&block_device->storage_partition, client_id)) {
+
+		*handle = BLOCK_DEVICE_PARTITION_HANDLE;
+		status = PSA_SUCCESS;
+	}
+
+	return status;
+}
+
+psa_status_t block_device_close(
+	struct block_device *block_device,
+	uint32_t client_id,
+	storage_partition_handle_t handle)
+{
+	psa_status_t status = block_device_check_access_permitted(block_device, client_id, handle);
+
+	return status;
+}
+
+struct block_store *block_device_init(
+	struct block_device *block_device,
+	const struct uuid_octets *disk_guid,
+	size_t num_blocks,
+	size_t block_size)
+{
+	/* block_device exposes a single storage partition that represents the
+	 * underlying storage device */
+	storage_partition_init(
+		&block_device->storage_partition,
+		disk_guid, num_blocks, block_size);
+
+	return &block_device->base_block_store;
+}
+
+void block_device_deinit(
+	struct block_device *block_device)
+{
+	storage_partition_deinit(
+		&block_device->storage_partition);
+}
diff --git a/components/service/block_storage/block_store/device/block_device.h b/components/service/block_storage/block_store/device/block_device.h
new file mode 100644
index 0000000..ee1203c
--- /dev/null
+++ b/components/service/block_storage/block_store/device/block_device.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef BLOCK_DEVICE_H
+#define BLOCK_DEVICE_H
+
+#include "service/block_storage/block_store/block_store.h"
+#include "service/block_storage/block_store/storage_partition.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief block_device structure
+ *
+ * A block_device is a block_store that provides block storage in some way. A
+ * block_device specializes the base block_store to provide a common base for
+ * block storage devices such as flash drivers. Because any block_device realizes
+ * the common block_store interface, it may be used in any situation where the
+ * upper layer uses the block_store interface.  A block_device exposes a single
+ * storage partition that represents the physical storage presented by
+ * the block_device. The block size and number of blocks will have been discovered
+ * in some way or will have been provided via configuration data.
+ */
+struct block_device
+{
+	struct block_store base_block_store;
+	struct storage_partition storage_partition;
+};
+
+/**
+ * \brief Initialize a block_device
+ *
+ * \param[in]  block_device     The subject block_device
+ * \param[in]  disk_guid        The disk GUID (can be nil)
+ * \param[in]  num_blocks       The number of contiguous blocks
+ * \param[in]  block_size       Block size in bytes
+ *
+ * \return Pointer to the base block_store or NULL on failure
+ */
+struct block_store *block_device_init(
+	struct block_device *block_device,
+	const struct uuid_octets *disk_guid,
+	size_t num_blocks,
+	size_t block_size);
+
+/**
+ * \brief De-initialize a block_device
+ *
+ * \param[in]  block_device  The subject block_device
+ */
+void block_device_deinit(
+	struct block_device *block_device);
+
+/**
+ * \brief Check if access is permitted
+ *
+ *  Called by concrete block devices to check if a client is permitted access.
+ *
+ * \param[in]  block_device  The subject block_device
+ * \param[in]  client_id     The requesting client ID
+ * \param[in]  handle        The handle presented by the client
+ *
+ * \return PSA_SUCCESS if access permitted
+ */
+psa_status_t block_device_check_access_permitted(
+	const struct block_device *block_device,
+	uint32_t client_id,
+	storage_partition_handle_t handle);
+
+/**
+ * \brief Get information about the block device
+ *
+ *  Called by concrete block devices to get information about the storage devices
+ *
+ * \param[in]  block_device    The subject block_device
+ * \param[in]  partition_guid  The requested partition GUID
+ * \param[out] info            The retrieved information about the storage device
+ *
+ * \return PSA_SUCCESS on success
+ */
+psa_status_t block_device_get_partition_info(
+	struct block_device *block_device,
+	const struct uuid_octets *partition_guid,
+	struct storage_partition_info *info);
+
+/**
+ * \brief Open the block device
+ *
+ *  Called by concrete block devices to obtain a handle to use the block device
+ *
+ * \param[in]  block_device    The subject block_device
+ * \param[in]  client_id       The requesting client ID
+ * \param[in]  partition_guid  The requested partition GUID
+ * \param[out] handle          The handle to use for device access operations
+ *
+ * \return PSA_SUCCESS on success
+ */
+psa_status_t block_device_open(
+	struct block_device *block_device,
+	uint32_t client_id,
+	const struct uuid_octets *partition_guid,
+	storage_partition_handle_t *handle);
+
+/**
+ * \brief Close the block device
+ *
+ * \param[in]  block_device    The subject block_device
+ * \param[in]  client_id       The requesting client ID
+ * \param[in]  handle          The handle obtained on open
+ *
+ * \return PSA_SUCCESS on success
+ */
+psa_status_t block_device_close(
+	struct block_device *block_device,
+	uint32_t client_id,
+	storage_partition_handle_t handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BLOCK_DEVICE_H */
diff --git a/components/service/block_storage/block_store/device/component.cmake b/components/service/block_storage/block_store/device/component.cmake
new file mode 100644
index 0000000..3397458
--- /dev/null
+++ b/components/service/block_storage/block_store/device/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}/block_device.c"
+	)
diff --git a/components/service/block_storage/block_store/device/ram/component.cmake b/components/service/block_storage/block_store/device/ram/component.cmake
new file mode 100644
index 0000000..ed04435
--- /dev/null
+++ b/components/service/block_storage/block_store/device/ram/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}/ram_block_store.c"
+	)
diff --git a/components/service/block_storage/block_store/device/ram/ram_block_store.c b/components/service/block_storage/block_store/device/ram/ram_block_store.c
new file mode 100644
index 0000000..65c56b4
--- /dev/null
+++ b/components/service/block_storage/block_store/device/ram/ram_block_store.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ram_block_store.h"
+
+
+#define RAM_BLOCK_STORE_ERASED_VALUE	    (0xff)
+
+static bool is_block_erased(const struct ram_block_store *ram_block_store,
+	uint32_t lba,
+	size_t offset,
+	size_t len)
+{
+	bool is_erased = true;
+	size_t block_start_index =
+		ram_block_store->base_block_device.storage_partition.block_size * lba;
+	size_t block_end_index =
+		block_start_index + ram_block_store->base_block_device.storage_partition.block_size;
+
+	size_t index = block_start_index + offset;
+	size_t end_index = (index + len < block_end_index) ?
+		index + len :
+		block_end_index;
+
+	while (index < end_index) {
+
+		if (ram_block_store->ram_back_store[index] != RAM_BLOCK_STORE_ERASED_VALUE) {
+
+			is_erased = false;
+			break;
+		}
+
+		++index;
+	}
+
+	return is_erased;
+}
+
+static psa_status_t ram_block_store_get_partition_info(void *context,
+	const struct uuid_octets *partition_guid,
+	struct storage_partition_info *info)
+{
+	struct ram_block_store *ram_block_store = (struct ram_block_store*)context;
+	return block_device_get_partition_info(
+		&ram_block_store->base_block_device, partition_guid, info);
+}
+
+static psa_status_t ram_block_store_open(void *context,
+	uint32_t client_id,
+	const struct uuid_octets *partition_guid,
+	storage_partition_handle_t *handle)
+{
+	struct ram_block_store *ram_block_store = (struct ram_block_store*)context;
+	return block_device_open(
+		&ram_block_store->base_block_device, client_id, partition_guid, handle);
+}
+
+static psa_status_t ram_block_store_close(void *context,
+	uint32_t client_id,
+	storage_partition_handle_t handle)
+{
+	struct ram_block_store *ram_block_store = (struct ram_block_store*)context;
+	return block_device_close(
+		&ram_block_store->base_block_device, client_id, handle);
+}
+
+static psa_status_t ram_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)
+{
+	struct ram_block_store *ram_block_store = (struct ram_block_store*)context;
+	psa_status_t status = block_device_check_access_permitted(
+		&ram_block_store->base_block_device, client_id, handle);
+
+	if (status == PSA_SUCCESS) {
+
+		const struct storage_partition *storage_partition =
+			&ram_block_store->base_block_device.storage_partition;
+
+		if (storage_partition_is_lba_legal(storage_partition, lba) &&
+			(offset < storage_partition->block_size)) {
+
+			size_t bytes_remaining = storage_partition->block_size - offset;
+			size_t bytes_to_read = (buffer_size < bytes_remaining) ?
+				buffer_size :
+				bytes_remaining;
+
+			const uint8_t *block_start =
+				&ram_block_store->ram_back_store[lba * storage_partition->block_size];
+
+			memcpy(buffer, &block_start[offset], bytes_to_read);
+			*data_len = bytes_to_read;
+		}
+		else {
+
+			status = PSA_ERROR_INVALID_ARGUMENT;
+		}
+	}
+
+	return status;
+}
+
+static psa_status_t ram_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)
+{
+	struct ram_block_store *ram_block_store = (struct ram_block_store*)context;
+	psa_status_t status = block_device_check_access_permitted(
+		&ram_block_store->base_block_device, client_id, handle);
+
+	if (status == PSA_SUCCESS) {
+
+		const struct storage_partition *storage_partition =
+			&ram_block_store->base_block_device.storage_partition;
+
+		if (storage_partition_is_lba_legal(storage_partition, lba) &&
+			(offset < storage_partition->block_size)) {
+
+			if (!is_block_erased(ram_block_store, lba, offset, data_len))
+				return PSA_ERROR_STORAGE_FAILURE;
+
+			size_t bytes_remaining = storage_partition->block_size - offset;
+			uint8_t *block_start =
+				&ram_block_store->ram_back_store[lba * storage_partition->block_size];
+
+			size_t bytes_to_write = (data_len < bytes_remaining) ?
+				data_len :
+				bytes_remaining;
+
+			memcpy(&block_start[offset], data, bytes_to_write);
+			*num_written = bytes_to_write;
+		}
+		else {
+
+			status = PSA_ERROR_INVALID_ARGUMENT;
+		}
+	}
+
+	return status;
+}
+
+static psa_status_t ram_block_store_erase(void *context,
+	uint32_t client_id,
+	storage_partition_handle_t handle,
+	uint32_t begin_lba,
+	size_t num_blocks)
+{
+	struct ram_block_store *ram_block_store = (struct ram_block_store*)context;
+	const struct storage_partition *storage_partition =
+		&ram_block_store->base_block_device.storage_partition;
+	psa_status_t status = block_device_check_access_permitted(
+		&ram_block_store->base_block_device, client_id, handle);
+
+	/* Sanitize the range of LBAs to erase */
+	if ((status == PSA_SUCCESS) &&
+		!storage_partition_is_lba_legal(storage_partition, begin_lba)) {
+
+		status = PSA_ERROR_INVALID_ARGUMENT;
+	}
+
+	if (status == PSA_SUCCESS) {
+
+		size_t blocks_to_erase = storage_partition_clip_num_blocks(storage_partition,
+			begin_lba, num_blocks);
+
+		uint8_t *erase_from =
+			&ram_block_store->ram_back_store[begin_lba * storage_partition->block_size];
+		size_t erase_len =
+			blocks_to_erase * storage_partition->block_size;
+
+		memset(erase_from, RAM_BLOCK_STORE_ERASED_VALUE, erase_len);
+	}
+
+	return status;
+}
+
+struct block_store *ram_block_store_init(
+	struct ram_block_store *ram_block_store,
+	const struct uuid_octets *disk_guid,
+	size_t num_blocks,
+	size_t block_size)
+{
+	struct block_store *retval = NULL;
+
+	/* Define concrete block store interface */
+	static const struct block_store_interface interface =
+	{
+		ram_block_store_get_partition_info,
+		ram_block_store_open,
+		ram_block_store_close,
+		ram_block_store_read,
+		ram_block_store_write,
+		ram_block_store_erase
+	};
+
+	/* Publish the public interface */
+	ram_block_store->base_block_device.base_block_store.context = ram_block_store;
+	ram_block_store->base_block_device. base_block_store.interface = &interface;
+
+	/* Allocate storage and set all to the erased state */
+	size_t back_store_size = num_blocks * block_size;
+	ram_block_store->ram_back_store = (uint8_t*)malloc(back_store_size);
+
+	if (ram_block_store->ram_back_store) {
+
+		memset(ram_block_store->ram_back_store, RAM_BLOCK_STORE_ERASED_VALUE, back_store_size);
+
+		retval = block_device_init(
+			&ram_block_store->base_block_device, disk_guid, num_blocks, block_size);
+	}
+
+	return retval;
+}
+
+void ram_block_store_deinit(
+	struct ram_block_store *ram_block_store)
+{
+	free(ram_block_store->ram_back_store);
+
+	block_device_deinit(&ram_block_store->base_block_device);
+}
diff --git a/components/service/block_storage/block_store/device/ram/ram_block_store.h b/components/service/block_storage/block_store/device/ram/ram_block_store.h
new file mode 100644
index 0000000..6c083eb
--- /dev/null
+++ b/components/service/block_storage/block_store/device/ram/ram_block_store.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef RAM_BLOCK_STORE_H
+#define RAM_BLOCK_STORE_H
+
+#include "service/block_storage/block_store/device/block_device.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief ram_block_store structure
+ *
+ * A ram_block_store is a block_device that uses normal memory for block storage.
+ * It presents a single storage partition with the capacity specified at initialization.
+ * The ram_block_store is intended to be used in test integrations where a real
+ * NV block_store is either not available or is not needed.
+ */
+struct ram_block_store
+{
+	struct block_device base_block_device;
+	uint8_t *ram_back_store;
+};
+
+/**
+ * \brief Initialize a ram_block_store
+ *
+ * \param[in]  ram_block_store  The subject ram_block_store
+ * \param[in]  disk_guid   		The disk GUID
+ * \param[in]  num_blocks       The number of contiguous blocks
+ * \param[in]  block_size       Block size in bytes
+ *
+ * \return Pointer to block_store or NULL on failure
+ */
+struct block_store *ram_block_store_init(
+	struct ram_block_store *ram_block_store,
+	const struct uuid_octets *disk_guid,
+	size_t num_blocks,
+	size_t block_size);
+
+/**
+ * \brief De-initialize a ram_block_store
+ *
+ *  Frees resource allocated during call to ram_block_store_init().
+ *
+ * \param[in]  ram_block_store  The subject ram_block_store
+ */
+void ram_block_store_deinit(
+	struct ram_block_store *ram_block_store);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RAM_BLOCK_STORE_H */
diff --git a/components/service/block_storage/block_store/device/ram/test/component.cmake b/components/service/block_storage/block_store/device/ram/test/component.cmake
new file mode 100644
index 0000000..9fa7a6c
--- /dev/null
+++ b/components/service/block_storage/block_store/device/ram/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}/ram_block_store_tests.cpp"
+	)
diff --git a/components/service/block_storage/block_store/device/ram/test/ram_block_store_tests.cpp b/components/service/block_storage/block_store/device/ram/test/ram_block_store_tests.cpp
new file mode 100644
index 0000000..dd3e4fc
--- /dev/null
+++ b/components/service/block_storage/block_store/device/ram/test/ram_block_store_tests.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cstring>
+#include <stdint.h>
+#include "common/uuid/uuid.h"
+#include "service/block_storage/block_store/device/ram/ram_block_store.h"
+#include "CppUTest/TestHarness.h"
+
+TEST_GROUP(RamBlockStoreTests)
+{
+	void setup()
+	{
+		uuid_parse_to_octets("6152f22b-8128-4c1f-981f-3bd279519907",
+			m_partition_guid.octets, sizeof(m_partition_guid.octets));
+
+		m_block_store = ram_block_store_init(&m_ram_block_store,
+			&m_partition_guid, NUM_BLOCKS, BLOCK_SIZE);
+
+		CHECK_TRUE(m_block_store);
+	}
+
+	void teardown()
+	{
+		ram_block_store_deinit(&m_ram_block_store);
+	}
+
+	static const size_t NUM_BLOCKS = 100;
+	static const size_t BLOCK_SIZE = 512;
+	static const uint32_t CLIENT_ID = 27;
+
+	struct uuid_octets m_partition_guid;
+	struct block_store *m_block_store;
+	struct ram_block_store m_ram_block_store;
+};
+
+TEST(RamBlockStoreTests, getPartitionInfo)
+{
+	struct storage_partition_info info;
+
+	psa_status_t status =
+		block_store_get_partition_info(m_block_store, &m_partition_guid, &info);
+
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	LONGS_EQUAL(NUM_BLOCKS, info.num_blocks);
+	LONGS_EQUAL(BLOCK_SIZE, info.block_size);
+}
+
+TEST(RamBlockStoreTests, openClose)
+{
+	storage_partition_handle_t handle;
+
+	psa_status_t status =
+		block_store_open(m_block_store, CLIENT_ID, &m_partition_guid, &handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	status = block_store_close(m_block_store, CLIENT_ID, handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+}
+
+TEST(RamBlockStoreTests, writeReadEraseBlock)
+{
+	storage_partition_handle_t handle;
+	uint8_t write_buffer[BLOCK_SIZE];
+	uint8_t read_buffer[BLOCK_SIZE];
+	size_t data_len = 0;
+	size_t num_written = 0;
+	uint32_t lba = 10;
+
+	psa_status_t status =
+		block_store_open(m_block_store, CLIENT_ID, &m_partition_guid, &handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Expect to be able to write to a block as all blocks are assumed to
+	 * be in the erased state */
+	memset(write_buffer, 0xaa, BLOCK_SIZE);
+	status = block_store_write(m_block_store, CLIENT_ID, handle, lba,
+		0, write_buffer, BLOCK_SIZE, &num_written);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE, num_written);
+
+	/* Expect to read back the same data */
+	memset(read_buffer, 0, BLOCK_SIZE);
+	status = block_store_read(m_block_store, CLIENT_ID, handle, lba,
+		0, BLOCK_SIZE, read_buffer, &data_len);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE, data_len);
+	MEMCMP_EQUAL(write_buffer, read_buffer, BLOCK_SIZE);
+
+	/* Attempt to write the same block again without an erase. Expect this to fail. */
+	memset(write_buffer, 0xbb, BLOCK_SIZE);
+	status = block_store_write(m_block_store, CLIENT_ID, handle, lba,
+		0, write_buffer, BLOCK_SIZE, &num_written);
+	LONGS_EQUAL(PSA_ERROR_STORAGE_FAILURE, status);
+
+	/* Erase the block */
+	status = block_store_erase(m_block_store, CLIENT_ID, handle, lba, 1);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Write to an erased block should now work */
+	status = block_store_write(m_block_store, CLIENT_ID, handle, lba,
+		0, write_buffer, BLOCK_SIZE, &num_written);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE, num_written);
+
+	status = block_store_close(m_block_store, CLIENT_ID, handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+}
+
+TEST(RamBlockStoreTests, writeBiggerThanBlock)
+{
+	storage_partition_handle_t handle;
+	uint8_t write_buffer[BLOCK_SIZE + BLOCK_SIZE / 2];
+	uint8_t read_buffer[BLOCK_SIZE];
+	size_t data_len = 0;
+	size_t num_written = 0;
+	uint32_t lba = 10;
+
+	psa_status_t status =
+		block_store_open(m_block_store, CLIENT_ID, &m_partition_guid, &handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Try writing more than the block size. Expect the operation to succeed but
+	 * that the written data is limited to the block size*/
+	memset(write_buffer, 0xaa, sizeof(write_buffer));
+	status = block_store_write(m_block_store, CLIENT_ID, handle, lba,
+		0, write_buffer, sizeof(write_buffer), &num_written);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE, num_written);
+
+	/* Expect to read back the same data upto the end of a block */
+	memset(read_buffer, 0, BLOCK_SIZE);
+	status = block_store_read(m_block_store, CLIENT_ID, handle, lba,
+		0, BLOCK_SIZE, read_buffer, &data_len);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+	UNSIGNED_LONGS_EQUAL(BLOCK_SIZE, data_len);
+	MEMCMP_EQUAL(write_buffer, read_buffer, BLOCK_SIZE);
+
+	status = block_store_close(m_block_store, CLIENT_ID, handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+}
+
+TEST(RamBlockStoreTests, writeToInvalidBlock)
+{
+	storage_partition_handle_t handle;
+	uint8_t write_buffer[BLOCK_SIZE];
+	size_t num_written = 0;
+	uint32_t lba = NUM_BLOCKS + 7;
+
+	psa_status_t status =
+		block_store_open(m_block_store, CLIENT_ID, &m_partition_guid, &handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Write to block beyond the limits of the storage partition should fail */
+	memset(write_buffer, 0xaa, BLOCK_SIZE);
+	status = block_store_write(m_block_store, CLIENT_ID, handle, lba,
+		0, write_buffer, BLOCK_SIZE, &num_written);
+	LONGS_EQUAL(PSA_ERROR_INVALID_ARGUMENT, status);
+
+	status = block_store_close(m_block_store, CLIENT_ID, handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+}
+
+TEST(RamBlockStoreTests, eraseOperations)
+{
+	storage_partition_handle_t handle;
+
+	psa_status_t status =
+		block_store_open(m_block_store, CLIENT_ID, &m_partition_guid, &handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Erase all blocks */
+	status = block_store_erase(m_block_store, CLIENT_ID, handle, 0, NUM_BLOCKS);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Erase all blocks using a length that exceeds the partition limit - should clip */
+	status = block_store_erase(m_block_store, CLIENT_ID, handle, 0, UINT32_MAX);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+
+	/* Begin LBA is outside of partition */
+	status = block_store_erase(m_block_store, CLIENT_ID, handle, NUM_BLOCKS + 1, NUM_BLOCKS);
+	LONGS_EQUAL(PSA_ERROR_INVALID_ARGUMENT, status);
+
+	status = block_store_close(m_block_store, CLIENT_ID, handle);
+	LONGS_EQUAL(PSA_SUCCESS, status);
+}
\ No newline at end of file
diff --git a/deployments/component-test/component-test.cmake b/deployments/component-test/component-test.cmake
index c742e6b..ca2985e 100644
--- a/deployments/component-test/component-test.cmake
+++ b/deployments/component-test/component-test.cmake
@@ -76,6 +76,10 @@
 		"components/service/attestation/client/provision"
 		"components/service/attestation/test/component"
 		"components/service/attestation/test/service"
+		"components/service/block_storage/block_store"
+		"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/crypto/client/cpp"
 		"components/service/crypto/client/cpp/protocol/protobuf"
 		"components/service/crypto/client/cpp/protocol/packed-c"