aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlivier Deprez <olivier.deprez@arm.com>2020-10-29 18:05:43 +0000
committerTrustedFirmware Code Review <review@review.trustedfirmware.org>2020-10-29 18:05:43 +0000
commit949f423d4648218bcb734874d7698a72d86563b1 (patch)
tree2f586f95feec90cb7bf70a73cbd19f77ee493401
parentb90571b4113cd4a6d5e505c0c39c4d50e1ff141b (diff)
parent40618a311dd200c62cb6b6753edb93f22cb831c8 (diff)
downloadtf-a-tests-master.tar.gz
Merge changes from topic "mem_share"HEADv2.4-rc0master
* changes: TFTF: tests for FF-A memory sharing operations SPM: FFA endpoints header SPM: TFTF skip test if FFA endpoint absent cactus: adding memory sharing tests cactus: macros for processing of commands SPM: memory sharing functions and structures FFA: Mem sharing ABIs
-rw-r--r--include/common/test_helpers.h15
-rw-r--r--include/runtime_services/ffa_endpoints.h23
-rw-r--r--include/runtime_services/ffa_helpers.h335
-rw-r--r--spm/cactus/cactus.ld.S1
-rw-r--r--spm/cactus/cactus.mk1
-rw-r--r--spm/cactus/cactus_def.h8
-rw-r--r--spm/cactus/cactus_ffa_tests.c159
-rw-r--r--spm/cactus/cactus_main.c66
-rw-r--r--spm/cactus/cactus_test_cmds.h60
-rw-r--r--spm/cactus/cactus_tests.h4
-rw-r--r--tftf/tests/runtime_services/secure_service/ffa_helpers.c255
-rw-r--r--tftf/tests/runtime_services/secure_service/test_ffa_memory_sharing.c187
-rw-r--r--tftf/tests/tests-spm.mk1
-rw-r--r--tftf/tests/tests-spm.xml10
14 files changed, 1088 insertions, 37 deletions
diff --git a/include/common/test_helpers.h b/include/common/test_helpers.h
index bf14a803..f9d41ddb 100644
--- a/include/common/test_helpers.h
+++ b/include/common/test_helpers.h
@@ -202,6 +202,21 @@ typedef test_result_t (*test_function_arg_t)(void *arg);
} \
} while (0)
+#define SKIP_TEST_IF_FFA_ENDPOINT_NOT_DEPLOYED(mb, uuid) \
+ do { \
+ const uint32_t ffa_uuid[4] = uuid; \
+ smc_ret_values smc_ret = ffa_partition_info_get(ffa_uuid); \
+ ffa_rx_release(); \
+ if (smc_ret.ret0 == FFA_ERROR && \
+ smc_ret.ret2 == FFA_ERROR_INVALID_PARAMETER) { \
+ tftf_testcase_printf("FFA endpoint not deployed!\n"); \
+ return TEST_RESULT_SKIPPED; \
+ } else if (smc_ret.ret0 != FFA_SUCCESS_SMC32) { \
+ ERROR("ffa_partition_info_get failed!\n"); \
+ return TEST_RESULT_FAIL; \
+ } \
+ } while (0)
+
/* Helper macro to verify if system suspend API is supported */
#define is_psci_sys_susp_supported() \
(tftf_get_psci_feature_info(SMC_PSCI_SYSTEM_SUSPEND) \
diff --git a/include/runtime_services/ffa_endpoints.h b/include/runtime_services/ffa_endpoints.h
new file mode 100644
index 00000000..8d108edc
--- /dev/null
+++ b/include/runtime_services/ffa_endpoints.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef FFA_ENDPOINTS_H
+#define FFA_ENDPOINTS_H
+
+/* UUID of cactus SPs as defined in the respective manifests. */
+#define PRIMARY_UUID {0xb4b5671e, 0x4a904fe1, 0xb81ffb13, 0xdae1dacb}
+#define SECONDARY_UUID {0xd1582309, 0xf02347b9, 0x827c4464, 0xf5578fc8}
+#define TERTIARY_UUID {0x79b55c73, 0x1d8c44b9, 0x859361e1, 0x770ad8d2}
+
+/* UUID of OPTEE SP as defined in the respective manifest. */
+#define OPTEE_UUID {0x486178e0, 0xe7f811e3, 0xbc5e0002, 0xa5d5c51b}
+
+#define OPTEE_FFA_GET_API_VERSION (0)
+#define OPTEE_FFA_GET_OS_VERSION (1)
+#define OPTEE_FFA_GET_OS_VERSION_MAJOR (3)
+#define OPTEE_FFA_GET_OS_VERSION_MINOR (8)
+
+#endif
diff --git a/include/runtime_services/ffa_helpers.h b/include/runtime_services/ffa_helpers.h
index 2cc2c467..766ea39f 100644
--- a/include/runtime_services/ffa_helpers.h
+++ b/include/runtime_services/ffa_helpers.h
@@ -24,6 +24,9 @@ typedef unsigned short ffa_vm_id_t;
typedef unsigned short ffa_vm_count_t;
typedef unsigned short ffa_vcpu_count_t;
typedef uint32_t ffa_int_id_t;
+typedef uint64_t ffa_memory_handle_t;
+/** Flags to indicate properties of receivers during memory region retrieval. */
+typedef uint8_t ffa_memory_receiver_flags_t;
#ifndef __ASSEMBLY__
@@ -43,13 +46,328 @@ struct ffa_partition_info {
uint32_t properties;
};
-/*
- * TODO: In the future this file should be placed in a common folder, and not
- * under tftf. The functions in this file are also used by SPs for SPM tests.
+enum ffa_data_access {
+ FFA_DATA_ACCESS_NOT_SPECIFIED,
+ FFA_DATA_ACCESS_RO,
+ FFA_DATA_ACCESS_RW,
+ FFA_DATA_ACCESS_RESERVED,
+};
+
+enum ffa_instruction_access {
+ FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED,
+ FFA_INSTRUCTION_ACCESS_NX,
+ FFA_INSTRUCTION_ACCESS_X,
+ FFA_INSTRUCTION_ACCESS_RESERVED,
+};
+
+enum ffa_memory_type {
+ FFA_MEMORY_NOT_SPECIFIED_MEM,
+ FFA_MEMORY_DEVICE_MEM,
+ FFA_MEMORY_NORMAL_MEM,
+};
+
+enum ffa_memory_cacheability {
+ FFA_MEMORY_CACHE_RESERVED = 0x0,
+ FFA_MEMORY_CACHE_NON_CACHEABLE = 0x1,
+ FFA_MEMORY_CACHE_RESERVED_1 = 0x2,
+ FFA_MEMORY_CACHE_WRITE_BACK = 0x3,
+ FFA_MEMORY_DEV_NGNRNE = 0x0,
+ FFA_MEMORY_DEV_NGNRE = 0x1,
+ FFA_MEMORY_DEV_NGRE = 0x2,
+ FFA_MEMORY_DEV_GRE = 0x3,
+};
+
+enum ffa_memory_shareability {
+ FFA_MEMORY_SHARE_NON_SHAREABLE,
+ FFA_MEMORY_SHARE_RESERVED,
+ FFA_MEMORY_OUTER_SHAREABLE,
+ FFA_MEMORY_INNER_SHAREABLE,
+};
+
+typedef uint8_t ffa_memory_access_permissions_t;
+
+/**
+ * This corresponds to table "Memory region attributes descriptor" of the FF-A
+ * 1.0 specification.
+ */
+typedef uint8_t ffa_memory_attributes_t;
+
+#define FFA_DATA_ACCESS_OFFSET (0x0U)
+#define FFA_DATA_ACCESS_MASK ((0x3U) << FFA_DATA_ACCESS_OFFSET)
+
+#define FFA_INSTRUCTION_ACCESS_OFFSET (0x2U)
+#define FFA_INSTRUCTION_ACCESS_MASK ((0x3U) << FFA_INSTRUCTION_ACCESS_OFFSET)
+
+#define FFA_MEMORY_TYPE_OFFSET (0x4U)
+#define FFA_MEMORY_TYPE_MASK ((0x3U) << FFA_MEMORY_TYPE_OFFSET)
+
+#define FFA_MEMORY_CACHEABILITY_OFFSET (0x2U)
+#define FFA_MEMORY_CACHEABILITY_MASK ((0x3U) << FFA_MEMORY_CACHEABILITY_OFFSET)
+
+#define FFA_MEMORY_SHAREABILITY_OFFSET (0x0U)
+#define FFA_MEMORY_SHAREABILITY_MASK ((0x3U) << FFA_MEMORY_SHAREABILITY_OFFSET)
+
+#define ATTR_FUNCTION_SET(name, container_type, offset, mask) \
+ static inline void ffa_set_##name##_attr(container_type *attr, \
+ const enum ffa_##name perm) \
+ { \
+ *attr = (*attr & ~(mask)) | ((perm << offset) & mask); \
+ }
+
+#define ATTR_FUNCTION_GET(name, container_type, offset, mask) \
+ static inline enum ffa_##name ffa_get_##name##_attr( \
+ container_type attr) \
+ { \
+ return (enum ffa_##name)((attr & mask) >> offset); \
+ }
+
+ATTR_FUNCTION_SET(data_access, ffa_memory_access_permissions_t,
+ FFA_DATA_ACCESS_OFFSET, FFA_DATA_ACCESS_MASK)
+ATTR_FUNCTION_GET(data_access, ffa_memory_access_permissions_t,
+ FFA_DATA_ACCESS_OFFSET, FFA_DATA_ACCESS_MASK)
+
+ATTR_FUNCTION_SET(instruction_access, ffa_memory_access_permissions_t,
+ FFA_INSTRUCTION_ACCESS_OFFSET, FFA_INSTRUCTION_ACCESS_MASK)
+ATTR_FUNCTION_GET(instruction_access, ffa_memory_access_permissions_t,
+ FFA_INSTRUCTION_ACCESS_OFFSET, FFA_INSTRUCTION_ACCESS_MASK)
+
+ATTR_FUNCTION_SET(memory_type, ffa_memory_attributes_t, FFA_MEMORY_TYPE_OFFSET,
+ FFA_MEMORY_TYPE_MASK)
+ATTR_FUNCTION_GET(memory_type, ffa_memory_attributes_t, FFA_MEMORY_TYPE_OFFSET,
+ FFA_MEMORY_TYPE_MASK)
+
+ATTR_FUNCTION_SET(memory_cacheability, ffa_memory_attributes_t,
+ FFA_MEMORY_CACHEABILITY_OFFSET, FFA_MEMORY_CACHEABILITY_MASK)
+ATTR_FUNCTION_GET(memory_cacheability, ffa_memory_attributes_t,
+ FFA_MEMORY_CACHEABILITY_OFFSET, FFA_MEMORY_CACHEABILITY_MASK)
+
+ATTR_FUNCTION_SET(memory_shareability, ffa_memory_attributes_t,
+ FFA_MEMORY_SHAREABILITY_OFFSET, FFA_MEMORY_SHAREABILITY_MASK)
+ATTR_FUNCTION_GET(memory_shareability, ffa_memory_attributes_t,
+ FFA_MEMORY_SHAREABILITY_OFFSET, FFA_MEMORY_SHAREABILITY_MASK)
+
+#define FFA_MEMORY_HANDLE_ALLOCATOR_MASK \
+ ((ffa_memory_handle_t)(UINT64_C(1) << 63))
+#define FFA_MEMORY_HANDLE_ALLOCATOR_HYPERVISOR \
+ ((ffa_memory_handle_t)(UINT64_C(1) << 63))
+#define FFA_MEMORY_HANDLE_INVALID (~UINT64_C(0))
+
+/**
+ * A set of contiguous pages which is part of a memory region. This corresponds
+ * to table "Constituent memory region descriptor" of the FFA 1.0 specification.
+ */
+struct ffa_memory_region_constituent {
+ /**
+ * The base IPA of the constituent memory region, aligned to 4 kiB page
+ * size granularity.
+ */
+ void *address;
+ /** The number of 4 kiB pages in the constituent memory region. */
+ uint32_t page_count;
+ /** Reserved field, must be 0. */
+ uint32_t reserved;
+};
+
+/**
+ * A set of pages comprising a memory region. This corresponds to table
+ * "Composite memory region descriptor" of the FFA 1.0 specification.
*/
+struct ffa_composite_memory_region {
+ /**
+ * The total number of 4 kiB pages included in this memory region. This
+ * must be equal to the sum of page counts specified in each
+ * `ffa_memory_region_constituent`.
+ */
+ uint32_t page_count;
+ /**
+ * The number of constituents (`ffa_memory_region_constituent`)
+ * included in this memory region range.
+ */
+ uint32_t constituent_count;
+ /** Reserved field, must be 0. */
+ uint64_t reserved_0;
+ /** An array of `constituent_count` memory region constituents. */
+ struct ffa_memory_region_constituent constituents[];
+};
+
+/**
+ * This corresponds to table "Memory access permissions descriptor" of the FFA
+ * 1.0 specification.
+ */
+struct ffa_memory_region_attributes {
+ /** The ID of the VM to which the memory is being given or shared. */
+ ffa_vm_id_t receiver;
+ /**
+ * The permissions with which the memory region should be mapped in the
+ * receiver's page table.
+ */
+ ffa_memory_access_permissions_t permissions;
+ /**
+ * Flags used during FFA_MEM_RETRIEVE_REQ and FFA_MEM_RETRIEVE_RESP
+ * for memory regions with multiple borrowers.
+ */
+ ffa_memory_receiver_flags_t flags;
+};
+
+/** Flags to control the behaviour of a memory sharing transaction. */
+typedef uint32_t ffa_memory_region_flags_t;
+
+/**
+ * Clear memory region contents after unmapping it from the sender and before
+ * mapping it for any receiver.
+ */
+#define FFA_MEMORY_REGION_FLAG_CLEAR 0x1U
+
+/**
+ * Whether the hypervisor may time slice the memory sharing or retrieval
+ * operation.
+ */
+#define FFA_MEMORY_REGION_FLAG_TIME_SLICE 0x2U
+
+/**
+ * Whether the hypervisor should clear the memory region after the receiver
+ * relinquishes it or is aborted.
+ */
+#define FFA_MEMORY_REGION_FLAG_CLEAR_RELINQUISH 0x4U
+
+#define FFA_MEMORY_REGION_TRANSACTION_TYPE_MASK ((0x3U) << 3)
+#define FFA_MEMORY_REGION_TRANSACTION_TYPE_UNSPECIFIED ((0x0U) << 3)
+#define FFA_MEMORY_REGION_TRANSACTION_TYPE_SHARE ((0x1U) << 3)
+#define FFA_MEMORY_REGION_TRANSACTION_TYPE_LEND ((0x2U) << 3)
+#define FFA_MEMORY_REGION_TRANSACTION_TYPE_DONATE ((0x3U) << 3)
+
+/**
+ * This corresponds to table "Endpoint memory access descriptor" of the FFA 1.0
+ * specification.
+ */
+struct ffa_memory_access {
+ struct ffa_memory_region_attributes receiver_permissions;
+ /**
+ * Offset in bytes from the start of the outer `ffa_memory_region` to
+ * an `ffa_composite_memory_region` struct.
+ */
+ uint32_t composite_memory_region_offset;
+ uint64_t reserved_0;
+};
+
+/**
+ * Information about a set of pages which are being shared. This corresponds to
+ * table "Lend, donate or share memory transaction descriptor" of the FFA
+ * 1.0 specification. Note that it is also used for retrieve requests and
+ * responses.
+ */
+struct ffa_memory_region {
+ /**
+ * The ID of the VM which originally sent the memory region, i.e. the
+ * owner.
+ */
+ ffa_vm_id_t sender;
+ ffa_memory_attributes_t attributes;
+ /** Reserved field, must be 0. */
+ uint8_t reserved_0;
+ /** Flags to control behaviour of the transaction. */
+ ffa_memory_region_flags_t flags;
+ ffa_memory_handle_t handle;
+ /**
+ * An implementation defined value associated with the receiver and the
+ * memory region.
+ */
+ uint64_t tag;
+ /** Reserved field, must be 0. */
+ uint32_t reserved_1;
+ /**
+ * The number of `ffa_memory_access` entries included in this
+ * transaction.
+ */
+ uint32_t receiver_count;
+ /**
+ * An array of `attribute_count` endpoint memory access descriptors.
+ * Each one specifies a memory region offset, an endpoint and the
+ * attributes with which this memory region should be mapped in that
+ * endpoint's page table.
+ */
+ struct ffa_memory_access receivers[];
+};
+
+/**
+ * Descriptor used for FFA_MEM_RELINQUISH requests. This corresponds to table
+ * "Descriptor to relinquish a memory region" of the FFA 1.0 specification.
+ */
+struct ffa_mem_relinquish {
+ ffa_memory_handle_t handle;
+ ffa_memory_region_flags_t flags;
+ uint32_t endpoint_count;
+ ffa_vm_id_t endpoints[];
+};
+
+static inline ffa_memory_handle_t ffa_assemble_handle(uint32_t h1, uint32_t h2)
+{
+ return (uint64_t)h1 | (uint64_t)h2 << 32;
+}
+
+static inline ffa_memory_handle_t ffa_mem_success_handle(smc_ret_values r)
+{
+ return ffa_assemble_handle(r.ret2, r.ret3);
+}
+
+/**
+ * Gets the `ffa_composite_memory_region` for the given receiver from an
+ * `ffa_memory_region`, or NULL if it is not valid.
+ */
+static inline struct ffa_composite_memory_region *
+ffa_memory_region_get_composite(struct ffa_memory_region *memory_region,
+ uint32_t receiver_index)
+{
+ uint32_t offset = memory_region->receivers[receiver_index]
+ .composite_memory_region_offset;
+
+ if (offset == 0) {
+ return NULL;
+ }
+
+ return (struct ffa_composite_memory_region *)((uint8_t *)memory_region +
+ offset);
+}
+
+static inline uint32_t ffa_mem_relinquish_init(
+ struct ffa_mem_relinquish *relinquish_request,
+ ffa_memory_handle_t handle, ffa_memory_region_flags_t flags,
+ ffa_vm_id_t sender)
+{
+ relinquish_request->handle = handle;
+ relinquish_request->flags = flags;
+ relinquish_request->endpoint_count = 1;
+ relinquish_request->endpoints[0] = sender;
+ return sizeof(struct ffa_mem_relinquish) + sizeof(ffa_vm_id_t);
+}
+
+uint32_t ffa_memory_retrieve_request_init(
+ struct ffa_memory_region *memory_region, ffa_memory_handle_t handle,
+ ffa_vm_id_t sender, ffa_vm_id_t receiver, uint32_t tag,
+ ffa_memory_region_flags_t flags, enum ffa_data_access data_access,
+ enum ffa_instruction_access instruction_access,
+ enum ffa_memory_type type, enum ffa_memory_cacheability cacheability,
+ enum ffa_memory_shareability shareability);
+
+uint32_t ffa_memory_region_init(
+ struct ffa_memory_region *memory_region, size_t memory_region_max_size,
+ ffa_vm_id_t sender, ffa_vm_id_t receiver,
+ const struct ffa_memory_region_constituent constituents[],
+ uint32_t constituent_count, uint32_t tag,
+ ffa_memory_region_flags_t flags, enum ffa_data_access data_access,
+ enum ffa_instruction_access instruction_access,
+ enum ffa_memory_type type, enum ffa_memory_cacheability cacheability,
+ enum ffa_memory_shareability shareability, uint32_t *total_length,
+ uint32_t *fragment_length);
+
bool check_spmc_execution_level(void);
smc_ret_values ffa_msg_send_direct_req(uint32_t source_id, uint32_t dest_id, uint32_t message);
smc_ret_values ffa_msg_send_direct_req64(uint32_t source_id, uint32_t dest_id, uint64_t message);
+smc_ret_values ffa_msg_send_direct_req64_5args(uint32_t source_id, uint32_t dest_id,
+ uint64_t arg0, uint64_t arg1,
+ uint64_t arg2, uint64_t arg3,
+ uint64_t arg4);
+
smc_ret_values ffa_run(uint32_t dest_id, uint32_t vcpu_id);
smc_ret_values ffa_version(uint32_t input_version);
smc_ret_values ffa_id_get(void);
@@ -62,6 +380,17 @@ smc_ret_values ffa_partition_info_get(const uint32_t uuid[4]);
smc_ret_values ffa_rx_release(void);
smc_ret_values ffa_rxtx_map(uintptr_t send, uintptr_t recv, uint32_t pages);
+smc_ret_values ffa_mem_donate(uint32_t descriptor_length,
+ uint32_t fragment_length);
+smc_ret_values ffa_mem_lend(uint32_t descriptor_length,
+ uint32_t fragment_length);
+smc_ret_values ffa_mem_share(uint32_t descriptor_length,
+ uint32_t fragment_length);
+smc_ret_values ffa_mem_retrieve_req(uint32_t descriptor_length,
+ uint32_t fragment_length);
+smc_ret_values ffa_mem_relinquish(void);
+smc_ret_values ffa_mem_reclaim(uint64_t handle, uint32_t flags);
+
#endif /* __ASSEMBLY__ */
#endif /* FFA_HELPERS_H */
diff --git a/spm/cactus/cactus.ld.S b/spm/cactus/cactus.ld.S
index 30ad0da7..11b28ba9 100644
--- a/spm/cactus/cactus.ld.S
+++ b/spm/cactus/cactus.ld.S
@@ -72,6 +72,7 @@ SECTIONS
__BSS_START__ = .;
*(SORT_BY_ALIGNMENT(.bss*))
*(COMMON)
+ *(xlat_table*)
. = NEXT(PAGE_SIZE);
__BSS_END__ = .;
}
diff --git a/spm/cactus/cactus.mk b/spm/cactus/cactus.mk
index 779fd38a..4b3f0bd3 100644
--- a/spm/cactus/cactus.mk
+++ b/spm/cactus/cactus.mk
@@ -74,6 +74,7 @@ $(eval $(call add_define,CACTUS_DEFINES,FVP_MAX_CPUS_PER_CLUSTER))
$(eval $(call add_define,CACTUS_DEFINES,FVP_MAX_PE_PER_CPU))
$(eval $(call add_define,CACTUS_DEFINES,LOG_LEVEL))
$(eval $(call add_define,CACTUS_DEFINES,PLAT_${PLAT}))
+$(eval $(call add_define,CACTUS_DEFINES,PLAT_XLAT_TABLES_DYNAMIC))
$(CACTUS_DTB) : $(BUILD_PLAT)/cactus $(BUILD_PLAT)/cactus/cactus.elf
$(CACTUS_DTB) : $(CACTUS_DTS)
diff --git a/spm/cactus/cactus_def.h b/spm/cactus/cactus_def.h
index 83be35f7..190f0631 100644
--- a/spm/cactus/cactus_def.h
+++ b/spm/cactus/cactus_def.h
@@ -36,12 +36,4 @@
#define get_sp_tx_start(sp_id) (CACTUS_TX_BASE + (((sp_id & 0x7FFFU) - 1U) * CACTUS_RX_TX_SIZE))
#define get_sp_tx_end(sp_id) (CACTUS_TX_BASE + (((sp_id & 0x7FFFU) - 1U) * CACTUS_RX_TX_SIZE) + PAGE_SIZE)
-/*
- * UUID of secure partition as defined in the respective manifests.
- */
-#define PRIMARY_UUID {0xb4b5671e, 0x4a904fe1, 0xb81ffb13, 0xdae1dacb}
-#define SECONDARY_UUID {0xd1582309, 0xf02347b9, 0x827c4464, 0xf5578fc8}
-#define TERTIARY_UUID {0x79b55c73, 0x1d8c44b9, 0x859361e1, 0x770ad8d2}
-
-
#endif /* CACTUS_DEF_H */
diff --git a/spm/cactus/cactus_ffa_tests.c b/spm/cactus/cactus_ffa_tests.c
index 28555afd..a49d6657 100644
--- a/spm/cactus/cactus_ffa_tests.c
+++ b/spm/cactus/cactus_ffa_tests.c
@@ -8,9 +8,13 @@
#include <errno.h>
#include <cactus_platform_def.h>
#include <cactus_def.h>
+#include <ffa_endpoints.h>
#include <ffa_helpers.h>
#include <sp_helpers.h>
+#include <lib/libc/string.h>
+#include <lib/xlat_tables/xlat_tables_v2.h>
+
/* FFA version test helpers */
#define FFA_MAJOR 1U
#define FFA_MINOR 0U
@@ -189,6 +193,161 @@ void ffa_version_test(void)
announce_test_end(test_ffa_version);
}
+bool ffa_memory_retrieve_test(struct mailbox_buffers *mb,
+ struct ffa_memory_region *retrieved,
+ uint64_t handle, ffa_vm_id_t sender,
+ ffa_vm_id_t receiver, uint32_t mem_func)
+{
+ smc_ret_values ret;
+ uint32_t fragment_size;
+ uint32_t total_size;
+ uint32_t descriptor_size;
+
+ if (retrieved == NULL || mb == NULL) {
+ ERROR("Invalid parameters!\n");
+ return false;
+ }
+
+
+ /*
+ * TODO: Revise shareability attribute in function call
+ * below.
+ * https://lists.trustedfirmware.org/pipermail/hafnium/2020-June/000023.html
+ */
+ descriptor_size = ffa_memory_retrieve_request_init(
+ mb->send, handle, sender, receiver, 0, 0,
+ FFA_DATA_ACCESS_RW,
+ FFA_INSTRUCTION_ACCESS_NX,
+ FFA_MEMORY_NORMAL_MEM,
+ FFA_MEMORY_CACHE_WRITE_BACK,
+ FFA_MEMORY_OUTER_SHAREABLE);
+
+ ret = ffa_mem_retrieve_req(descriptor_size, descriptor_size);
+
+ if (ret.ret0 != FFA_MEM_RETRIEVE_RESP) {
+ ERROR("Couldn't retrieve the memory page!\n");
+ return false;
+ }
+
+ /*
+ * Following total_size and fragment_size are useful to keep track
+ * of the state of transaction. When the sum of all fragment_size of all
+ * fragments is equal to total_size, the memory transaction has been
+ * completed.
+ * This is a simple test with only one segment. As such, upon
+ * successful ffa_mem_retrieve_req, total_size must be equal to
+ * fragment_size.
+ */
+ total_size = ret.ret1;
+ fragment_size = ret.ret2;
+
+ if (total_size != fragment_size) {
+ ERROR("Only expect one memory segment to be sent!\n");
+ return false;
+ }
+
+ if (fragment_size > PAGE_SIZE) {
+ ERROR("Fragment should be smaller than RX buffer!\n");
+ return false;
+ }
+
+ memcpy((void *)retrieved, mb->recv, fragment_size);
+
+ if (ffa_rx_release().ret0 != FFA_SUCCESS_SMC32) {
+ ERROR("Failed to release Rx buffer!\n");
+ return false;
+ }
+
+ if (retrieved->receiver_count != 1) {
+ VERBOSE("This memory has been shared with multiple"
+ " receivers!\n");
+ }
+
+ NOTICE("Memory Retrieved!\n");
+
+ return true;
+}
+
+bool ffa_memory_relinquish_test(struct ffa_mem_relinquish *m,
+ uint64_t handle,
+ ffa_vm_id_t id)
+{
+ ffa_mem_relinquish_init(m, handle, 0, id);
+
+ if (ffa_mem_relinquish().ret0 != FFA_SUCCESS_SMC32) {
+ ERROR("%s failed to relinquish memory!\n", __func__);
+ return false;
+ }
+
+ NOTICE("Memory Relinquished!\n");
+ return true;
+}
+
+void ffa_memory_management_test(struct mailbox_buffers *mb, ffa_vm_id_t vm_id,
+ ffa_vm_id_t sender, uint32_t mem_func,
+ uint64_t handle)
+{
+ const char *test_ffa = "Memory Management";
+ struct ffa_memory_region m;
+ struct ffa_composite_memory_region *composite;
+ int ret;
+ unsigned int mem_attrs;
+ uint32_t *ptr;
+
+ announce_test_section_start(test_ffa);
+
+ expect(ffa_memory_retrieve_test(
+ mb, &m, handle, sender, vm_id, mem_func),
+ true);
+
+ composite = ffa_memory_region_get_composite(&m, 0);
+
+ NOTICE("Address: %p; page_count: %x %x\n",
+ composite->constituents[0].address,
+ composite->constituents[0].page_count, PAGE_SIZE);
+
+ /* This test is only concerned with RW permissions. */
+ expect(ffa_get_data_access_attr(
+ m.receivers[0].receiver_permissions.permissions),
+ FFA_DATA_ACCESS_RW);
+
+ mem_attrs = MT_RW_DATA | MT_NS | MT_EXECUTE_NEVER;
+
+ ret = mmap_add_dynamic_region(
+ (uint64_t)composite->constituents[0].address,
+ (uint64_t)composite->constituents[0].address,
+ composite->constituents[0].page_count * PAGE_SIZE,
+ mem_attrs);
+ expect(ret, 0);
+
+ VERBOSE("Memory has been mapped\n");
+
+ ptr = (uint32_t *) composite->constituents[0].address;
+
+ /* Write mem_func to retrieved memory region for validation purposes. */
+ VERBOSE("Writing: %x\n", mem_func);
+ for (unsigned int i = 0U; i < 5U; i++)
+ ptr[i] = mem_func;
+
+ /*
+ * A FFA_MEM_DONATE changes the ownership of the page, as such no
+ * relinquish is needed.
+ */
+ if (mem_func != FFA_MEM_DONATE_SMC32) {
+ ret = mmap_remove_dynamic_region(
+ (uint64_t)composite->constituents[0].address,
+ composite->constituents[0].page_count * PAGE_SIZE);
+ expect(ret, 0);
+
+ expect(ffa_memory_relinquish_test(
+ (struct ffa_mem_relinquish *)mb->send,
+ m.handle, vm_id),
+ true);
+ }
+
+ announce_test_section_end(test_ffa);
+}
+
void ffa_tests(struct mailbox_buffers *mb)
{
const char *test_ffa = "FFA Interfaces";
diff --git a/spm/cactus/cactus_main.c b/spm/cactus/cactus_main.c
index acbe2af6..7c70d67b 100644
--- a/spm/cactus/cactus_main.c
+++ b/spm/cactus/cactus_main.c
@@ -24,6 +24,8 @@
#include <plat_arm.h>
#include <platform_def.h>
+#include <cactus_test_cmds.h>
+
/* Host machine information injected by the build system in the ELF file. */
extern const char build_message[];
extern const char version_string[];
@@ -36,21 +38,25 @@ extern const char version_string[];
* but rather through Hafnium print hypercall.
*
*/
-static void __dead2 message_loop(ffa_vm_id_t vm_id)
+static void __dead2 message_loop(ffa_vm_id_t vm_id, struct mailbox_buffers *mb)
{
smc_ret_values ffa_ret;
uint32_t sp_response;
+ ffa_vm_id_t source;
/*
- * This initial wait call is necessary to inform SPMD that
- * SP initialization has completed. It blocks until receiving
- * a direct message request.
- */
+ * This initial wait call is necessary to inform SPMD that
+ * SP initialization has completed. It blocks until receiving
+ * a direct message request.
+ */
+
ffa_ret = ffa_msg_wait();
for (;;) {
+ VERBOSE("Woke up with func id: %lx\n", ffa_ret.ret0);
- if (ffa_ret.ret0 != FFA_MSG_SEND_DIRECT_REQ_SMC32) {
+ if (ffa_ret.ret0 != FFA_MSG_SEND_DIRECT_REQ_SMC32 &&
+ ffa_ret.ret0 != FFA_MSG_SEND_DIRECT_REQ_SMC64) {
ERROR("%s(%u) unknown func id 0x%lx\n",
__func__, vm_id, ffa_ret.ret0);
break;
@@ -61,24 +67,46 @@ static void __dead2 message_loop(ffa_vm_id_t vm_id)
__func__, vm_id, ffa_ret.ret1);
break;
}
+ source = ffa_ret.ret2;
- if (ffa_ret.ret2 != HYP_ID) {
+ if (source != HYP_ID) {
ERROR("%s(%u) invalid hyp id 0x%lx\n",
__func__, vm_id, ffa_ret.ret2);
break;
}
- /*
- * For the sake of testing, add the vm id to the
- * received message.
- */
- sp_response = ffa_ret.ret3 | vm_id;
-
- /*
- * Send a response through direct messaging then block
- * until receiving a new message request.
- */
- ffa_ret = ffa_msg_send_direct_resp(vm_id, HYP_ID, sp_response);
+ PRINT_CMD(ffa_ret);
+
+ switch (CACTUS_GET_CMD(ffa_ret)) {
+ case FFA_MEM_SHARE_SMC32:
+ case FFA_MEM_LEND_SMC32:
+ case FFA_MEM_DONATE_SMC32:
+ ffa_memory_management_test(
+ mb, vm_id, source,
+ CACTUS_GET_CMD(ffa_ret),
+ CACTUS_MEM_SEND_GET_HANDLE(ffa_ret));
+
+ /*
+ * If execution gets to this point means all operations
+ * with memory retrieval went well, as such replying
+ */
+ ffa_ret = CACTUS_SUCCESS_RESP(vm_id, source);
+ break;
+ default:
+ /*
+ * Currently direct message test is handled here.
+ * TODO: create a case within the switch case
+ * For the sake of testing, add the vm id to the
+ * received message.
+ */
+ NOTICE("Replying to Direct MSG test\n");
+ sp_response = ffa_ret.ret3 | vm_id;
+ ffa_ret = ffa_msg_send_direct_resp(vm_id,
+ HYP_ID,
+ sp_response);
+
+ break;
+ }
}
panic();
@@ -227,7 +255,7 @@ void __dead2 cactus_main(void)
ffa_tests(&mb);
/* End up to message loop */
- message_loop(ffa_id);
+ message_loop(ffa_id, &mb);
/* Not reached */
}
diff --git a/spm/cactus/cactus_test_cmds.h b/spm/cactus/cactus_test_cmds.h
new file mode 100644
index 00000000..6329b6da
--- /dev/null
+++ b/spm/cactus/cactus_test_cmds.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CACTUS_TEST_CMDS
+#define CACTUS_TEST_CMDS
+
+#include <debug.h>
+#include <ffa_helpers.h>
+
+/**
+ * Success and error return to be sent over a msg response.
+ */
+#define CACTUS_SUCCESS 0
+#define CACTUS_ERROR -1
+
+/**
+ * Get command from struct smc_ret_values.
+ */
+#define CACTUS_GET_CMD(smc_ret) smc_ret.ret3
+
+/**
+ * Template for commands to be sent to CACTUS partitions over direct
+ * messages interfaces.
+ */
+#define CACTUS_SEND_CMD(source, dest, cmd, val0, val1, val2, val3) \
+ ffa_msg_send_direct_req64_5args(source, dest, cmd, \
+ val0, val1, val2, val3)
+
+#define PRINT_CMD(smc_ret) \
+ VERBOSE("cmd %lx; args: %lx, %lx, %lx, %lx\n", \
+ smc_ret.ret3, smc_ret.ret4, smc_ret.ret5, \
+ smc_ret.ret6, smc_ret.ret7)
+
+/**
+ * Command to notify cactus of a memory management operation. The cmd value
+ * should be the memory management smc function id.
+ */
+#define CACTUS_MEM_SEND_CMD(source, dest, mem_func, handle) \
+ CACTUS_SEND_CMD(source, dest, mem_func, handle, 0, 0, 0)
+
+#define CACTUS_MEM_SEND_GET_HANDLE(smc_ret) smc_ret.ret4
+
+/**
+ * Template for responses to CACTUS commands.
+ */
+#define CACTUS_RESPONSE(source, dest, response) \
+ ffa_msg_send_direct_resp(source, dest, response)
+
+#define CACTUS_SUCCESS_RESP(source, dest) \
+ CACTUS_RESPONSE(source, dest, CACTUS_SUCCESS)
+
+#define CACTUS_ERROR_RESP(source, dest) \
+ CACTUS_RESPONSE(source, dest, CACTUS_ERROR)
+
+#define CACTUS_GET_RESPONSE(smc_ret) smc_ret.ret3
+
+#endif
diff --git a/spm/cactus/cactus_tests.h b/spm/cactus/cactus_tests.h
index 2e13a6f9..fd229bf5 100644
--- a/spm/cactus/cactus_tests.h
+++ b/spm/cactus/cactus_tests.h
@@ -16,6 +16,10 @@
/*
* Test to FFA interfaces.
*/
+void ffa_memory_management_test(struct mailbox_buffers *mb, ffa_vm_id_t vm_id,
+ ffa_vm_id_t sender, uint32_t mem_func,
+ uint64_t handle);
+
void ffa_tests(struct mailbox_buffers *mb);
/*
diff --git a/tftf/tests/runtime_services/secure_service/ffa_helpers.c b/tftf/tests/runtime_services/secure_service/ffa_helpers.c
index bda47c98..8ee4ebc7 100644
--- a/tftf/tests/runtime_services/secure_service/ffa_helpers.c
+++ b/tftf/tests/runtime_services/secure_service/ffa_helpers.c
@@ -6,14 +6,10 @@
#include <debug.h>
#include <smccc.h>
+#include <ffa_endpoints.h>
#include <ffa_helpers.h>
#include <ffa_svc.h>
-#define OPTEE_FFA_GET_API_VERSION (0)
-#define OPTEE_FFA_GET_OS_VERSION (1)
-#define OPTEE_FFA_GET_OS_VERSION_MAJOR (3)
-#define OPTEE_FFA_GET_OS_VERSION_MINOR (8)
-
/*-----------------------------------------------------------------------------
* FFA_RUN
*
@@ -86,7 +82,7 @@ smc_ret_values ffa_msg_send_direct_req(uint32_t source_id, uint32_t dest_id,
message, 0, 0, 0, 0);
}
-static smc_ret_values __ffa_msg_send_direct_req64_5(uint32_t source_id,
+smc_ret_values ffa_msg_send_direct_req64_5args(uint32_t source_id,
uint32_t dest_id,
uint64_t arg0,
uint64_t arg1,
@@ -108,7 +104,7 @@ static smc_ret_values __ffa_msg_send_direct_req64_5(uint32_t source_id,
smc_ret_values ffa_msg_send_direct_req64(uint32_t source_id, uint32_t dest_id,
uint64_t message)
{
- return __ffa_msg_send_direct_req64_5(source_id, dest_id,
+ return ffa_msg_send_direct_req64_5args(source_id, dest_id,
message, 0, 0, 0, 0);
}
@@ -155,6 +151,165 @@ bool check_spmc_execution_level(void)
return (is_optee_spmc_criteria == 2U);
}
+/**
+ * Initialises the header of the given `ffa_memory_region`, not including the
+ * composite memory region offset.
+ */
+static void ffa_memory_region_init_header(
+ struct ffa_memory_region *memory_region, ffa_vm_id_t sender,
+ ffa_memory_attributes_t attributes, ffa_memory_region_flags_t flags,
+ ffa_memory_handle_t handle, uint32_t tag, ffa_vm_id_t receiver,
+ ffa_memory_access_permissions_t permissions)
+{
+ memory_region->sender = sender;
+ memory_region->attributes = attributes;
+ memory_region->reserved_0 = 0;
+ memory_region->flags = flags;
+ memory_region->handle = handle;
+ memory_region->tag = tag;
+ memory_region->reserved_1 = 0;
+ memory_region->receiver_count = 1;
+ memory_region->receivers[0].receiver_permissions.receiver = receiver;
+ memory_region->receivers[0].receiver_permissions.permissions =
+ permissions;
+ memory_region->receivers[0].receiver_permissions.flags = 0;
+ memory_region->receivers[0].reserved_0 = 0;
+}
+
+/**
+ * Initialises the given `ffa_memory_region` and copies as many as possible of
+ * the given constituents to it.
+ *
+ * Returns the number of constituents remaining which wouldn't fit, and (via
+ * return parameters) the size in bytes of the first fragment of data copied to
+ * `memory_region` (attributes, constituents and memory region header size), and
+ * the total size of the memory sharing message including all constituents.
+ */
+uint32_t ffa_memory_region_init(
+ struct ffa_memory_region *memory_region, size_t memory_region_max_size,
+ ffa_vm_id_t sender, ffa_vm_id_t receiver,
+ const struct ffa_memory_region_constituent constituents[],
+ uint32_t constituent_count, uint32_t tag,
+ ffa_memory_region_flags_t flags, enum ffa_data_access data_access,
+ enum ffa_instruction_access instruction_access,
+ enum ffa_memory_type type, enum ffa_memory_cacheability cacheability,
+ enum ffa_memory_shareability shareability, uint32_t *total_length,
+ uint32_t *fragment_length)
+{
+ ffa_memory_access_permissions_t permissions = 0;
+ ffa_memory_attributes_t attributes = 0;
+ struct ffa_composite_memory_region *composite_memory_region;
+ uint32_t fragment_max_constituents;
+ uint32_t count_to_copy;
+ uint32_t i;
+ uint32_t constituents_offset;
+
+ /* Set memory region's permissions. */
+ ffa_set_data_access_attr(&permissions, data_access);
+ ffa_set_instruction_access_attr(&permissions, instruction_access);
+
+ /* Set memory region's page attributes. */
+ ffa_set_memory_type_attr(&attributes, type);
+ ffa_set_memory_cacheability_attr(&attributes, cacheability);
+ ffa_set_memory_shareability_attr(&attributes, shareability);
+
+ ffa_memory_region_init_header(memory_region, sender, attributes, flags,
+ 0, tag, receiver, permissions);
+ /*
+ * Note that `sizeof(struct_ffa_memory_region)` and `sizeof(struct
+ * ffa_memory_access)` must both be multiples of 16 (as verified by the
+ * asserts in `ffa_memory.c`, so it is guaranteed that the offset we
+ * calculate here is aligned to a 64-bit boundary and so 64-bit values
+ * can be copied without alignment faults.
+ */
+ memory_region->receivers[0].composite_memory_region_offset =
+ sizeof(struct ffa_memory_region) +
+ memory_region->receiver_count *
+ sizeof(struct ffa_memory_access);
+
+ composite_memory_region =
+ ffa_memory_region_get_composite(memory_region, 0);
+ composite_memory_region->page_count = 0;
+ composite_memory_region->constituent_count = constituent_count;
+ composite_memory_region->reserved_0 = 0;
+
+ constituents_offset =
+ memory_region->receivers[0].composite_memory_region_offset +
+ sizeof(struct ffa_composite_memory_region);
+ fragment_max_constituents =
+ (memory_region_max_size - constituents_offset) /
+ sizeof(struct ffa_memory_region_constituent);
+
+ count_to_copy = constituent_count;
+ if (count_to_copy > fragment_max_constituents) {
+ count_to_copy = fragment_max_constituents;
+ }
+
+ for (i = 0; i < constituent_count; ++i) {
+ if (i < count_to_copy) {
+ composite_memory_region->constituents[i] =
+ constituents[i];
+ }
+ composite_memory_region->page_count +=
+ constituents[i].page_count;
+ }
+
+ if (total_length != NULL) {
+ *total_length =
+ constituents_offset +
+ composite_memory_region->constituent_count *
+ sizeof(struct ffa_memory_region_constituent);
+ }
+ if (fragment_length != NULL) {
+ *fragment_length =
+ constituents_offset +
+ count_to_copy *
+ sizeof(struct ffa_memory_region_constituent);
+ }
+
+ return composite_memory_region->constituent_count - count_to_copy;
+}
+
+/**
+ * Initialises the given `ffa_memory_region` to be used for an
+ * `FFA_MEM_RETRIEVE_REQ` by the receiver of a memory transaction.
+ *
+ * Returns the size of the message written.
+ */
+uint32_t ffa_memory_retrieve_request_init(
+ struct ffa_memory_region *memory_region, ffa_memory_handle_t handle,
+ ffa_vm_id_t sender, ffa_vm_id_t receiver, uint32_t tag,
+ ffa_memory_region_flags_t flags, enum ffa_data_access data_access,
+ enum ffa_instruction_access instruction_access,
+ enum ffa_memory_type type, enum ffa_memory_cacheability cacheability,
+ enum ffa_memory_shareability shareability)
+{
+ ffa_memory_access_permissions_t permissions = 0;
+ ffa_memory_attributes_t attributes = 0;
+
+ /* Set memory region's permissions. */
+ ffa_set_data_access_attr(&permissions, data_access);
+ ffa_set_instruction_access_attr(&permissions, instruction_access);
+
+ /* Set memory region's page attributes. */
+ ffa_set_memory_type_attr(&attributes, type);
+ ffa_set_memory_cacheability_attr(&attributes, cacheability);
+ ffa_set_memory_shareability_attr(&attributes, shareability);
+
+ ffa_memory_region_init_header(memory_region, sender, attributes, flags,
+ handle, tag, receiver, permissions);
+ /*
+ * Offset 0 in this case means that the hypervisor should allocate the
+ * address ranges. This is the only configuration supported by Hafnium,
+ * as it enforces 1:1 mappings in the stage 2 page tables.
+ */
+ memory_region->receivers[0].composite_memory_region_offset = 0;
+ memory_region->receivers[0].reserved_0 = 0;
+
+ return sizeof(struct ffa_memory_region) +
+ memory_region->receiver_count * sizeof(struct ffa_memory_access);
+}
+
/*
* FFA Version ABI helper.
* Version fields:
@@ -260,3 +415,89 @@ smc_ret_values ffa_rxtx_map(uintptr_t send, uintptr_t recv, uint32_t pages)
return tftf_smc(&args);
}
+
+/* Donate memory to another partition */
+smc_ret_values ffa_mem_donate(uint32_t descriptor_length,
+ uint32_t fragment_length)
+{
+ smc_args args = {
+ .fid = FFA_MEM_DONATE_SMC32,
+ .arg1 = descriptor_length,
+ .arg2 = fragment_length,
+ .arg3 = FFA_PARAM_MBZ,
+ .arg4 = FFA_PARAM_MBZ
+ };
+
+ return tftf_smc(&args);
+}
+
+/* Lend memory to another partition */
+smc_ret_values ffa_mem_lend(uint32_t descriptor_length,
+ uint32_t fragment_length)
+{
+ smc_args args = {
+ .fid = FFA_MEM_LEND_SMC32,
+ .arg1 = descriptor_length,
+ .arg2 = fragment_length,
+ .arg3 = FFA_PARAM_MBZ,
+ .arg4 = FFA_PARAM_MBZ
+ };
+
+ return tftf_smc(&args);
+}
+
+/* Share memory with another partition */
+smc_ret_values ffa_mem_share(uint32_t descriptor_length,
+ uint32_t fragment_length)
+{
+ smc_args args = {
+ .fid = FFA_MEM_SHARE_SMC32,
+ .arg1 = descriptor_length,
+ .arg2 = fragment_length,
+ .arg3 = FFA_PARAM_MBZ,
+ .arg4 = FFA_PARAM_MBZ
+ };
+
+ return tftf_smc(&args);
+}
+
+/* Retrieve memory shared by another partition */
+smc_ret_values ffa_mem_retrieve_req(uint32_t descriptor_length,
+ uint32_t fragment_length)
+{
+ smc_args args = {
+ .fid = FFA_MEM_RETRIEVE_REQ_SMC32,
+ .arg1 = descriptor_length,
+ .arg2 = fragment_length,
+ .arg3 = FFA_PARAM_MBZ,
+ .arg4 = FFA_PARAM_MBZ,
+ .arg5 = FFA_PARAM_MBZ,
+ .arg6 = FFA_PARAM_MBZ,
+ .arg7 = FFA_PARAM_MBZ
+ };
+
+ return tftf_smc(&args);
+}
+
+/* Relinquish access to memory region */
+smc_ret_values ffa_mem_relinquish(void)
+{
+ smc_args args = {
+ .fid = FFA_MEM_RELINQUISH,
+ };
+
+ return tftf_smc(&args);
+}
+
+/* Reclaim exclusive access to owned memory region */
+smc_ret_values ffa_mem_reclaim(uint64_t handle, uint32_t flags)
+{
+ smc_args args = {
+ .fid = FFA_MEM_RECLAIM,
+ .arg1 = (uint32_t) handle,
+ .arg2 = (uint32_t) (handle >> 32),
+ .arg3 = flags
+ };
+
+ return tftf_smc(&args);
+}
diff --git a/tftf/tests/runtime_services/secure_service/test_ffa_memory_sharing.c b/tftf/tests/runtime_services/secure_service/test_ffa_memory_sharing.c
new file mode 100644
index 00000000..0ae8a8d3
--- /dev/null
+++ b/tftf/tests/runtime_services/secure_service/test_ffa_memory_sharing.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <cactus_test_cmds.h>
+#include <debug.h>
+#include <ffa_endpoints.h>
+#include <ffa_helpers.h>
+#include <test_helpers.h>
+#include <tftf_lib.h>
+#include <xlat_tables_defs.h>
+
+#define MAILBOX_SIZE PAGE_SIZE
+
+#define SENDER HYP_ID
+#define RECEIVER SP_ID(1)
+
+/* Memory section to be sent over mem management ABIs */
+static __aligned(PAGE_SIZE) uint8_t share_page[PAGE_SIZE];
+
+static __aligned(PAGE_SIZE) uint8_t send_page[PAGE_SIZE];
+static __aligned(PAGE_SIZE) uint8_t recv_page[PAGE_SIZE];
+
+/* Within the same test the RXTX Buffers only need to be shared once */
+static bool rxtx_mapped;
+
+static struct mailbox_buffers mb = {
+ .recv = (void *)recv_page,
+ .send = (void *)send_page,
+ };
+
+static test_result_t test_memory_send_sp(uint32_t mem_func)
+{
+ smc_ret_values ret;
+ uint32_t remaining_constituent_count;
+ uint32_t total_length;
+ uint32_t fragment_length;
+ uint32_t sent_length;
+ ffa_memory_handle_t handle;
+ uint32_t *ptr;
+
+ /**********************************************************************
+ * Verify that FFA is there and that it has the correct version.
+ **********************************************************************/
+ SKIP_TEST_IF_FFA_VERSION_LESS_THAN(1, 0);
+
+ /**********************************************************************
+ * If OPTEE is SPMC skip this test.
+ **********************************************************************/
+ if (check_spmc_execution_level()) {
+ VERBOSE("OPTEE as SPMC at S-EL1. Skipping test!\n");
+ return TEST_RESULT_SKIPPED;
+ }
+
+ if (!rxtx_mapped) {
+ ret = ffa_rxtx_map((uintptr_t)mb.send, (uintptr_t)mb.recv, 1);
+
+ if (ret.ret0 != FFA_SUCCESS_SMC32) {
+ ERROR("ffa_rxtx_map failed (%lx)\n", ret.ret0);
+ return TEST_RESULT_FAIL;
+ }
+ rxtx_mapped = true;
+ }
+
+ /**********************************************************************
+ * Verify that cactus primary SP is deployed in the system.
+ **********************************************************************/
+ SKIP_TEST_IF_FFA_ENDPOINT_NOT_DEPLOYED(mb, PRIMARY_UUID);
+
+ struct ffa_memory_region_constituent constituents[] = {
+ {(void *)share_page, 1, 0}
+ };
+
+ const uint32_t constituents_count = sizeof(constituents) /
+ sizeof(struct ffa_memory_region_constituent);
+
+ enum ffa_data_access data_access = (mem_func == FFA_MEM_DONATE_SMC32) ?
+ FFA_DATA_ACCESS_NOT_SPECIFIED :
+ FFA_DATA_ACCESS_RW;
+
+ /*
+ * TODO: Revise shareability attribute in function call
+ * below.
+ * https://lists.trustedfirmware.org/pipermail/hafnium/2020-June/000023.html
+ */
+ remaining_constituent_count = ffa_memory_region_init(
+ mb.send, MAILBOX_SIZE, SENDER, RECEIVER, constituents,
+ constituents_count, 0, 0,
+ data_access,
+ FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED,
+ FFA_MEMORY_NORMAL_MEM,
+ FFA_MEMORY_CACHE_WRITE_BACK,
+ FFA_MEMORY_OUTER_SHAREABLE,
+ &total_length,
+ &fragment_length
+ );
+
+ switch (mem_func) {
+ case FFA_MEM_SHARE_SMC32:
+ ret = ffa_mem_share(total_length, fragment_length);
+ break;
+ case FFA_MEM_LEND_SMC32:
+ ret = ffa_mem_lend(total_length, fragment_length);
+ break;
+ case FFA_MEM_DONATE_SMC32:
+ ret = ffa_mem_donate(total_length, fragment_length);
+ break;
+ default:
+ NOTICE("TFTF - Invalid func id!\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ sent_length = fragment_length;
+
+ if (ret.ret0 != FFA_SUCCESS_SMC32) {
+ tftf_testcase_printf("Failed to send memory to SP %x.\n",
+ RECEIVER);
+ return TEST_RESULT_FAIL;
+ }
+
+ if (sent_length != total_length) {
+ tftf_testcase_printf("Sent and Total lengths must be equal!\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ if (remaining_constituent_count != 0) {
+ tftf_testcase_printf("Remaining constituent should be 0\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ handle = ffa_mem_success_handle(ret);
+
+ VERBOSE("TFTF - Handle: %llx\nTFTF - Address: %p\n",
+ handle, constituents[0].address);
+
+ ptr = (uint32_t *)constituents[0].address;
+
+ ret = CACTUS_MEM_SEND_CMD(SENDER, RECEIVER, mem_func, handle);
+
+ if (ret.ret0 != FFA_MSG_SEND_DIRECT_RESP_SMC32) {
+ ERROR("Failed to send message. error: %lx\n",
+ ret.ret2);
+ return TEST_RESULT_FAIL;
+ }
+
+ if (CACTUS_GET_RESPONSE(ret) != CACTUS_SUCCESS) {
+ tftf_testcase_printf("Failed memory send operation!\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ /*
+ * Print 5 words from the memory region to validate SP wrote to the
+ * memory region.
+ */
+ VERBOSE("TFTF - Memory contents after SP use:\n");
+ for (unsigned int i = 0U; i < 5U; i++)
+ VERBOSE(" %u: %x\n", i, ptr[i]);
+
+ /* To make the compiler happy in case it is not a verbose build */
+ if (LOG_LEVEL < LOG_LEVEL_VERBOSE)
+ (void)ptr;
+
+ if (mem_func != FFA_MEM_DONATE_SMC32 &&
+ ffa_mem_reclaim(handle, 0).ret0 == FFA_ERROR) {
+ tftf_testcase_printf("Couldn't reclaim memory\n");
+ return TEST_RESULT_FAIL;
+ }
+
+ return TEST_RESULT_SUCCESS;
+}
+
+test_result_t test_mem_share_sp(void)
+{
+ return test_memory_send_sp(FFA_MEM_SHARE_SMC32);
+}
+
+test_result_t test_mem_lend_sp(void)
+{
+ return test_memory_send_sp(FFA_MEM_LEND_SMC32);
+}
+
+test_result_t test_mem_donate_sp(void)
+{
+ return test_memory_send_sp(FFA_MEM_DONATE_SMC32);
+}
diff --git a/tftf/tests/tests-spm.mk b/tftf/tests/tests-spm.mk
index bef373fb..ee339b5a 100644
--- a/tftf/tests/tests-spm.mk
+++ b/tftf/tests/tests-spm.mk
@@ -10,4 +10,5 @@ TESTS_SOURCES += \
test_ffa_direct_messaging.c \
test_ffa_version.c \
test_ffa_features.c \
+ test_ffa_memory_sharing.c \
)
diff --git a/tftf/tests/tests-spm.xml b/tftf/tests/tests-spm.xml
index e2f29bfe..85001f22 100644
--- a/tftf/tests/tests-spm.xml
+++ b/tftf/tests/tests-spm.xml
@@ -30,6 +30,16 @@
</testsuite>
+ <testsuite name="FF-A Memory Sharing"
+ description="Test FF-A Memory Sharing ABIs" >
+ <testcase name="Lend Memory to Secure World"
+ function="test_mem_lend_sp" />
+ <testcase name="Share Memory with Secure World"
+ function="test_mem_share_sp" />
+ <testcase name="Donate Memory to Secure World"
+ function="test_mem_donate_sp"/>
+ </testsuite>
+
<testsuite name="PSA FF-A features"
description="Test FFA_FEATURES ABI" >
<testcase name="Test FFA_FEATURES"