blob: d5322a3773043af263de82c33dc3234197b6f48b [file] [log] [blame]
/*
* Copyright 2018 The Hafnium Authors.
*
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/BSD-3-Clause.
*/
#include "ffa_mailbox.h"
#include "call.h"
#include "ffa_utils.h"
static uint8_t send_page[FFA_PAGE_SIZE] __attribute__((aligned(FFA_PAGE_SIZE)));
static uint8_t recv_page[FFA_PAGE_SIZE] __attribute__((aligned(FFA_PAGE_SIZE)));
// static_assert(sizeof(send_page) == FFA_PAGE_SIZE, "Send page is not a page.");
// static_assert(sizeof(recv_page) == FFA_PAGE_SIZE, "Recv page is not a page.");
static uint64_t send_page_addr = (uint64_t)send_page;
static uint64_t recv_page_addr = (uint64_t)recv_page;
struct mailbox_buffers set_up_mailbox(void)
{
ASSERT_EQ(ffa_rxtx_map(send_page_addr, recv_page_addr).func,
FFA_SUCCESS_32);
return (struct mailbox_buffers){
.send = send_page,
.recv = recv_page,
};
}
/*
* Helper function to send memory to a VM then send a message with the retrieve
* request it needs to retrieve it.
*/
ffa_memory_handle_t send_memory_and_retrieve_request(
uint32_t share_func, void *tx_buffer, ffa_vm_id_t sender,
ffa_vm_id_t recipient,
struct ffa_memory_region_constituent constituents[],
uint32_t constituent_count, ffa_memory_region_flags_t flags,
enum ffa_data_access send_data_access,
enum ffa_data_access retrieve_data_access,
enum ffa_instruction_access send_instruction_access,
enum ffa_instruction_access retrieve_instruction_access)
{
uint32_t total_length;
uint32_t fragment_length;
uint32_t msg_size;
struct ffa_value ret;
const ffa_memory_handle_t INVALID_FRAGMENT_HANDLE = 0xffffffffffffffff;
ffa_memory_handle_t fragment_handle = INVALID_FRAGMENT_HANDLE;
ffa_memory_handle_t handle;
uint32_t remaining_constituent_count;
uint32_t sent_length;
/* Send the first fragment of the memory. */
remaining_constituent_count = ffa_memory_region_init(
tx_buffer, FFA_MAILBOX_SIZE, sender, recipient, constituents,
constituent_count, 0, flags, send_data_access,
send_instruction_access, FFA_MEMORY_NORMAL_MEM,
FFA_MEMORY_CACHE_WRITE_BACK, FFA_MEMORY_OUTER_SHAREABLE,
&total_length, &fragment_length);
if (remaining_constituent_count == 0) {
EXPECT_EQ(total_length, fragment_length);
}
switch (share_func) {
case FFA_MEM_DONATE_32:
ret = ffa_mem_donate(total_length, fragment_length);
break;
case FFA_MEM_LEND_32:
ret = ffa_mem_lend(total_length, fragment_length);
break;
case FFA_MEM_SHARE_32:
ret = ffa_mem_share(total_length, fragment_length);
break;
default:
//FAIL("Invalid share_func %#x.\n", share_func);
/* Never reached, but needed to keep clang-analyser happy. */
return 0;
}
sent_length = fragment_length;
/* Send the remaining fragments. */
while (remaining_constituent_count != 0) {
//dlog_verbose("%d constituents left to send.\n",
// remaining_constituent_count);
EXPECT_EQ(ret.func, FFA_MEM_FRAG_RX_32);
if (fragment_handle == INVALID_FRAGMENT_HANDLE) {
fragment_handle = ffa_frag_handle(ret);
} else {
EXPECT_EQ(ffa_frag_handle(ret), fragment_handle);
}
EXPECT_EQ(ret.arg3, sent_length);
/* Sender MBZ at virtual instance. */
EXPECT_EQ(ffa_frag_sender(ret), 0);
remaining_constituent_count = ffa_memory_fragment_init(
tx_buffer, FFA_MAILBOX_SIZE,
constituents + constituent_count -
remaining_constituent_count,
remaining_constituent_count, &fragment_length);
ret = ffa_mem_frag_tx(fragment_handle, fragment_length);
sent_length += fragment_length;
}
EXPECT_EQ(sent_length, total_length);
EXPECT_EQ(ret.func, FFA_SUCCESS_32);
handle = ffa_mem_success_handle(ret);
EXPECT_EQ(handle & FFA_MEMORY_HANDLE_ALLOCATOR_MASK,
FFA_MEMORY_HANDLE_ALLOCATOR_HYPERVISOR);
if (fragment_handle != INVALID_FRAGMENT_HANDLE) {
EXPECT_EQ(handle, fragment_handle);
}
/*
* Send the appropriate retrieve request to the VM so that it can use it
* to retrieve the memory.
*/
msg_size = ffa_memory_retrieve_request_init(
tx_buffer, handle, sender, recipient, 0, 0,
retrieve_data_access, retrieve_instruction_access,
FFA_MEMORY_NORMAL_MEM, FFA_MEMORY_CACHE_WRITE_BACK,
FFA_MEMORY_OUTER_SHAREABLE);
EXPECT_LE(msg_size, FFA_MAILBOX_SIZE);
EXPECT_EQ(ffa_msg_send(sender, recipient, msg_size, 0).func,
FFA_SUCCESS_32);
return handle;
}
/*
* Helper function to send memory to a VM then send a message with the retrieve
* request it needs to retrieve it, forcing the request to be made in at least
* two fragments even if it could fit in one.
*/
ffa_memory_handle_t send_memory_and_retrieve_request_force_fragmented(
uint32_t share_func, void *tx_buffer, ffa_vm_id_t sender,
ffa_vm_id_t recipient,
struct ffa_memory_region_constituent constituents[],
uint32_t constituent_count, ffa_memory_region_flags_t flags,
enum ffa_data_access send_data_access,
enum ffa_data_access retrieve_data_access,
enum ffa_instruction_access send_instruction_access,
enum ffa_instruction_access retrieve_instruction_access)
{
uint32_t total_length;
uint32_t fragment_length;
uint32_t msg_size;
uint32_t remaining_constituent_count;
struct ffa_value ret;
ffa_memory_handle_t handle;
/* Send everything except the last constituent in the first fragment. */
remaining_constituent_count = ffa_memory_region_init(
tx_buffer, FFA_MAILBOX_SIZE, sender, recipient, constituents,
constituent_count, 0, flags, send_data_access,
send_instruction_access, FFA_MEMORY_NORMAL_MEM,
FFA_MEMORY_CACHE_WRITE_BACK, FFA_MEMORY_OUTER_SHAREABLE,
&total_length, &fragment_length);
EXPECT_EQ(remaining_constituent_count, 0);
EXPECT_EQ(total_length, fragment_length);
/* Don't include the last constituent in the first fragment. */
fragment_length -= sizeof(struct ffa_memory_region_constituent);
switch (share_func) {
case FFA_MEM_DONATE_32:
ret = ffa_mem_donate(total_length, fragment_length);
break;
case FFA_MEM_LEND_32:
ret = ffa_mem_lend(total_length, fragment_length);
break;
case FFA_MEM_SHARE_32:
ret = ffa_mem_share(total_length, fragment_length);
break;
default:
//FAIL("Invalid share_func %#x.\n", share_func);
/* Never reached, but needed to keep clang-analyser happy. */
return 0;
}
EXPECT_EQ(ret.func, FFA_MEM_FRAG_RX_32);
EXPECT_EQ(ret.arg3, fragment_length);
/* Sender MBZ at virtual instance. */
EXPECT_EQ(ffa_frag_sender(ret), 0);
handle = ffa_frag_handle(ret);
/* Send the last constituent in a separate fragment. */
remaining_constituent_count = ffa_memory_fragment_init(
tx_buffer, FFA_MAILBOX_SIZE,
&constituents[constituent_count - 1], 1, &fragment_length);
EXPECT_EQ(remaining_constituent_count, 0);
ret = ffa_mem_frag_tx(handle, fragment_length);
EXPECT_EQ(ret.func, FFA_SUCCESS_32);
EXPECT_EQ(ffa_mem_success_handle(ret), handle);
/*
* Send the appropriate retrieve request to the VM so that it can use it
* to retrieve the memory.
*/
msg_size = ffa_memory_retrieve_request_init(
tx_buffer, handle, sender, recipient, 0, 0,
retrieve_data_access, retrieve_instruction_access,
FFA_MEMORY_NORMAL_MEM, FFA_MEMORY_CACHE_WRITE_BACK,
FFA_MEMORY_OUTER_SHAREABLE);
EXPECT_LE(msg_size, FFA_MAILBOX_SIZE);
EXPECT_EQ(ffa_msg_send(sender, recipient, msg_size, 0).func,
FFA_SUCCESS_32);
return handle;
}
/*
* Use the retrieve request from the receive buffer to retrieve a memory region
* which has been sent to us. Copies all the fragments into the provided buffer
* if any, and checks that the total length of all fragments is no more than
* `memory_region_max_size`. Returns the sender, and the handle via a return
* parameter.
*/
ffa_vm_id_t retrieve_memory_from_message(
void *recv_buf, void *send_buf, struct ffa_value msg_ret,
ffa_memory_handle_t *handle,
struct ffa_memory_region *memory_region_ret,
size_t memory_region_max_size)
{
uint32_t msg_size;
struct ffa_value ret;
struct ffa_memory_region *memory_region;
ffa_vm_id_t sender;
struct ffa_memory_region *retrieve_request;
ffa_memory_handle_t handle_;
uint32_t fragment_length;
uint32_t total_length;
uint32_t fragment_offset;
EXPECT_EQ(msg_ret.func, FFA_MSG_SEND_32);
msg_size = ffa_msg_send_size(msg_ret);
sender = ffa_msg_send_sender(msg_ret);
retrieve_request = (struct ffa_memory_region *)recv_buf;
handle_ = retrieve_request->handle;
if (handle != NULL) {
*handle = handle_;
}
memcpy_s(send_buf, FFA_MAILBOX_SIZE, recv_buf, msg_size);
ffa_rx_release();
ret = ffa_mem_retrieve_req(msg_size, msg_size);
EXPECT_EQ(ret.func, FFA_MEM_RETRIEVE_RESP_32);
total_length = ret.arg1;
fragment_length = ret.arg2;
EXPECT_GE(fragment_length,
sizeof(struct ffa_memory_region) +
sizeof(struct ffa_memory_access) +
sizeof(struct ffa_composite_memory_region));
EXPECT_LE(fragment_length, FFA_MAILBOX_SIZE);
EXPECT_LE(fragment_length, total_length);
memory_region = (struct ffa_memory_region *)recv_buf;
EXPECT_EQ(memory_region->receiver_count, 1);
EXPECT_EQ(memory_region->receivers[0].receiver_permissions.receiver,
ffa_id_get().arg2);
/* Copy into the return buffer. */
if (memory_region_ret != NULL) {
memcpy_s(memory_region_ret, memory_region_max_size,
memory_region, fragment_length);
}
/*
* Release the RX buffer now that we have read everything we need from
* it.
*/
memory_region = NULL;
EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
/* Retrieve the remaining fragments. */
fragment_offset = fragment_length;
while (fragment_offset < total_length) {
ret = ffa_mem_frag_rx(handle_, fragment_offset);
EXPECT_EQ(ret.func, FFA_MEM_FRAG_TX_32);
EXPECT_EQ(ffa_frag_handle(ret), handle_);
/* Sender MBZ at virtual instance. */
EXPECT_EQ(ffa_frag_sender(ret), 0);
fragment_length = ret.arg3;
EXPECT_GT(fragment_length, 0);
ASSERT_LE(fragment_offset + fragment_length,
memory_region_max_size);
if (memory_region_ret != NULL) {
memcpy_s((uint8_t *)memory_region_ret + fragment_offset,
memory_region_max_size - fragment_offset,
recv_buf, fragment_length);
}
fragment_offset += fragment_length;
EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32);
}
EXPECT_EQ(fragment_offset, total_length);
return sender;
}
/*
* Use the retrieve request from the receive buffer to retrieve a memory region
* which has been sent to us, expecting it to fail with the given error code.
* Returns the sender.
*/
ffa_vm_id_t retrieve_memory_from_message_expect_fail(void *recv_buf,
void *send_buf,
struct ffa_value msg_ret,
int32_t expected_error)
{
uint32_t msg_size;
struct ffa_value ret;
ffa_vm_id_t sender;
EXPECT_EQ(msg_ret.func, FFA_MSG_SEND_32);
msg_size = ffa_msg_send_size(msg_ret);
sender = ffa_msg_send_sender(msg_ret);
memcpy_s(send_buf, FFA_MAILBOX_SIZE, recv_buf, msg_size);
ffa_rx_release();
ret = ffa_mem_retrieve_req(msg_size, msg_size);
EXPECT_FFA_ERROR(ret, expected_error);
return sender;
}