| /* |
| * 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; |
| } |