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