fix(ff-a): memory sharing clear memory flag use
This patch insures the borrower of a memory region can request to have
the memory cleared in FFA_MEM_RETRIEVE_REQ call, only if memory sender
has set the equivalent flag in the memory transaction descriptor
upon calling either FFA_MEM_DONATE or FFA_MEM_LEND.
This is defined by the FF-A v1.1 Beta0 specification, in the table
10.22.
Change-Id: Ibf3e5cab9512c1bea3f83f939c0b7d1ae9cc74fd
Signed-off-by: J-Alves <joao.alves@arm.com>
diff --git a/src/ffa_memory.c b/src/ffa_memory.c
index 51a73ec..f51bbd8 100644
--- a/src/ffa_memory.c
+++ b/src/ffa_memory.c
@@ -2269,6 +2269,21 @@
}
/*
+ * If the borrower needs the memory to be cleared before mapping to its
+ * address space, the sender should have set the flag when calling
+ * FFA_MEM_LEND/FFA_MEM_DONATE, else return FFA_DENIED.
+ */
+ if ((retrieve_request->flags & FFA_MEMORY_REGION_FLAG_CLEAR) != 0U &&
+ (share_state->memory_region->flags &
+ FFA_MEMORY_REGION_FLAG_CLEAR) == 0U) {
+ dlog_verbose(
+ "Borrower needs memory cleared. Sender needs to set "
+ "flag for clearing memory.\n");
+ ret = ffa_error(FFA_DENIED);
+ goto out;
+ }
+
+ /*
* Check permissions from sender against permissions requested by
* receiver.
*/
diff --git a/test/vmapi/primary_with_secondaries/memory_sharing.c b/test/vmapi/primary_with_secondaries/memory_sharing.c
index 997f435..cfd6d38 100644
--- a/test/vmapi/primary_with_secondaries/memory_sharing.c
+++ b/test/vmapi/primary_with_secondaries/memory_sharing.c
@@ -1425,7 +1425,7 @@
uint8_t *ptr = pages;
struct ffa_value run_res;
- SERVICE_SELECT(SERVICE_VM1, "ffa_memory_share_fail", mb.send);
+ SERVICE_SELECT(SERVICE_VM1, "ffa_memory_share_fail_denied", mb.send);
/* Initialise the memory before giving it. */
memset_s(ptr, sizeof(pages), 'b', PAGE_SIZE);
@@ -2317,7 +2317,7 @@
{.address = (uint64_t)pages + PAGE_SIZE * 3, .page_count = 1},
};
- SERVICE_SELECT(SERVICE_VM1, "ffa_memory_share_fail", mb.send);
+ SERVICE_SELECT(SERVICE_VM1, "ffa_memory_share_fail_denied", mb.send);
/* Call FFA_MEM_SEND, setting FFA_DATA_ACCESS_RO. */
EXPECT_EQ(ffa_memory_region_init(
@@ -2353,3 +2353,78 @@
EXPECT_EQ(ffa_mem_reclaim(handle, 0).func, FFA_SUCCESS_32);
}
+
+/**
+ * A borrower can only request a memory region to be cleared on his end, if
+ * the sender has set the FFA_MEMORY_REGION_FLAG_CLEAR flag in the transaction
+ * descriptor. If the sender flag is clear, expect FFA_DENIED error.
+ */
+TEST(memory_sharing, ffa_validate_retrieve_req_clear_flag_if_sender_not_clear)
+{
+ struct ffa_value ret;
+ struct mailbox_buffers mb = set_up_mailbox();
+ uint32_t msg_size;
+ ffa_memory_handle_t handle;
+ struct ffa_value (*send_function[])(uint32_t, uint32_t) = {
+ ffa_mem_lend,
+ ffa_mem_donate,
+ };
+
+ struct ffa_memory_region_constituent constituents[] = {
+ {.address = (uint64_t)pages, .page_count = 2},
+ {.address = (uint64_t)pages + PAGE_SIZE * 3, .page_count = 1},
+ };
+
+ SERVICE_SELECT(SERVICE_VM1, "ffa_memory_share_fail_denied", mb.send);
+
+ for (uint32_t i = 0; i < ARRAY_SIZE(send_function); i++) {
+ /*
+ * Call FF-A memory send interface, not setting the
+ * FFA_MEMORY_REGION_FLAG_CLEAR.
+ */
+ EXPECT_EQ(ffa_memory_region_init(
+ mb.send, HF_MAILBOX_SIZE, HF_PRIMARY_VM_ID,
+ SERVICE_VM1, constituents,
+ ARRAY_SIZE(constituents), 0, 0,
+ /* Different args for lend and donate. */
+ send_function[i] == ffa_mem_lend
+ ? FFA_DATA_ACCESS_RW
+ : FFA_DATA_ACCESS_NOT_SPECIFIED,
+ FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED,
+ FFA_MEMORY_NORMAL_MEM,
+ FFA_MEMORY_CACHE_WRITE_BACK,
+ FFA_MEMORY_INNER_SHAREABLE, NULL, &msg_size),
+ 0);
+
+ ret = send_function[i](msg_size, msg_size);
+ EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+ handle = ffa_mem_success_handle(ret);
+
+ /*
+ * Prepare retrieve request with RW, and setting flag to clear
+ * memory. Should fail at the receiver's FFA_MEM_RETRIEVE_REQ
+ * call with FFA_DENIED.
+ */
+ msg_size = ffa_memory_retrieve_request_init(
+ mb.send, handle, HF_PRIMARY_VM_ID, SERVICE_VM1, 0,
+ FFA_MEMORY_REGION_FLAG_CLEAR, FFA_DATA_ACCESS_RW,
+ /* Different args for lend and donate. */
+ send_function[i] == ffa_mem_lend
+ ? FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED
+ : FFA_INSTRUCTION_ACCESS_NX,
+ FFA_MEMORY_NORMAL_MEM, FFA_MEMORY_CACHE_WRITE_BACK,
+ FFA_MEMORY_INNER_SHAREABLE);
+
+ EXPECT_LE(msg_size, HF_MAILBOX_SIZE);
+
+ EXPECT_EQ(
+ ffa_msg_send(HF_PRIMARY_VM_ID, SERVICE_VM1, msg_size, 0)
+ .func,
+ FFA_SUCCESS_32);
+
+ ffa_run(SERVICE_VM1, 0);
+
+ EXPECT_EQ(ffa_mem_reclaim(handle, 0).func, FFA_SUCCESS_32);
+ }
+}
diff --git a/test/vmapi/primary_with_secondaries/services/memory.c b/test/vmapi/primary_with_secondaries/services/memory.c
index b8f592c..5e25237 100644
--- a/test/vmapi/primary_with_secondaries/services/memory.c
+++ b/test/vmapi/primary_with_secondaries/services/memory.c
@@ -646,7 +646,7 @@
/**
* Attempt to retrieve a shared page but expect to fail with FFA_DENIED.
*/
-TEST_SERVICE(ffa_memory_share_fail)
+TEST_SERVICE(ffa_memory_share_fail_denied)
{
for (;;) {
void *recv_buf = SERVICE_RECV_BUFFER();