Add semihosting block store

To support test, a block store that uses semihosting file IO
facilities available on virtual platforms and real platforms
with a debugger is added. This uses a file residing in the
host file system for storage. Uses the semihosting component
from tf-a. tf-a is fetched as an external component and the
semihosting code is treated as a platform driver. The platform
definition file defines whether semihosting is avaiable for the
selected platform.

Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: I38f9a13e49676572306158963c14f7b62b30dd1a
diff --git a/components/service/block_storage/block_store/device/block_device.c b/components/service/block_storage/block_store/device/block_device.c
index f45215a..dd3abd0 100644
--- a/components/service/block_storage/block_store/device/block_device.c
+++ b/components/service/block_storage/block_store/device/block_device.c
@@ -91,3 +91,14 @@
 	storage_partition_deinit(
 		&block_device->storage_partition);
 }
+
+void block_device_configure(
+	struct block_device *block_device,
+	const struct uuid_octets *disk_guid,
+	size_t num_blocks,
+	size_t block_size)
+{
+	storage_partition_init(
+		&block_device->storage_partition,
+		disk_guid, num_blocks, block_size);
+}
\ No newline at end of file
diff --git a/components/service/block_storage/block_store/device/block_device.h b/components/service/block_storage/block_store/device/block_device.h
index ee1203c..f179c72 100644
--- a/components/service/block_storage/block_store/device/block_device.h
+++ b/components/service/block_storage/block_store/device/block_device.h
@@ -36,8 +36,12 @@
 /**
  * \brief Initialize a block_device
  *
- * \param[in]  block_device     The subject block_device
- * \param[in]  disk_guid        The disk GUID (can be nil)
+ * If configuration parameters are known at initialisation, they may be
+ * provided to configure the block device. If not, pass zero values for
+ * unknown configuration parameters.
+ *
+ * \param[in]  block_device  	The subject block_device
+ * \param[in]  disk_guid   		The disk GUID (can be NULL)
  * \param[in]  num_blocks       The number of contiguous blocks
  * \param[in]  block_size       Block size in bytes
  *
@@ -58,6 +62,23 @@
 	struct block_device *block_device);
 
 /**
+ * \brief Configure a block_device
+ *
+ * Configure an initialised block_device. Configuration parameters are assumed
+ * to have been sanity checked based on knowledge of the concrete block_device.
+ *
+ * \param[in]  block_device  	The subject block_device
+ * \param[in]  disk_guid   		The disk GUID (can be NULL)
+ * \param[in]  num_blocks       The number of contiguous blocks
+ * \param[in]  block_size       Block size in bytes
+ */
+void block_device_configure(
+	struct block_device *block_device,
+	const struct uuid_octets *disk_guid,
+	size_t num_blocks,
+	size_t block_size);
+
+/**
  * \brief Check if access is permitted
  *
  *  Called by concrete block devices to check if a client is permitted access.
diff --git a/components/service/block_storage/block_store/device/semihosting/component.cmake b/components/service/block_storage/block_store/device/semihosting/component.cmake
new file mode 100644
index 0000000..0514314
--- /dev/null
+++ b/components/service/block_storage/block_store/device/semihosting/component.cmake
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------------
+# 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}/semihosting_block_store.c"
+	)
+
+set_property(TARGET ${TGT} APPEND PROPERTY TS_PLATFORM_DRIVER_DEPENDENCIES
+	"semihosting"
+	)
\ No newline at end of file
diff --git a/components/service/block_storage/block_store/device/semihosting/semihosting_block_store.c b/components/service/block_storage/block_store/device/semihosting/semihosting_block_store.c
new file mode 100644
index 0000000..56c4334
--- /dev/null
+++ b/components/service/block_storage/block_store/device/semihosting/semihosting_block_store.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stddef.h>
+#include <lib/semihosting.h>
+#include "semihosting_block_store.h"
+
+#define ERASED_DATA_VAL		0xff
+
+static psa_status_t seek(long handle, ssize_t pos)
+{
+	/* Appears to return bogus error so ignore error code */
+	semihosting_file_seek(handle, pos);
+	return PSA_SUCCESS;
+}
+
+static psa_status_t prepare_for_read(
+	const struct semihosting_block_store *this_instance,
+	uint32_t lba, size_t offset,
+	size_t requested_read_len,
+	size_t *adjusted_read_len)
+{
+	psa_status_t status = PSA_ERROR_BAD_STATE;
+
+	const struct storage_partition *storage_partition =
+		&this_instance->base_block_device.storage_partition;
+
+	ssize_t read_pos = lba * storage_partition->block_size + offset;
+	ssize_t file_len = semihosting_file_length(this_instance->file_handle);
+
+	if (file_len >= 0) {
+
+		/* File exists so attempt to seek the read position to the requested LBA + offset */
+		if (read_pos <= file_len) {
+
+			size_t bytes_until_end_of_file = (size_t)(file_len - read_pos);
+			size_t bytes_until_end_of_block = storage_partition->block_size - offset;
+
+			size_t read_limit = (bytes_until_end_of_file < bytes_until_end_of_block) ?
+				bytes_until_end_of_file :
+				bytes_until_end_of_block;
+
+			*adjusted_read_len = (requested_read_len < read_limit) ?
+				requested_read_len :
+				read_limit;
+
+			status = seek(this_instance->file_handle, read_pos);
+		}
+		else {
+
+			/* Requested block is beyond the end of the file */
+			status = PSA_ERROR_INVALID_ARGUMENT;
+		}
+	}
+
+	return status;
+}
+
+static psa_status_t write_erased(
+	const struct semihosting_block_store *this_instance,
+	size_t pos,
+	size_t len)
+{
+	psa_status_t status = PSA_ERROR_BAD_STATE;
+	size_t remaining_len = len;
+
+	status = seek(this_instance->file_handle, pos);
+
+	while ((status == PSA_SUCCESS) && (remaining_len > 0)) {
+
+		size_t erase_len = (remaining_len < sizeof(this_instance->erase_buf)) ?
+			remaining_len :
+			sizeof(this_instance->erase_buf);
+
+		size_t write_len = erase_len;
+
+		if (semihosting_file_write(this_instance->file_handle,
+			&write_len,
+			(const uintptr_t)this_instance->erase_buf)) {
+
+			status = PSA_ERROR_BAD_STATE;
+		}
+
+		remaining_len -= erase_len;
+	}
+
+	return status;
+}
+
+static psa_status_t prepare_for_write(
+	const struct semihosting_block_store *this_instance,
+	uint32_t lba, size_t offset,
+	size_t requested_write_len,
+	size_t *adjusted_write_len)
+{
+	psa_status_t status = PSA_ERROR_BAD_STATE;
+
+	const struct storage_partition *storage_partition =
+		&this_instance->base_block_device.storage_partition;
+
+	size_t bytes_until_end_of_block = storage_partition->block_size - offset;
+	*adjusted_write_len = (requested_write_len < bytes_until_end_of_block) ?
+		requested_write_len :
+		bytes_until_end_of_block;
+
+	ssize_t write_pos = lba * storage_partition->block_size + offset;
+	ssize_t file_len = semihosting_file_length(this_instance->file_handle);
+
+	if (file_len >= 0) {
+
+		if (write_pos > file_len) {
+
+			/* Writing beyond the current end-of-file so extend the file */
+			status = write_erased(this_instance, file_len, write_pos - file_len);
+		}
+		else {
+
+			/* Writing over existing data */
+			status = seek(this_instance->file_handle, write_pos);
+		}
+	}
+
+	return status;
+}
+
+static psa_status_t semihosting_block_store_get_partition_info(void *context,
+	const struct uuid_octets *partition_guid,
+	struct storage_partition_info *info)
+{
+	struct semihosting_block_store *this_instance = (struct semihosting_block_store*)context;
+
+	return block_device_get_partition_info(
+		&this_instance->base_block_device, partition_guid, info);
+}
+
+static psa_status_t semihosting_block_store_open(void *context,
+	uint32_t client_id,
+	const struct uuid_octets *partition_guid,
+	storage_partition_handle_t *handle)
+{
+	struct semihosting_block_store *this_instance = (struct semihosting_block_store*)context;
+	psa_status_t status = PSA_ERROR_BAD_STATE;
+
+	if (this_instance->file_handle > 0) {
+
+		status = block_device_open(
+			&this_instance->base_block_device,
+			client_id, partition_guid, handle);
+	}
+
+	return status;
+}
+
+static psa_status_t semihosting_block_store_close(void *context,
+	uint32_t client_id,
+	storage_partition_handle_t handle)
+{
+	struct semihosting_block_store *this_instance = (struct semihosting_block_store*)context;
+
+	return block_device_close(
+		&this_instance->base_block_device, client_id, handle);
+}
+
+static psa_status_t semihosting_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 semihosting_block_store *this_instance =
+		(struct semihosting_block_store*)context;
+
+	psa_status_t status = block_device_check_access_permitted(
+		&this_instance->base_block_device, client_id, handle);
+
+	*data_len = 0;
+
+	if (status == PSA_SUCCESS) {
+
+		const struct storage_partition *storage_partition =
+			&this_instance->base_block_device.storage_partition;
+
+		if (storage_partition_is_lba_legal(storage_partition, lba) &&
+			(offset < storage_partition->block_size)) {
+
+			size_t read_len = 0;
+
+			status = prepare_for_read(
+				this_instance,
+				lba, offset,
+				buffer_size,
+				&read_len);
+
+			if (status == PSA_SUCCESS) {
+
+				if (!semihosting_file_read(this_instance->file_handle,
+					&read_len,
+					(uintptr_t)buffer)) {
+
+					*data_len = read_len;
+				}
+				else {
+
+					status = PSA_ERROR_BAD_STATE;
+				}
+			}
+		}
+		else {
+
+			/* Block or offset outside of configured limits */
+			status = PSA_ERROR_INVALID_ARGUMENT;
+		}
+	}
+
+	return status;
+}
+
+static psa_status_t semihosting_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 semihosting_block_store *this_instance = (struct semihosting_block_store*)context;
+	psa_status_t status = block_device_check_access_permitted(
+		&this_instance->base_block_device, client_id, handle);
+
+	*num_written = 0;
+
+	if (status == PSA_SUCCESS) {
+
+		const struct storage_partition *storage_partition =
+			&this_instance->base_block_device.storage_partition;
+
+		if (storage_partition_is_lba_legal(storage_partition, lba) &&
+			(offset < storage_partition->block_size)) {
+
+			size_t adjusted_len = 0;
+
+			status = prepare_for_write(
+				this_instance,
+				lba, offset,
+				data_len,
+				&adjusted_len);
+
+			if (status == PSA_SUCCESS) {
+
+				size_t write_len = adjusted_len;
+
+				if (!semihosting_file_write(this_instance->file_handle,
+					&write_len,
+					(uintptr_t)data)) {
+
+					*num_written = adjusted_len;
+				}
+				else {
+
+					status = PSA_ERROR_BAD_STATE;
+				}
+			}
+		}
+		else {
+
+			/* Block or offset outside of configured limits */
+			status = PSA_ERROR_INVALID_ARGUMENT;
+		}
+	}
+
+	return status;
+}
+
+static psa_status_t semihosting_block_store_erase(void *context,
+	uint32_t client_id,
+	storage_partition_handle_t handle,
+	uint32_t begin_lba,
+	size_t num_blocks)
+{
+	struct semihosting_block_store *this_instance = (struct semihosting_block_store*)context;
+	const struct storage_partition *storage_partition =
+		&this_instance->base_block_device.storage_partition;
+
+	psa_status_t status = block_device_check_access_permitted(
+		&this_instance->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_remaining = storage_partition->num_blocks - begin_lba;
+		size_t blocks_to_erase = (num_blocks < blocks_remaining) ? num_blocks : blocks_remaining;
+
+		ssize_t file_len = semihosting_file_length(this_instance->file_handle);
+
+		if (file_len >= 0) {
+
+			/* File exists. If erased block falls within the limits of the file,
+			 * explicitly set blocks to the erased state. If erased block is
+			 * beyond EOF, there's nothing to do. */
+			ssize_t block_pos = begin_lba * storage_partition->block_size;
+
+			if (block_pos < file_len) {
+
+				status = write_erased(this_instance,
+					block_pos,
+					blocks_to_erase * storage_partition->block_size);
+			}
+		}
+		else {
+
+			status = PSA_ERROR_BAD_STATE;
+		}
+	}
+
+	return status;
+}
+
+struct block_store *semihosting_block_store_init(
+	struct semihosting_block_store *this_instance,
+	const char *filename)
+{
+	struct block_store *block_store = NULL;
+
+	/* Define concrete block store interface */
+	static const struct block_store_interface interface =
+	{
+		semihosting_block_store_get_partition_info,
+		semihosting_block_store_open,
+		semihosting_block_store_close,
+		semihosting_block_store_read,
+		semihosting_block_store_write,
+		semihosting_block_store_erase
+	};
+
+	/* Initialize base block_store */
+	this_instance->base_block_device.base_block_store.context = this_instance;
+	this_instance->base_block_device.base_block_store.interface = &interface;
+
+	/* Initialize buffer used for erase operations */
+	memset(this_instance->erase_buf, ERASED_DATA_VAL, sizeof(this_instance->erase_buf));
+
+	/* Test if host file exists. If not, create one */
+	if (semihosting_get_flen(filename) < 0) {
+
+		long handle = semihosting_file_open(filename, FOPEN_MODE_WPLUSB);
+		semihosting_file_close(handle);
+	}
+
+	/* Open host file for normal operation */
+	this_instance->file_handle = semihosting_file_open(
+		filename,
+		FOPEN_MODE_RPLUSB);
+
+	if (this_instance->file_handle > 0) {
+
+		block_store = block_device_init(
+			&this_instance->base_block_device, NULL, 0, 0);
+	}
+
+	return block_store;
+}
+
+void semihosting_block_store_deinit(
+	struct semihosting_block_store *this_instance)
+{
+	if (this_instance->file_handle > 0) {
+
+		semihosting_file_close(this_instance->file_handle);
+		this_instance->file_handle = 0;
+	}
+
+	block_device_deinit(&this_instance->base_block_device);
+}
+
+psa_status_t semihosting_block_store_configure(
+	struct semihosting_block_store *this_instance,
+	const struct uuid_octets *disk_guid,
+	size_t num_blocks,
+	size_t block_size)
+{
+	block_device_configure(&this_instance->base_block_device,
+		disk_guid, num_blocks, block_size);
+
+	return PSA_SUCCESS;
+}
+
+void semihosting_block_store_wipe(
+	const char *filename)
+{
+	/* If file exists and has contents, open with mode w+b to clear contents */
+	if (semihosting_get_flen(filename) > 0) {
+
+		long handle = semihosting_file_open(filename, FOPEN_MODE_WPLUSB);
+		semihosting_file_close(handle);
+	}
+}
\ No newline at end of file
diff --git a/components/service/block_storage/block_store/device/semihosting/semihosting_block_store.h b/components/service/block_storage/block_store/device/semihosting/semihosting_block_store.h
new file mode 100644
index 0000000..572b925
--- /dev/null
+++ b/components/service/block_storage/block_store/device/semihosting/semihosting_block_store.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef SEMIHOSTING_BLOCK_STORE_H
+#define SEMIHOSTING_BLOCK_STORE_H
+
+#include <stdint.h>
+#include "service/block_storage/block_store/device/block_device.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief semihosting_block_store structure
+ *
+ * A semihosting_block_store is a block_device that can be used on virtual
+ * platforms with semihosting capability to store blocks in a file residing
+ * in the host machine's filesystem. The file contains the raw block data
+ * which may include a partition header such as a GPT. The semihosting_block_store
+ * is agnostic about the file contents.
+ */
+struct semihosting_block_store
+{
+	struct block_device base_block_device;
+	long file_handle;
+	uint8_t erase_buf[256];
+};
+
+/**
+ * \brief Initialize a semihosting_block_store
+ *
+ * \param[in]  semihosting_block_store  The subject semihosting_block_store
+ * \param[in]  filename			The host filename used for storage
+ *
+ * \return Pointer to block_store or NULL on failure
+ */
+struct block_store *semihosting_block_store_init(
+	struct semihosting_block_store *semihosting_block_store,
+	const char *filename);
+
+/**
+ * \brief De-initialize a semihosting_block_store
+ *
+ * \param[in]  semihosting_block_store  The subject semihosting_block_store
+ */
+void semihosting_block_store_deinit(
+	struct semihosting_block_store *semihosting_block_store);
+
+/**
+ * \brief Configure the semihosting_block_store
+ *
+ * \param[in]  semihosting_block_store  The subject semihosting_block_store
+ * \param[in]  disk_guid   		The disk GUID (nil uuid for any)
+ * \param[in]  num_blocks       The number of contiguous blocks
+ * \param[in]  block_size       Block size in bytes
+ *
+ * \return PSA_SUCCESS if successful
+ */
+psa_status_t semihosting_block_store_configure(
+	struct semihosting_block_store *semihosting_block_store,
+	const struct uuid_octets *disk_guid,
+	size_t num_blocks,
+	size_t block_size);
+
+/**
+ * \brief Wipe contents of the block store
+ *
+ * Test support function to wipe the contents of the block store file.
+ *
+ * \param[in]  filename			The host filename used for storage
+ */
+void semihosting_block_store_wipe(
+	const char *filename);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SEMIHOSTING_BLOCK_STORE_H */
diff --git a/components/service/block_storage/block_store/device/semihosting/test/component.cmake b/components/service/block_storage/block_store/device/semihosting/test/component.cmake
new file mode 100644
index 0000000..a92cbac
--- /dev/null
+++ b/components/service/block_storage/block_store/device/semihosting/test/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}/sh_block_store_tests.c"
+	)
+
diff --git a/components/service/block_storage/block_store/device/semihosting/test/sh_block_store_tests.c b/components/service/block_storage/block_store/device/semihosting/test/sh_block_store_tests.c
new file mode 100644
index 0000000..84a5a8e
--- /dev/null
+++ b/components/service/block_storage/block_store/device/semihosting/test/sh_block_store_tests.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include "common/uuid/uuid.h"
+#include <service/test_runner/provider/backend/simple_c/simple_c_test_runner.h>
+#include <service/block_storage/block_store/device/semihosting/semihosting_block_store.h>
+
+#define BLOCK_SIZE		(128)
+#define NUM_BLOCKS		(20)
+#define CLIENT_ID		(0)
+
+/**
+ * Tests for the semihosting block store.
+ */
+static const char *sh_filename = "test-store.img";
+static struct semihosting_block_store sh_block_store;
+static storage_partition_handle_t partition_handle;
+
+static bool setup(struct test_failure *failure)
+{
+	semihosting_block_store_wipe(sh_filename);
+
+	if (!semihosting_block_store_init(&sh_block_store, sh_filename)) {
+
+		/* Failed to initialize block store */
+		failure->line_num = __LINE__;
+		return false;
+	}
+
+	struct uuid_octets disk_guid;
+	uuid_parse_to_octets("418edc38-fa66-4871-979b-e67bee88b8f2",
+		disk_guid.octets,
+		sizeof(disk_guid.octets));
+
+	semihosting_block_store_configure(&sh_block_store,
+		&disk_guid,
+		NUM_BLOCKS, BLOCK_SIZE);
+
+	psa_status_t status = block_store_open(
+		&sh_block_store.base_block_device.base_block_store,
+		CLIENT_ID,
+		&disk_guid,
+		&partition_handle);
+
+	if (status != PSA_SUCCESS) {
+
+		failure->line_num = __LINE__;
+		failure->info = status;
+
+		semihosting_block_store_deinit(&sh_block_store);
+
+		return false;
+	}
+
+	return true;
+}
+
+static void teardown(void)
+{
+	block_store_close(
+		&sh_block_store.base_block_device.base_block_store,
+		CLIENT_ID,
+		partition_handle);
+
+	semihosting_block_store_deinit(&sh_block_store);
+}
+
+static bool set_block(struct test_failure *failure,
+	size_t lba, size_t offset,
+	size_t len, uint8_t val,
+	size_t *num_written)
+{
+	bool success = true;
+	struct block_store *bs = &sh_block_store.base_block_device.base_block_store;
+	uint8_t *write_buf = malloc(len);
+
+	if (!write_buf) {
+
+		failure->line_num = __LINE__;
+		return false;
+	}
+
+	memset(write_buf, val, len);
+	*num_written = 0;
+
+	psa_status_t status = block_store_write(
+		bs, CLIENT_ID, partition_handle,
+		lba, offset,
+		write_buf, len, num_written);
+
+	if (status != PSA_SUCCESS) {
+
+		failure->line_num = __LINE__;
+		failure->info = status;
+		success	= false;
+	}
+
+	free(write_buf);
+
+	return success;
+}
+
+static bool check_block(struct test_failure *failure,
+	size_t lba, size_t offset,
+	size_t len, uint8_t expected_val)
+{
+	bool success = true;
+	struct block_store *bs = &sh_block_store.base_block_device.base_block_store;
+	uint8_t *read_buf = malloc(len);
+
+	if (!read_buf) {
+
+		failure->line_num = __LINE__;
+		return false;
+	}
+
+	size_t num_read = 0;
+
+	psa_status_t status = block_store_read(
+		bs, CLIENT_ID, partition_handle,
+		lba, offset,
+		len, read_buf,
+		&num_read);
+
+	if (status == PSA_SUCCESS) {
+
+		if (num_read == len) {
+
+			for (size_t i = 0; i < len; i++) {
+
+				if (read_buf[i] != expected_val) {
+
+					failure->line_num = __LINE__;
+					failure->info = read_buf[i];
+					success	= false;
+					break;
+				}
+			}
+		}
+		else {
+
+			failure->line_num = __LINE__;
+			failure->info = num_read;
+			success	= false;
+		}
+	}
+	else {
+
+		failure->line_num = __LINE__;
+		failure->info = status;
+		success	= false;
+	}
+
+	free(read_buf);
+
+	return success;
+}
+
+static bool erase_blocks(struct test_failure *failure,
+	uint32_t begin_lba,
+	size_t num_blocks)
+{
+	bool success = true;
+	struct block_store *bs = &sh_block_store.base_block_device.base_block_store;
+
+	psa_status_t status = block_store_erase(
+		bs, CLIENT_ID, partition_handle,
+		begin_lba, num_blocks);
+
+	if (status != PSA_SUCCESS) {
+
+		failure->line_num = __LINE__;
+		failure->info = status;
+		success	= false;
+	}
+
+	return success;
+}
+
+/*
+ * Check a sequence of whole block writes and reads.
+ */
+static bool check_whole_block_rw(struct test_failure *failure)
+{
+	bool pass = setup(failure);
+
+	if (pass) {
+
+		size_t num_written = 0;
+
+		if (pass && (pass = set_block(failure, 7, 0, BLOCK_SIZE, 'a', &num_written))) {
+
+			if (!(pass = (num_written == BLOCK_SIZE))) {
+
+				failure->line_num = __LINE__;
+				failure->info = num_written;
+			}
+		}
+
+		if (pass && (pass = set_block(failure, 6, 0, BLOCK_SIZE, 'b', &num_written))) {
+
+			if (!(pass = (num_written == BLOCK_SIZE))) {
+
+				failure->line_num = __LINE__;
+				failure->info = num_written;
+			}
+		}
+
+		if (pass && (pass = set_block(failure, 1, 0, BLOCK_SIZE, 'c', &num_written))) {
+
+			if (!(pass = (num_written == BLOCK_SIZE))) {
+
+				failure->line_num = __LINE__;
+				failure->info = num_written;
+			}
+		}
+
+		if (pass && (pass = set_block(failure, 9, 0, BLOCK_SIZE, 'd', &num_written))) {
+
+			if (!(pass = (num_written == BLOCK_SIZE))) {
+
+				failure->line_num = __LINE__;
+				failure->info = num_written;
+			}
+		}
+
+		/* Check written blocks are as expected */
+		if (pass) {
+
+			pass =
+				check_block(failure, 7, 0, BLOCK_SIZE, 'a') &&
+				check_block(failure, 6, 0, BLOCK_SIZE, 'b') &&
+				check_block(failure, 1, 0, BLOCK_SIZE, 'c') &&
+				check_block(failure, 9, 0, BLOCK_SIZE, 'd');
+		}
+
+		/* Erase all the written blocks */
+		if (pass) {
+
+			pass =
+				erase_blocks(failure, 9, 1) &&
+				erase_blocks(failure, 1, 1) &&
+				erase_blocks(failure, 7, 1) &&
+				erase_blocks(failure, 6, 1);
+		}
+
+		teardown();
+	}
+
+	return pass;
+}
+
+/**
+ * Define and register test group
+ */
+void sh_block_store_tests_register(void)
+{
+	static const struct simple_c_test_case sh_block_store_tests[] = {
+		{.name = "WholeBlockRw", .test_func = check_whole_block_rw},
+	};
+
+	static const struct simple_c_test_group sh_block_store_test_group =
+	{
+		.group = "ShBlockStoreTests",
+		.num_test_cases = sizeof(sh_block_store_tests)/sizeof(struct simple_c_test_case),
+		.test_cases = sh_block_store_tests
+	};
+
+	simple_c_test_runner_register_group(&sh_block_store_test_group);
+}
diff --git a/components/service/block_storage/block_store/device/semihosting/test/sh_block_store_tests.h b/components/service/block_storage/block_store/device/semihosting/test/sh_block_store_tests.h
new file mode 100644
index 0000000..8780647
--- /dev/null
+++ b/components/service/block_storage/block_store/device/semihosting/test/sh_block_store_tests.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SH_BLOCK_STORE_TESTS_H
+#define SH_BLOCK_STORE_TESTS_H
+
+void sh_block_store_tests_register(void);
+
+#endif /* SH_BLOCK_STORE_TESTS_H */
diff --git a/components/service/block_storage/block_store/storage_partition.c b/components/service/block_storage/block_store/storage_partition.c
index a3b1331..b5d7cf6 100644
--- a/components/service/block_storage/block_store/storage_partition.c
+++ b/components/service/block_storage/block_store/storage_partition.c
@@ -15,7 +15,9 @@
 	size_t num_blocks,
 	size_t block_size)
 {
-	partition->partition_guid = *partition_guid;
+	memset(partition, 0, sizeof(struct storage_partition));
+
+	if (partition_guid) partition->partition_guid = *partition_guid;
 	partition->block_size = block_size;
 	partition->num_blocks = num_blocks;
 
diff --git a/components/service/block_storage/factory/semihosting/block_store_factory.c b/components/service/block_storage/factory/semihosting/block_store_factory.c
new file mode 100644
index 0000000..ce4bae4
--- /dev/null
+++ b/components/service/block_storage/factory/semihosting/block_store_factory.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "block_store_factory.h"
+#include "service/block_storage/block_store/device/semihosting/semihosting_block_store.h"
+#include "service/block_storage/block_store/partitioned/partitioned_block_store.h"
+
+struct block_store_assembly
+{
+	struct semihosting_block_store semihosting_block_store;
+	struct partitioned_block_store partitioned_block_store;
+};
+
+struct block_store *semihosting_block_store_factory_create(void)
+{
+	struct block_store *product = NULL;
+	struct block_store_assembly *assembly =
+		(struct block_store_assembly*)malloc(sizeof(struct block_store_assembly));
+
+	if (assembly) {
+
+		struct uuid_octets back_store_guid;
+		memset(&back_store_guid, 0, sizeof(back_store_guid));
+
+		/* Initialise a semihosting_block_store to provide underlying storage */
+		struct block_store *back_store = semihosting_block_store_init(
+			&assembly->semihosting_block_store,
+			"secure-flash.img");
+
+		/* Stack a partitioned_block_store over the back store */
+		product = partitioned_block_store_init(
+			&assembly->partitioned_block_store,
+			0,
+			&back_store_guid,
+			back_store,
+			NULL);
+
+		if (!product) {
+
+			/* Something went wrong! */
+			free(assembly);
+		}
+	}
+
+	return product;
+}
+
+void semihosting_block_store_factory_destroy(struct block_store *block_store)
+{
+	if (block_store) {
+
+		size_t offset_into_assembly =
+			offsetof(struct block_store_assembly, partitioned_block_store) +
+			offsetof(struct partitioned_block_store, base_block_store);
+
+		struct block_store_assembly *assembly = (struct block_store_assembly*)
+			((uint8_t*)block_store - offset_into_assembly);
+
+		partitioned_block_store_deinit(&assembly->partitioned_block_store);
+		semihosting_block_store_deinit(&assembly->semihosting_block_store);
+
+		free(assembly);
+	}
+}
diff --git a/components/service/block_storage/factory/semihosting/block_store_factory.h b/components/service/block_storage/factory/semihosting/block_store_factory.h
new file mode 100644
index 0000000..85c2f64
--- /dev/null
+++ b/components/service/block_storage/factory/semihosting/block_store_factory.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2022, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ */
+
+#ifndef SEMIHOSTING_BLOCK_STORE_FACTORY_H
+#define SEMIHOSTING_BLOCK_STORE_FACTORY_H
+
+#include "service/block_storage/block_store/block_store.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * A block store factory that constructs a partitioned block store backed
+ * by a semihosting block device.
+ */
+
+/**
+ * \brief Factory method to create a block_store
+ *
+ * \return A pointer to the constructed block_store (NULL on failure)
+ */
+struct block_store *semihosting_block_store_factory_create(void);
+
+/**
+ * \brief Destroys a block_store created with block_store_factory_create
+ *
+ * \param[in] block_store    The block store to destroy
+ */
+void semihosting_block_store_factory_destroy(struct block_store *block_store);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SEMIHOSTING_BLOCK_STORE_FACTORY_H */
diff --git a/components/service/block_storage/factory/semihosting/component.cmake b/components/service/block_storage/factory/semihosting/component.cmake
new file mode 100644
index 0000000..97affaf
--- /dev/null
+++ b/components/service/block_storage/factory/semihosting/component.cmake
@@ -0,0 +1,20 @@
+#-------------------------------------------------------------------------------
+# 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_factory.c"
+	)
+
+# If none already defined, make this the default factory for the deployment
+if (NOT DEFINED TS_BLOCK_STORE_FACTORY)
+	set(TS_BLOCK_STORE_FACTORY "semihosting_block_store_factory")
+	target_compile_definitions(${TGT} PRIVATE
+		CONCRETE_BLOCK_STORE_FACTORY=${TS_BLOCK_STORE_FACTORY})
+endif()
\ No newline at end of file
diff --git a/components/service/crypto/backend/mbedcrypto/trng_adapter/platform/component.cmake b/components/service/crypto/backend/mbedcrypto/trng_adapter/platform/component.cmake
index 575ac22..d2bf6d4 100644
--- a/components/service/crypto/backend/mbedcrypto/trng_adapter/platform/component.cmake
+++ b/components/service/crypto/backend/mbedcrypto/trng_adapter/platform/component.cmake
@@ -1,5 +1,5 @@
 #-------------------------------------------------------------------------------
-# Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
+# Copyright (c) 2020-2022, Arm Limited and Contributors. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -12,6 +12,6 @@
 	"${CMAKE_CURRENT_LIST_DIR}/platform_trng_adapter.c"
 	)
 
-set_property(TARGET ${TGT} APPEND_STRING PROPERTY TS_PLATFORM_DRIVER_DEPENDENCIES
+set_property(TARGET ${TGT} APPEND PROPERTY TS_PLATFORM_DRIVER_DEPENDENCIES
 	"trng"
 	)
diff --git a/deployments/block-storage/config/semihosted-opteesp/CMakeLists.txt b/deployments/block-storage/config/semihosted-opteesp/CMakeLists.txt
new file mode 100644
index 0000000..1dbe0e0
--- /dev/null
+++ b/deployments/block-storage/config/semihosted-opteesp/CMakeLists.txt
@@ -0,0 +1,105 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
+
+#-------------------------------------------------------------------------------
+# Set default platform.
+#
+#-------------------------------------------------------------------------------
+set(TS_PLATFORM "arm/fvp/fvp_base_revc-2xaemv8a" CACHE STRING "Target platform location.")
+
+include(../../../deployment.cmake REQUIRED)
+
+#-------------------------------------------------------------------------------
+#  The CMakeLists.txt for building the block_storage deployment for opteesp
+#
+#  Builds the block_storage service provider for running in an SEL0 secure partition
+#  hosted by OPTEE in the role of SPM.
+#-------------------------------------------------------------------------------
+include(${TS_ROOT}/environments/opteesp/env.cmake)
+project(trusted-services LANGUAGES C ASM)
+add_executable(block-storage)
+target_include_directories(block-storage PRIVATE "${TOP_LEVEL_INCLUDE_DIRS}")
+set(SP_UUID_CANON "63646e80-eb52-462f-ac4f-8cdf3987519c")
+set(SP_HEAP_SIZE "120 * 1024" CACHE STRING "SP heap size in bytes")
+set(TRACE_PREFIX "BLOCK" CACHE STRING "Trace prefix")
+include(${TS_ROOT}/tools/cmake/common/TargetCompileDefinitions.cmake)
+set_target_uuids(
+	SP_UUID ${SP_UUID_CANON}
+	SP_NAME "block-storage"
+)
+
+target_include_directories(block-storage PRIVATE
+	${CMAKE_CURRENT_LIST_DIR}
+)
+
+#-------------------------------------------------------------------------------
+#  External project source-level dependencies
+#
+#-------------------------------------------------------------------------------
+include(${TS_ROOT}/external/tf_a/tf-a.cmake)
+add_tfa_dependency(TARGET "block-storage")
+
+#-------------------------------------------------------------------------------
+#  Deployment specific components. This deployment uses an infrastructure that
+#  that provides NV storage backed by a semihosted file residing in a host
+#  device filesystem.
+#-------------------------------------------------------------------------------
+add_components(TARGET "block-storage"
+	BASE_DIR ${TS_ROOT}
+	COMPONENTS
+		"environments/opteesp"
+)
+
+include(../../env/commonsp/block_storage_sp.cmake REQUIRED)
+include(../../block-storage.cmake REQUIRED)
+include(../../infra/semihosted.cmake REQUIRED)
+
+#-------------------------------------------------------------------------------
+#  Set target platform to provide drivers needed by the deployment
+#
+#-------------------------------------------------------------------------------
+add_platform(TARGET "block-storage")
+
+#-------------------------------------------------------------------------------
+#  Deployment specific build options
+#-------------------------------------------------------------------------------
+target_compile_definitions(block-storage PRIVATE
+	ARM64=1
+)
+
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
+	target_compile_options(block-storage PRIVATE
+		-std=c99
+	)
+
+endif()
+
+compiler_generate_stripped_elf(TARGET block-storage NAME "${SP_UUID_CANON}.stripped.elf" RES STRIPPED_ELF)
+
+#-------------------------------------------------------------------------------
+#  Deployment specific install options
+#-------------------------------------------------------------------------------
+if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+	set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install CACHE PATH "location to install build output to." FORCE)
+endif()
+
+install(TARGETS block-storage
+			PUBLIC_HEADER DESTINATION ${TS_ENV}/include
+			RUNTIME DESTINATION ${TS_ENV}/bin
+		)
+install(FILES ${STRIPPED_ELF} DESTINATION ${TS_ENV}/bin)
+
+include(${TS_ROOT}/tools/cmake/common/ExportSp.cmake)
+export_sp(
+	SP_UUID_CANON ${SP_UUID_CANON}
+	SP_UUID_LE ${SP_UUID_LE}
+	SP_NAME "block-storage"
+	MK_IN ${TS_ROOT}/environments/opteesp/sp.mk.in
+	DTS_IN ${CMAKE_CURRENT_LIST_DIR}/default_block-storage.dts.in
+	JSON_IN ${TS_ROOT}/environments/opteesp/sp_pkg.json.in
+)
diff --git a/deployments/block-storage/config/semihosted-opteesp/default_block-storage.dts.in b/deployments/block-storage/config/semihosted-opteesp/default_block-storage.dts.in
new file mode 100644
index 0000000..0d30738
--- /dev/null
+++ b/deployments/block-storage/config/semihosted-opteesp/default_block-storage.dts.in
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+@DTS_TAG@
+
+@DTS_NODE@ {
+	compatible = "arm,ffa-manifest-1.0";
+	ffa-version = <0x00010000>; /* 31:16 - Major, 15:0 - Minor */
+	uuid = <@EXPORT_SP_UUID_DT@>;
+	description = "BlockStorage";
+	execution-ctx-count = <1>;
+	exception-level = <1>; /* S-EL0 */
+	execution-state = <0>; /* AArch64 */
+	xlat-granule = <0>; /* 4KiB */
+	messaging-method = <3>; /* Direct messaging only */
+	legacy-elf-format = <1>;
+};
diff --git a/deployments/block-storage/config/semihosted-opteesp/optee_sp_user_defines.h b/deployments/block-storage/config/semihosted-opteesp/optee_sp_user_defines.h
new file mode 100644
index 0000000..798e873
--- /dev/null
+++ b/deployments/block-storage/config/semihosted-opteesp/optee_sp_user_defines.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+ */
+
+#ifndef OPTEE_SP_USER_DEFINES_H
+#define OPTEE_SP_USER_DEFINES_H
+
+#define OPTEE_SP_FLAGS			0
+
+/* Provisioned stack size */
+#define OPTEE_SP_STACK_SIZE			(64 * 1024)
+
+#endif /* SP_HEADER_DEFINES_H */
diff --git a/deployments/block-storage/infra/semihosted.cmake b/deployments/block-storage/infra/semihosted.cmake
new file mode 100644
index 0000000..1f9ed28
--- /dev/null
+++ b/deployments/block-storage/infra/semihosted.cmake
@@ -0,0 +1,28 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# A block-storage infrastructure for use on virtual platforms where storage
+# is provided by file residing in the host's filesystem. If the file contains
+# a GPT, it will be used to configure storage partitions.
+#-------------------------------------------------------------------------------
+
+#-------------------------------------------------------------------------------
+# Infrastructure components
+#
+#-------------------------------------------------------------------------------
+add_components(TARGET "block-storage"
+	BASE_DIR ${TS_ROOT}
+	COMPONENTS
+		"components/service/block_storage/block_store/device"
+		"components/service/block_storage/block_store/device/semihosting"
+		"components/service/block_storage/block_store/partitioned"
+		"components/service/block_storage/factory/semihosting"
+)
+
+#-------------------------------------------------------------------------------
+#  This infrastructure depends on platform specific drivers
+#
+#-------------------------------------------------------------------------------
+add_platform(TARGET "block-storage")
\ No newline at end of file
diff --git a/deployments/env-test/config/baremetal-fvp_base_revc-opteesp/CMakeLists.txt b/deployments/env-test/config/baremetal-fvp_base_revc-opteesp/CMakeLists.txt
index 1273a2b..b885cac 100644
--- a/deployments/env-test/config/baremetal-fvp_base_revc-opteesp/CMakeLists.txt
+++ b/deployments/env-test/config/baremetal-fvp_base_revc-opteesp/CMakeLists.txt
@@ -35,6 +35,14 @@
 
 #-------------------------------------------------------------------------------
 #  Deployment specific components
+#  External project source-level dependencies
+#
+#-------------------------------------------------------------------------------
+include(${TS_ROOT}/external/tf_a/tf-a.cmake)
+add_tfa_dependency(TARGET "env-test")
+
+#-------------------------------------------------------------------------------
+#  Defines environment and test suites for env-test deployment
 #
 #-------------------------------------------------------------------------------
 add_components(TARGET "env-test"
diff --git a/deployments/env-test/suites/baremetal-tests.cmake b/deployments/env-test/suites/baremetal-tests.cmake
index 6591719..6baf1f6 100644
--- a/deployments/env-test/suites/baremetal-tests.cmake
+++ b/deployments/env-test/suites/baremetal-tests.cmake
@@ -14,6 +14,7 @@
 add_components(TARGET "env-test"
 	BASE_DIR ${TS_ROOT}
 	COMPONENTS
+		"components/common/uuid"
 		"components/service/crypto/backend/mbedcrypto"
 		"components/service/crypto/backend/mbedcrypto/trng_adapter/platform"
 		"components/service/crypto/backend/mbedcrypto/trng_adapter/test"
@@ -21,6 +22,10 @@
 		"components/service/secure_storage/frontend/psa/its"
 		"components/service/secure_storage/backend/secure_storage_client"
 		"components/config/test/sp"
+		"components/service/block_storage/block_store"
+		"components/service/block_storage/block_store/device"
+		"components/service/block_storage/block_store/device/semihosting"
+		"components/service/block_storage/block_store/device/semihosting/test"
 )
 
 target_sources(env-test PRIVATE
@@ -40,4 +45,4 @@
 #  This test suite depends on platform specific drivers
 #
 #-------------------------------------------------------------------------------
-add_platform(TARGET "env-test")
\ No newline at end of file
+add_platform(TARGET "env-test")
diff --git a/deployments/env-test/suites/registration/baremetal_tests.c b/deployments/env-test/suites/registration/baremetal_tests.c
index 5c01999..d7695a5 100644
--- a/deployments/env-test/suites/registration/baremetal_tests.c
+++ b/deployments/env-test/suites/registration/baremetal_tests.c
@@ -6,6 +6,7 @@
 #include <service/test_runner/provider/backend/simple_c/simple_c_test_runner.h>
 #include <config/test/sp/sp_config_tests.h>
 #include <service/crypto/backend/mbedcrypto/trng_adapter/test/trng_env_tests.h>
+#include <service/block_storage/block_store/device/semihosting/test/sh_block_store_tests.h>
 
 /**
  * Register tests that constitute the 'baremetal-tests' suite. Used for testing
@@ -17,4 +18,5 @@
 
 	sp_config_tests_register();
 	trng_env_tests_register();
+	sh_block_store_tests_register();
 }
diff --git a/external/tf_a/tf-a.cmake b/external/tf_a/tf-a.cmake
index 2f5d81f..4c86096 100644
--- a/external/tf_a/tf-a.cmake
+++ b/external/tf_a/tf-a.cmake
@@ -52,5 +52,6 @@
 	endif()
 
 	target_include_directories(${MY_PARAMS_TARGET} PRIVATE "${TFA_SOURCE_DIR}/include")
+	target_include_directories(${MY_PARAMS_TARGET} PRIVATE "${TFA_SOURCE_DIR}/include/arch/aarch64")
 
 endfunction()
\ No newline at end of file
diff --git a/platform/drivers/tf-a/lib/semihosting/driver.cmake b/platform/drivers/tf-a/lib/semihosting/driver.cmake
new file mode 100644
index 0000000..3a33628
--- /dev/null
+++ b/platform/drivers/tf-a/lib/semihosting/driver.cmake
@@ -0,0 +1,20 @@
+#-------------------------------------------------------------------------------
+# 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()
+
+#-------------------------------------------------------------------------------
+# Depends on the tf-a external component
+#-------------------------------------------------------------------------------
+set(_SEMIHOSTING_LIB_DIR "${TFA_SOURCE_DIR}/lib/semihosting")
+
+target_sources(${TGT} PRIVATE
+	"${_SEMIHOSTING_LIB_DIR}/semihosting.c"
+	"${_SEMIHOSTING_LIB_DIR}/aarch64/semihosting_call.S"
+)
+
diff --git a/platform/providers/arm/fvp/fvp_base_revc-2xaemv8a/platform.cmake b/platform/providers/arm/fvp/fvp_base_revc-2xaemv8a/platform.cmake
index adefbae..afaaac4 100644
--- a/platform/providers/arm/fvp/fvp_base_revc-2xaemv8a/platform.cmake
+++ b/platform/providers/arm/fvp/fvp_base_revc-2xaemv8a/platform.cmake
@@ -23,4 +23,8 @@
 
 if ("secure-nor-flash" IN_LIST _platform_driver_dependencies)
 	include(${TS_ROOT}/platform/drivers/tf-a/drivers/cfi/v2m/v2m_flash.cmake)
+endif()
+
+if ("semihosting" IN_LIST _platform_driver_dependencies)
+	include(${TS_ROOT}/platform/drivers/tf-a/lib/semihosting/driver.cmake)
 endif()
\ No newline at end of file