| /* |
| * Copyright 2018 The Hafnium Authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "hf/arch/vm/interrupts.h" |
| |
| #include "hf/check.h" |
| #include "hf/mm.h" |
| #include "hf/std.h" |
| |
| #include "vmapi/hf/call.h" |
| |
| #include "primary_with_secondary.h" |
| #include "test/hftest.h" |
| #include "test/vmapi/exception_handler.h" |
| #include "test/vmapi/ffa.h" |
| |
| alignas(PAGE_SIZE) static uint8_t page[PAGE_SIZE]; |
| |
| TEST_SERVICE(memory_increment) |
| { |
| /* Loop, writing message to the shared memory. */ |
| for (;;) { |
| size_t i; |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| |
| struct ffa_value ret = ffa_msg_wait(); |
| ffa_vm_id_t sender = retrieve_memory_from_message( |
| recv_buf, send_buf, ret, NULL); |
| struct ffa_memory_region *memory_region = |
| (struct ffa_memory_region *)recv_buf; |
| struct ffa_composite_memory_region *composite = |
| ffa_memory_region_get_composite(memory_region, 0); |
| uint8_t *ptr = (uint8_t *)composite->constituents[0].address; |
| |
| ASSERT_EQ(memory_region->receiver_count, 1); |
| ASSERT_NE(memory_region->receivers[0] |
| .composite_memory_region_offset, |
| 0); |
| |
| /* Allow the memory to be populated. */ |
| EXPECT_EQ(ffa_yield().func, FFA_SUCCESS_32); |
| |
| /* Increment each byte of memory. */ |
| for (i = 0; i < PAGE_SIZE; ++i) { |
| ++ptr[i]; |
| } |
| |
| /* Signal completion and reset. */ |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| ffa_msg_send(hf_vm_get_id(), sender, sizeof(ptr), 0); |
| } |
| } |
| |
| TEST_SERVICE(give_memory_and_fault) |
| { |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct ffa_memory_region_constituent constituents[] = { |
| {.address = (uint64_t)&page, .page_count = 1}, |
| }; |
| |
| /* Give memory to the primary. */ |
| send_memory_and_retrieve_request( |
| FFA_MEM_DONATE_32, send_buf, hf_vm_get_id(), HF_PRIMARY_VM_ID, |
| constituents, ARRAY_SIZE(constituents), |
| FFA_MEMORY_REGION_FLAG_CLEAR, FFA_DATA_ACCESS_NOT_SPECIFIED, |
| FFA_DATA_ACCESS_RW, FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED, |
| FFA_INSTRUCTION_ACCESS_X); |
| |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| /* Try using the memory that isn't valid unless it's been returned. */ |
| page[16] = 123; |
| |
| FAIL("Exception not generated by invalid access."); |
| } |
| |
| TEST_SERVICE(lend_memory_and_fault) |
| { |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct ffa_memory_region_constituent constituents[] = { |
| {.address = (uint64_t)&page, .page_count = 1}, |
| }; |
| |
| /* Lend memory to the primary. */ |
| send_memory_and_retrieve_request( |
| FFA_MEM_LEND_32, send_buf, hf_vm_get_id(), HF_PRIMARY_VM_ID, |
| constituents, ARRAY_SIZE(constituents), |
| FFA_MEMORY_REGION_FLAG_CLEAR, FFA_DATA_ACCESS_RW, |
| FFA_DATA_ACCESS_RW, FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED, |
| FFA_INSTRUCTION_ACCESS_X); |
| |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| /* Try using the memory that isn't valid unless it's been returned. */ |
| page[633] = 180; |
| |
| FAIL("Exception not generated by invalid access."); |
| } |
| |
| TEST_SERVICE(ffa_memory_return) |
| { |
| struct ffa_value ret = ffa_msg_wait(); |
| uint8_t *ptr; |
| size_t i; |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| ffa_vm_id_t sender = |
| retrieve_memory_from_message(recv_buf, send_buf, ret, NULL); |
| struct ffa_memory_region *memory_region = |
| (struct ffa_memory_region *)recv_buf; |
| struct ffa_composite_memory_region *composite = |
| ffa_memory_region_get_composite(memory_region, 0); |
| |
| ptr = (uint8_t *)composite->constituents[0].address; |
| |
| /* Check that one has access to the shared region. */ |
| for (i = 0; i < PAGE_SIZE; ++i) { |
| ptr[i]++; |
| } |
| |
| /* Give the memory back and notify the sender. */ |
| send_memory_and_retrieve_request( |
| FFA_MEM_DONATE_32, send_buf, hf_vm_get_id(), sender, |
| composite->constituents, composite->constituent_count, 0, |
| FFA_DATA_ACCESS_NOT_SPECIFIED, FFA_DATA_ACCESS_RW, |
| FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED, FFA_INSTRUCTION_ACCESS_X); |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| |
| /* |
| * Try and access the memory which will cause a fault unless the memory |
| * has been shared back again. |
| */ |
| ptr[0] = 123; |
| |
| FAIL("Exception not generated by invalid access."); |
| } |
| |
| /** |
| * Attempt to modify above the upper bound of a memory region sent to us. |
| */ |
| TEST_SERVICE(ffa_check_upper_bound) |
| { |
| struct ffa_memory_region *memory_region; |
| struct ffa_composite_memory_region *composite; |
| uint8_t *ptr; |
| uint8_t index; |
| |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct ffa_value ret = ffa_msg_wait(); |
| |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| retrieve_memory_from_message(recv_buf, send_buf, ret, NULL); |
| memory_region = (struct ffa_memory_region *)recv_buf; |
| composite = ffa_memory_region_get_composite(memory_region, 0); |
| |
| /* Choose which constituent we want to test. */ |
| index = *(uint8_t *)composite->constituents[0].address; |
| ptr = (uint8_t *)composite->constituents[index].address; |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| |
| /* |
| * Check that we can't access out of bounds after the region sent to us. |
| * This should trigger the exception handler. |
| */ |
| ptr[PAGE_SIZE]++; |
| |
| FAIL("Exception not generated by access out of bounds."); |
| } |
| |
| /** |
| * Attempt to modify below the lower bound of a memory region sent to us. |
| */ |
| TEST_SERVICE(ffa_check_lower_bound) |
| { |
| struct ffa_memory_region *memory_region; |
| struct ffa_composite_memory_region *composite; |
| uint8_t *ptr; |
| uint8_t index; |
| |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct ffa_value ret = ffa_msg_wait(); |
| |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| retrieve_memory_from_message(recv_buf, send_buf, ret, NULL); |
| memory_region = (struct ffa_memory_region *)recv_buf; |
| composite = ffa_memory_region_get_composite(memory_region, 0); |
| |
| /* Choose which constituent we want to test. */ |
| index = *(uint8_t *)composite->constituents[0].address; |
| ptr = (uint8_t *)composite->constituents[index].address; |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| |
| /* |
| * Check that we can't access out of bounds before the region sent to |
| * us. This should trigger the exception handler. |
| */ |
| ptr[-1]++; |
| |
| FAIL("Exception not generated by access out of bounds."); |
| } |
| |
| /** |
| * Attempt to donate memory and then modify. |
| */ |
| TEST_SERVICE(ffa_donate_secondary_and_fault) |
| { |
| uint8_t *ptr; |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| |
| struct ffa_value ret = ffa_msg_wait(); |
| ffa_vm_id_t sender = |
| retrieve_memory_from_message(recv_buf, send_buf, ret, NULL); |
| struct ffa_memory_region *memory_region = |
| (struct ffa_memory_region *)recv_buf; |
| struct ffa_composite_memory_region *composite = |
| ffa_memory_region_get_composite(memory_region, 0); |
| |
| ASSERT_EQ(sender, HF_PRIMARY_VM_ID); |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| ptr = (uint8_t *)composite->constituents[0].address; |
| |
| /* Donate memory to next VM. */ |
| send_memory_and_retrieve_request( |
| FFA_MEM_DONATE_32, send_buf, hf_vm_get_id(), SERVICE_VM2, |
| composite->constituents, composite->constituent_count, 0, |
| FFA_DATA_ACCESS_NOT_SPECIFIED, FFA_DATA_ACCESS_RW, |
| FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED, FFA_INSTRUCTION_ACCESS_X); |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| |
| /* Ensure that we are unable to modify memory any more. */ |
| ptr[0] = 'c'; |
| |
| FAIL("Exception not generated by invalid access."); |
| } |
| |
| /** |
| * Attempt to donate memory twice from VM. |
| */ |
| TEST_SERVICE(ffa_donate_twice) |
| { |
| uint32_t msg_size; |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| |
| struct ffa_value ret = ffa_msg_wait(); |
| ffa_vm_id_t sender = |
| retrieve_memory_from_message(recv_buf, send_buf, ret, NULL); |
| struct ffa_memory_region *memory_region = |
| (struct ffa_memory_region *)recv_buf; |
| struct ffa_composite_memory_region *composite = |
| ffa_memory_region_get_composite(memory_region, 0); |
| struct ffa_memory_region_constituent constituent = |
| composite->constituents[0]; |
| |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| |
| /* Yield to allow attempt to re donate from primary. */ |
| ffa_yield(); |
| |
| /* Give the memory back and notify the sender. */ |
| send_memory_and_retrieve_request( |
| FFA_MEM_DONATE_32, send_buf, hf_vm_get_id(), sender, |
| &constituent, 1, 0, FFA_DATA_ACCESS_NOT_SPECIFIED, |
| FFA_DATA_ACCESS_RW, FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED, |
| FFA_INSTRUCTION_ACCESS_X); |
| |
| /* Attempt to donate the memory to another VM. */ |
| msg_size = ffa_memory_region_init( |
| send_buf, hf_vm_get_id(), SERVICE_VM2, &constituent, 1, 0, 0, |
| FFA_DATA_ACCESS_NOT_SPECIFIED, |
| FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED, FFA_MEMORY_NORMAL_MEM, |
| FFA_MEMORY_CACHE_WRITE_BACK, FFA_MEMORY_OUTER_SHAREABLE); |
| EXPECT_FFA_ERROR(ffa_mem_donate(msg_size, msg_size), FFA_DENIED); |
| |
| ffa_yield(); |
| } |
| |
| /** |
| * Continually receive memory, check if we have access and ensure it is not |
| * changed by a third party. |
| */ |
| TEST_SERVICE(ffa_memory_receive) |
| { |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| |
| for (;;) { |
| struct ffa_value ret = ffa_msg_wait(); |
| struct ffa_memory_region *memory_region; |
| struct ffa_composite_memory_region *composite; |
| uint8_t *ptr; |
| |
| retrieve_memory_from_message(recv_buf, send_buf, ret, NULL); |
| memory_region = (struct ffa_memory_region *)recv_buf; |
| composite = ffa_memory_region_get_composite(memory_region, 0); |
| ptr = (uint8_t *)composite->constituents[0].address; |
| |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| ptr[0] = 'd'; |
| ffa_yield(); |
| |
| /* Ensure memory has not changed. */ |
| EXPECT_EQ(ptr[0], 'd'); |
| ffa_yield(); |
| } |
| } |
| |
| /** |
| * Receive memory and attempt to donate from primary VM. |
| */ |
| TEST_SERVICE(ffa_donate_invalid_source) |
| { |
| uint32_t msg_size; |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| |
| struct ffa_value ret = ffa_msg_wait(); |
| ffa_vm_id_t sender = |
| retrieve_memory_from_message(recv_buf, send_buf, ret, NULL); |
| struct ffa_memory_region *memory_region = |
| (struct ffa_memory_region *)recv_buf; |
| struct ffa_composite_memory_region *composite = |
| ffa_memory_region_get_composite(memory_region, 0); |
| |
| /* Give the memory back and notify the sender. */ |
| send_memory_and_retrieve_request( |
| FFA_MEM_DONATE_32, send_buf, hf_vm_get_id(), sender, |
| composite->constituents, composite->constituent_count, 0, |
| FFA_DATA_ACCESS_NOT_SPECIFIED, FFA_DATA_ACCESS_RW, |
| FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED, FFA_INSTRUCTION_ACCESS_X); |
| |
| /* Fail to donate the memory from the primary to VM2. */ |
| msg_size = ffa_memory_region_init( |
| send_buf, HF_PRIMARY_VM_ID, SERVICE_VM2, |
| composite->constituents, composite->constituent_count, 0, 0, |
| FFA_DATA_ACCESS_NOT_SPECIFIED, |
| FFA_INSTRUCTION_ACCESS_NOT_SPECIFIED, FFA_MEMORY_NORMAL_MEM, |
| FFA_MEMORY_CACHE_WRITE_BACK, FFA_MEMORY_OUTER_SHAREABLE); |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| EXPECT_FFA_ERROR(ffa_mem_donate(msg_size, msg_size), |
| FFA_INVALID_PARAMETERS); |
| ffa_yield(); |
| } |
| |
| TEST_SERVICE(ffa_memory_lend_relinquish) |
| { |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| /* Loop, giving memory back to the sender. */ |
| for (;;) { |
| uint8_t *ptr; |
| uint8_t *ptr2; |
| uint32_t count; |
| uint32_t count2; |
| size_t i; |
| ffa_memory_handle_t handle; |
| |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| |
| struct ffa_value ret = ffa_msg_wait(); |
| ffa_vm_id_t sender = retrieve_memory_from_message( |
| recv_buf, send_buf, ret, &handle); |
| struct ffa_memory_region *memory_region = |
| (struct ffa_memory_region *)recv_buf; |
| struct ffa_composite_memory_region *composite = |
| ffa_memory_region_get_composite(memory_region, 0); |
| struct ffa_memory_region_constituent *constituents = |
| composite->constituents; |
| |
| /* ASSERT_TRUE isn't enough for clang-analyze. */ |
| CHECK(composite != NULL); |
| |
| ptr = (uint8_t *)constituents[0].address; |
| count = constituents[0].page_count; |
| ptr2 = (uint8_t *)constituents[1].address; |
| count2 = constituents[1].page_count; |
| /* Relevant information read, mailbox can be cleared. */ |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| |
| /* Check that one has access to the shared region. */ |
| for (i = 0; i < PAGE_SIZE * count; ++i) { |
| ptr[i]++; |
| } |
| for (i = 0; i < PAGE_SIZE * count2; ++i) { |
| ptr2[i]++; |
| } |
| |
| /* Give the memory back and notify the sender. */ |
| ffa_mem_relinquish_init(send_buf, handle, 0, hf_vm_get_id()); |
| EXPECT_EQ(ffa_mem_relinquish().func, FFA_SUCCESS_32); |
| EXPECT_EQ(ffa_msg_send(hf_vm_get_id(), sender, 0, 0).func, |
| FFA_SUCCESS_32); |
| |
| /* |
| * Try and access the memory which will cause a fault unless the |
| * memory has been shared back again. |
| */ |
| ptr[0] = 123; |
| } |
| } |
| |
| /** |
| * Ensure that we can't relinquish donated memory. |
| */ |
| TEST_SERVICE(ffa_memory_donate_relinquish) |
| { |
| for (;;) { |
| size_t i; |
| ffa_memory_handle_t handle; |
| struct ffa_memory_region *memory_region; |
| struct ffa_composite_memory_region *composite; |
| uint8_t *ptr; |
| |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct ffa_value ret = ffa_msg_wait(); |
| |
| retrieve_memory_from_message(recv_buf, send_buf, ret, &handle); |
| memory_region = (struct ffa_memory_region *)recv_buf; |
| composite = ffa_memory_region_get_composite(memory_region, 0); |
| |
| ptr = (uint8_t *)composite->constituents[0].address; |
| |
| /* Check that we have access to the shared region. */ |
| for (i = 0; i < PAGE_SIZE; ++i) { |
| ptr[i]++; |
| } |
| |
| /* |
| * Attempt to relinquish the memory, which should fail because |
| * it was donated not lent. |
| */ |
| ffa_mem_relinquish_init(send_buf, handle, 0, hf_vm_get_id()); |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| EXPECT_FFA_ERROR(ffa_mem_relinquish(), FFA_INVALID_PARAMETERS); |
| |
| /* Ensure we still have access to the memory. */ |
| ptr[0] = 123; |
| |
| ffa_yield(); |
| } |
| } |
| |
| /** |
| * Receive memory that has been shared, try to relinquish it with the clear flag |
| * set (and expect to fail), and then relinquish without any flags. |
| */ |
| TEST_SERVICE(ffa_memory_share_relinquish_clear) |
| { |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| /* Loop, receiving memory and relinquishing it. */ |
| for (;;) { |
| ffa_memory_handle_t handle; |
| |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| |
| struct ffa_value ret = ffa_msg_wait(); |
| ffa_vm_id_t sender = retrieve_memory_from_message( |
| recv_buf, send_buf, ret, &handle); |
| |
| /* |
| * Mailbox can be cleared, we don't actually care what the |
| * memory region is. |
| */ |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| |
| /* Trying to relinquish the memory and clear it should fail. */ |
| ffa_mem_relinquish_init(send_buf, handle, |
| FFA_MEMORY_REGION_FLAG_CLEAR, |
| hf_vm_get_id()); |
| EXPECT_FFA_ERROR(ffa_mem_relinquish(), FFA_INVALID_PARAMETERS); |
| |
| /* Give the memory back and notify the sender. */ |
| ffa_mem_relinquish_init(send_buf, handle, 0, hf_vm_get_id()); |
| EXPECT_EQ(ffa_mem_relinquish().func, FFA_SUCCESS_32); |
| EXPECT_EQ(ffa_msg_send(hf_vm_get_id(), sender, 0, 0).func, |
| FFA_SUCCESS_32); |
| } |
| } |
| |
| /** |
| * Receive memory and attempt to donate from primary VM. |
| */ |
| TEST_SERVICE(ffa_lend_invalid_source) |
| { |
| ffa_memory_handle_t handle; |
| uint32_t msg_size; |
| |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct ffa_value ret = ffa_msg_wait(); |
| ffa_vm_id_t sender = |
| retrieve_memory_from_message(recv_buf, send_buf, ret, &handle); |
| struct ffa_memory_region *memory_region = |
| (struct ffa_memory_region *)recv_buf; |
| struct ffa_composite_memory_region *composite = |
| ffa_memory_region_get_composite(memory_region, 0); |
| |
| /* Give the memory back and notify the sender. */ |
| ffa_mem_relinquish_init(send_buf, handle, 0, hf_vm_get_id()); |
| EXPECT_EQ(ffa_mem_relinquish().func, FFA_SUCCESS_32); |
| EXPECT_EQ(ffa_msg_send(hf_vm_get_id(), sender, 0, 0).func, |
| FFA_SUCCESS_32); |
| |
| /* Ensure we cannot lend from the primary to another secondary. */ |
| msg_size = ffa_memory_region_init( |
| send_buf, HF_PRIMARY_VM_ID, SERVICE_VM2, |
| composite->constituents, composite->constituent_count, 0, 0, |
| FFA_DATA_ACCESS_RW, FFA_INSTRUCTION_ACCESS_X, |
| FFA_MEMORY_NORMAL_MEM, FFA_MEMORY_CACHE_WRITE_BACK, |
| FFA_MEMORY_OUTER_SHAREABLE); |
| EXPECT_FFA_ERROR(ffa_mem_lend(msg_size, msg_size), |
| FFA_INVALID_PARAMETERS); |
| |
| /* Ensure we cannot share from the primary to another secondary. */ |
| msg_size = ffa_memory_region_init( |
| send_buf, HF_PRIMARY_VM_ID, SERVICE_VM2, |
| composite->constituents, composite->constituent_count, 0, 0, |
| FFA_DATA_ACCESS_RW, FFA_INSTRUCTION_ACCESS_X, |
| FFA_MEMORY_NORMAL_MEM, FFA_MEMORY_CACHE_WRITE_BACK, |
| FFA_MEMORY_OUTER_SHAREABLE); |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| EXPECT_FFA_ERROR(ffa_mem_share(msg_size, msg_size), |
| FFA_INVALID_PARAMETERS); |
| |
| ffa_yield(); |
| } |
| |
| /** |
| * Attempt to execute an instruction from the lent memory. |
| */ |
| TEST_SERVICE(ffa_memory_lend_relinquish_X) |
| { |
| exception_setup(NULL, exception_handler_yield_instruction_abort); |
| |
| for (;;) { |
| ffa_memory_handle_t handle; |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct ffa_value ret = ffa_msg_wait(); |
| ffa_vm_id_t sender = retrieve_memory_from_message( |
| recv_buf, send_buf, ret, &handle); |
| struct ffa_memory_region *memory_region = |
| (struct ffa_memory_region *)recv_buf; |
| struct ffa_composite_memory_region *composite = |
| ffa_memory_region_get_composite(memory_region, 0); |
| struct ffa_memory_region_constituent *constituents; |
| uint64_t *ptr; |
| |
| /* ASSERT_TRUE isn't enough for clang-analyze. */ |
| CHECK(composite != NULL); |
| |
| constituents = composite->constituents; |
| ptr = (uint64_t *)constituents[0].address; |
| |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| |
| /* |
| * Verify that the instruction in memory is the encoded RET |
| * instruction. |
| */ |
| EXPECT_EQ(*ptr, 0xD65F03C0); |
| /* Try to execute instruction from the shared memory region. */ |
| __asm__ volatile("blr %0" ::"r"(ptr)); |
| |
| /* Release the memory again. */ |
| ffa_mem_relinquish_init(send_buf, handle, 0, hf_vm_get_id()); |
| EXPECT_EQ(ffa_mem_relinquish().func, FFA_SUCCESS_32); |
| EXPECT_EQ(ffa_msg_send(hf_vm_get_id(), sender, 0, 0).func, |
| FFA_SUCCESS_32); |
| } |
| } |
| |
| /** |
| * Attempt to retrieve a shared page but expect to fail. |
| */ |
| TEST_SERVICE(ffa_memory_share_fail) |
| { |
| for (;;) { |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct ffa_value ret = ffa_msg_wait(); |
| ffa_vm_id_t sender = retrieve_memory_from_message_expect_fail( |
| recv_buf, send_buf, ret, FFA_DENIED); |
| |
| /* Return control to primary. */ |
| EXPECT_EQ(ffa_msg_send(hf_vm_get_id(), sender, 0, 0).func, |
| FFA_SUCCESS_32); |
| } |
| } |
| |
| /** |
| * Attempt to read and write to a shared page. |
| */ |
| TEST_SERVICE(ffa_memory_lend_relinquish_RW) |
| { |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| for (;;) { |
| ffa_memory_handle_t handle; |
| uint8_t *ptr; |
| size_t i; |
| |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct ffa_value ret = ffa_msg_wait(); |
| ffa_vm_id_t sender = retrieve_memory_from_message( |
| recv_buf, send_buf, ret, &handle); |
| struct ffa_memory_region *memory_region = |
| (struct ffa_memory_region *)recv_buf; |
| struct ffa_composite_memory_region *composite = |
| ffa_memory_region_get_composite(memory_region, 0); |
| struct ffa_memory_region_constituent constituent_copy = |
| composite->constituents[0]; |
| |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| |
| ptr = (uint8_t *)constituent_copy.address; |
| |
| /* Check that we have read access. */ |
| for (i = 0; i < PAGE_SIZE; ++i) { |
| EXPECT_EQ(ptr[i], 'b'); |
| } |
| |
| /* Return control to primary, to verify shared access. */ |
| ffa_yield(); |
| |
| /* Attempt to modify the memory. */ |
| for (i = 0; i < PAGE_SIZE; ++i) { |
| ptr[i]++; |
| } |
| |
| /* Give the memory back and notify the sender. */ |
| ffa_mem_relinquish_init(send_buf, handle, 0, hf_vm_get_id()); |
| EXPECT_EQ(ffa_mem_relinquish().func, FFA_SUCCESS_32); |
| EXPECT_EQ(ffa_msg_send(hf_vm_get_id(), sender, 0, 0).func, |
| FFA_SUCCESS_32); |
| } |
| } |
| |
| TEST_SERVICE(ffa_memory_lend_twice) |
| { |
| struct ffa_value ret = ffa_msg_wait(); |
| uint8_t *ptr; |
| uint32_t msg_size; |
| size_t i; |
| |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct ffa_memory_region *memory_region; |
| struct ffa_composite_memory_region *composite; |
| struct ffa_memory_region_constituent constituent_copy; |
| |
| retrieve_memory_from_message(recv_buf, send_buf, ret, NULL); |
| memory_region = (struct ffa_memory_region *)recv_buf; |
| composite = ffa_memory_region_get_composite(memory_region, 0); |
| constituent_copy = composite->constituents[0]; |
| |
| EXPECT_EQ(ffa_rx_release().func, FFA_SUCCESS_32); |
| |
| ptr = (uint8_t *)constituent_copy.address; |
| |
| /* Check that we have read access. */ |
| for (i = 0; i < PAGE_SIZE; ++i) { |
| EXPECT_EQ(ptr[i], 'b'); |
| } |
| |
| /* Attempt to modify the memory. */ |
| for (i = 0; i < PAGE_SIZE; ++i) { |
| ptr[i]++; |
| } |
| |
| for (i = 1; i < PAGE_SIZE * 2; i++) { |
| constituent_copy.address = (uint64_t)ptr + i; |
| |
| /* Fail to lend or share the memory from the primary. */ |
| msg_size = ffa_memory_region_init( |
| send_buf, HF_PRIMARY_VM_ID, SERVICE_VM2, |
| &constituent_copy, 1, 0, 0, FFA_DATA_ACCESS_RW, |
| FFA_INSTRUCTION_ACCESS_X, FFA_MEMORY_NORMAL_MEM, |
| FFA_MEMORY_CACHE_WRITE_BACK, |
| FFA_MEMORY_OUTER_SHAREABLE); |
| EXPECT_FFA_ERROR(ffa_mem_lend(msg_size, msg_size), |
| FFA_INVALID_PARAMETERS); |
| msg_size = ffa_memory_region_init( |
| send_buf, HF_PRIMARY_VM_ID, SERVICE_VM2, |
| &constituent_copy, 1, 0, 0, FFA_DATA_ACCESS_RW, |
| FFA_INSTRUCTION_ACCESS_X, FFA_MEMORY_NORMAL_MEM, |
| FFA_MEMORY_CACHE_WRITE_BACK, |
| FFA_MEMORY_OUTER_SHAREABLE); |
| EXPECT_FFA_ERROR(ffa_mem_share(msg_size, msg_size), |
| FFA_INVALID_PARAMETERS); |
| } |
| |
| /* Return control to primary. */ |
| ffa_yield(); |
| } |