| /* |
| * 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/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/spci.h" |
| |
| alignas(PAGE_SIZE) static uint8_t page[PAGE_SIZE]; |
| |
| TEST_SERVICE(memory_increment) |
| { |
| /* Loop, writing message to the shared memory. */ |
| for (;;) { |
| struct spci_value ret = spci_msg_wait(); |
| uint8_t *ptr; |
| size_t i; |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| struct spci_memory_region *memory_region = |
| (struct spci_memory_region *)recv_buf; |
| struct spci_memory_region_constituent *constituents = |
| spci_memory_region_get_constituents(memory_region); |
| spci_vm_id_t sender = memory_region->sender; |
| |
| EXPECT_EQ(ret.func, SPCI_MEM_SHARE_32); |
| |
| ptr = (uint8_t *)spci_memory_region_constituent_get_address( |
| &constituents[0]); |
| |
| /* Check the memory was cleared. */ |
| for (i = 0; i < PAGE_SIZE; ++i) { |
| ASSERT_EQ(ptr[i], 0); |
| } |
| |
| /* Allow the memory to be populated. */ |
| EXPECT_EQ(spci_yield().func, SPCI_SUCCESS_32); |
| |
| /* Increment each byte of memory. */ |
| for (i = 0; i < PAGE_SIZE; ++i) { |
| ++ptr[i]; |
| } |
| |
| /* Signal completion and reset. */ |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| spci_msg_send(hf_vm_get_id(), sender, sizeof(ptr), 0); |
| } |
| } |
| |
| TEST_SERVICE(give_memory_and_fault) |
| { |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| |
| /* Give memory to the primary. */ |
| struct spci_memory_region_constituent constituents[] = { |
| spci_memory_region_constituent_init((uint64_t)&page, 1), |
| }; |
| uint32_t msg_size = spci_memory_region_init( |
| send_buf, hf_vm_get_id(), HF_PRIMARY_VM_ID, constituents, |
| ARRAY_SIZE(constituents), 0, SPCI_MEMORY_REGION_FLAG_CLEAR, |
| SPCI_MEMORY_RW_X, SPCI_MEMORY_NORMAL_MEM, |
| SPCI_MEMORY_CACHE_WRITE_BACK, SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_EQ(spci_mem_donate(msg_size, msg_size, 0).func, SPCI_SUCCESS_32); |
| |
| 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(); |
| |
| /* Lend memory to the primary. */ |
| struct spci_memory_region_constituent constituents[] = { |
| spci_memory_region_constituent_init((uint64_t)&page, 1), |
| }; |
| uint32_t msg_size = spci_memory_region_init( |
| send_buf, hf_vm_get_id(), HF_PRIMARY_VM_ID, constituents, |
| ARRAY_SIZE(constituents), 0, SPCI_MEMORY_REGION_FLAG_CLEAR, |
| SPCI_MEMORY_RW_X, SPCI_MEMORY_NORMAL_MEM, |
| SPCI_MEMORY_CACHE_WRITE_BACK, SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_EQ(spci_mem_lend(msg_size, msg_size, 0).func, SPCI_SUCCESS_32); |
| |
| 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(spci_memory_return) |
| { |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| /* Loop, giving memory back to the sender. */ |
| for (;;) { |
| struct spci_value ret = spci_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 spci_memory_region *memory_region; |
| struct spci_memory_region_constituent *constituents; |
| |
| /* |
| * The memory may have been sent in one of several different |
| * ways. |
| */ |
| EXPECT_TRUE(ret.func == SPCI_MEM_DONATE_32 || |
| ret.func == SPCI_MEM_LEND_32 || |
| ret.func == SPCI_MEM_SHARE_32); |
| |
| memory_region = (struct spci_memory_region *)recv_buf; |
| constituents = |
| spci_memory_region_get_constituents(memory_region); |
| ptr = (uint8_t *)spci_memory_region_constituent_get_address( |
| &constituents[0]); |
| |
| /* 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. */ |
| msg_size = spci_memory_region_init( |
| send_buf, hf_vm_get_id(), memory_region->sender, |
| constituents, memory_region->constituent_count, 0, 0, |
| SPCI_MEMORY_RW_X, SPCI_MEMORY_NORMAL_MEM, |
| SPCI_MEMORY_CACHE_WRITE_BACK, |
| SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| EXPECT_EQ(spci_mem_donate(msg_size, msg_size, 0).func, |
| SPCI_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."); |
| } |
| } |
| |
| TEST_SERVICE(spci_donate_check_upper_bound) |
| { |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| for (;;) { |
| struct spci_value ret = spci_msg_wait(); |
| uint8_t *ptr; |
| uint8_t index; |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| struct spci_memory_region *memory_region; |
| struct spci_memory_region_constituent *constituents; |
| |
| exception_handler_reset(); |
| |
| EXPECT_EQ(ret.func, SPCI_MEM_DONATE_32); |
| |
| memory_region = (struct spci_memory_region *)recv_buf; |
| constituents = |
| spci_memory_region_get_constituents(memory_region); |
| |
| /* Choose which constituent we want to test. */ |
| index = *(uint8_t *)spci_memory_region_constituent_get_address( |
| &constituents[0]); |
| ptr = (uint8_t *)spci_memory_region_constituent_get_address( |
| &constituents[index]); |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| |
| /* |
| * Check that one cannot access out of bounds after donated |
| * region. This should trigger the exception handler. |
| */ |
| ptr[PAGE_SIZE]++; |
| } |
| } |
| |
| TEST_SERVICE(spci_donate_check_lower_bound) |
| { |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| for (;;) { |
| struct spci_value ret = spci_msg_wait(); |
| uint8_t *ptr; |
| uint8_t index; |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| struct spci_memory_region *memory_region; |
| struct spci_memory_region_constituent *constituents; |
| |
| exception_handler_reset(); |
| |
| EXPECT_EQ(ret.func, SPCI_MEM_DONATE_32); |
| |
| memory_region = (struct spci_memory_region *)recv_buf; |
| constituents = |
| spci_memory_region_get_constituents(memory_region); |
| |
| /* Choose which constituent we want to test. */ |
| index = *(uint8_t *)spci_memory_region_constituent_get_address( |
| &constituents[0]); |
| ptr = (uint8_t *)spci_memory_region_constituent_get_address( |
| &constituents[index]); |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| |
| /* |
| * Check that one cannot access out of bounds after donated |
| * region. This should trigger the exception handler. |
| */ |
| ptr[-1]++; |
| } |
| } |
| |
| /** |
| * Attempt to donate memory and then modify. |
| */ |
| TEST_SERVICE(spci_donate_secondary_and_fault) |
| { |
| struct spci_value ret = spci_msg_wait(); |
| uint8_t *ptr; |
| uint32_t msg_size; |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct spci_memory_region *memory_region = |
| (struct spci_memory_region *)recv_buf; |
| struct spci_memory_region_constituent *constituents = |
| spci_memory_region_get_constituents(memory_region); |
| |
| EXPECT_EQ(ret.func, SPCI_MEM_DONATE_32); |
| |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| ptr = (uint8_t *)spci_memory_region_constituent_get_address( |
| &constituents[0]); |
| |
| /* Donate memory to next VM. */ |
| msg_size = spci_memory_region_init( |
| send_buf, hf_vm_get_id(), SERVICE_VM2, constituents, |
| memory_region->constituent_count, 0, 0, SPCI_MEMORY_RW_X, |
| SPCI_MEMORY_NORMAL_MEM, SPCI_MEMORY_CACHE_WRITE_BACK, |
| SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| EXPECT_EQ(spci_mem_donate(msg_size, msg_size, 0).func, SPCI_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(spci_donate_twice) |
| { |
| uint32_t msg_size; |
| struct spci_value ret = spci_msg_wait(); |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct spci_memory_region *memory_region = |
| (struct spci_memory_region *)recv_buf; |
| struct spci_memory_region_constituent constituent = |
| spci_memory_region_get_constituents(memory_region)[0]; |
| |
| EXPECT_EQ(ret.func, SPCI_MEM_DONATE_32); |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| |
| /* Yield to allow attempt to re donate from primary. */ |
| spci_yield(); |
| |
| /* Give the memory back and notify the sender. */ |
| msg_size = spci_memory_region_init( |
| send_buf, SERVICE_VM1, HF_PRIMARY_VM_ID, &constituent, 1, 0, 0, |
| SPCI_MEMORY_RW_X, SPCI_MEMORY_NORMAL_MEM, |
| SPCI_MEMORY_CACHE_WRITE_BACK, SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_EQ(spci_mem_donate(msg_size, msg_size, 0).func, SPCI_SUCCESS_32); |
| |
| /* Attempt to donate the memory to another VM. */ |
| msg_size = spci_memory_region_init( |
| send_buf, hf_vm_get_id(), SERVICE_VM2, &constituent, 1, 0, 0, |
| SPCI_MEMORY_RW_X, SPCI_MEMORY_NORMAL_MEM, |
| SPCI_MEMORY_CACHE_WRITE_BACK, SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_SPCI_ERROR(spci_mem_donate(msg_size, msg_size, 0), |
| SPCI_INVALID_PARAMETERS); |
| |
| spci_yield(); |
| } |
| |
| /** |
| * Continually receive memory, check if we have access and ensure it is not |
| * changed by a third party. |
| */ |
| TEST_SERVICE(spci_memory_receive) |
| { |
| for (;;) { |
| struct spci_value ret = spci_msg_wait(); |
| uint8_t *ptr; |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| struct spci_memory_region *memory_region = |
| (struct spci_memory_region *)recv_buf; |
| struct spci_memory_region_constituent *constituents = |
| spci_memory_region_get_constituents(memory_region); |
| |
| EXPECT_EQ(ret.func, SPCI_MEM_DONATE_32); |
| |
| ptr = (uint8_t *)spci_memory_region_constituent_get_address( |
| &constituents[0]); |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| ptr[0] = 'd'; |
| spci_yield(); |
| |
| /* Ensure memory has not changed. */ |
| EXPECT_EQ(ptr[0], 'd'); |
| spci_yield(); |
| } |
| } |
| |
| /** |
| * Receive memory and attempt to donate from primary VM. |
| */ |
| TEST_SERVICE(spci_donate_invalid_source) |
| { |
| uint32_t msg_size; |
| struct spci_value ret = spci_msg_wait(); |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct spci_memory_region *memory_region = |
| (struct spci_memory_region *)recv_buf; |
| struct spci_memory_region_constituent *constituents = |
| spci_memory_region_get_constituents(memory_region); |
| |
| EXPECT_EQ(ret.func, SPCI_MEM_DONATE_32); |
| |
| /* Give the memory back and notify the sender. */ |
| msg_size = spci_memory_region_init( |
| send_buf, hf_vm_get_id(), HF_PRIMARY_VM_ID, constituents, |
| memory_region->constituent_count, 0, 0, SPCI_MEMORY_RW_X, |
| SPCI_MEMORY_NORMAL_MEM, SPCI_MEMORY_CACHE_WRITE_BACK, |
| SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_EQ(spci_mem_donate(msg_size, msg_size, 0).func, SPCI_SUCCESS_32); |
| |
| /* Fail to donate the memory from the primary to VM2. */ |
| msg_size = spci_memory_region_init( |
| send_buf, HF_PRIMARY_VM_ID, SERVICE_VM2, constituents, |
| memory_region->constituent_count, 0, 0, SPCI_MEMORY_RW_X, |
| SPCI_MEMORY_NORMAL_MEM, SPCI_MEMORY_CACHE_WRITE_BACK, |
| SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| EXPECT_SPCI_ERROR(spci_mem_donate(msg_size, msg_size, 0), |
| SPCI_INVALID_PARAMETERS); |
| spci_yield(); |
| } |
| |
| TEST_SERVICE(spci_memory_lend_relinquish) |
| { |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| /* Loop, giving memory back to the sender. */ |
| for (;;) { |
| struct spci_value ret = spci_msg_wait(); |
| uint8_t *ptr; |
| uint8_t *ptr2; |
| uint32_t count; |
| uint32_t count2; |
| uint32_t msg_size; |
| size_t i; |
| |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct spci_memory_region *memory_region = |
| (struct spci_memory_region *)recv_buf; |
| struct spci_memory_region_constituent *constituents = |
| spci_memory_region_get_constituents(memory_region); |
| |
| /* |
| * The memory may have been sent in one of several different |
| * ways. |
| */ |
| EXPECT_TRUE(ret.func == SPCI_MEM_DONATE_32 || |
| ret.func == SPCI_MEM_LEND_32 || |
| ret.func == SPCI_MEM_SHARE_32); |
| |
| ptr = (uint8_t *)spci_memory_region_constituent_get_address( |
| &constituents[0]); |
| count = constituents[0].page_count; |
| ptr2 = (uint8_t *)spci_memory_region_constituent_get_address( |
| &constituents[1]); |
| count2 = constituents[1].page_count; |
| |
| /* 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. */ |
| msg_size = spci_memory_region_init( |
| send_buf, hf_vm_get_id(), memory_region->sender, |
| constituents, memory_region->constituent_count, 0, 0, |
| SPCI_MEMORY_RW_X, SPCI_MEMORY_NORMAL_MEM, |
| SPCI_MEMORY_CACHE_WRITE_BACK, |
| SPCI_MEMORY_OUTER_SHAREABLE); |
| /* Relevant information read, mailbox can be cleared. */ |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| EXPECT_EQ(hf_spci_mem_relinquish(msg_size, msg_size, 0).func, |
| SPCI_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(spci_memory_donate_relinquish) |
| { |
| for (;;) { |
| struct spci_value ret = spci_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 spci_memory_region *memory_region = |
| (struct spci_memory_region *)recv_buf; |
| struct spci_memory_region_constituent *constituents = |
| spci_memory_region_get_constituents(memory_region); |
| |
| EXPECT_EQ(ret.func, SPCI_MEM_DONATE_32); |
| |
| ptr = (uint8_t *)spci_memory_region_constituent_get_address( |
| &constituents[0]); |
| |
| /* 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. |
| */ |
| msg_size = spci_memory_region_init( |
| send_buf, hf_vm_get_id(), HF_PRIMARY_VM_ID, |
| constituents, memory_region->constituent_count, 0, 0, |
| SPCI_MEMORY_RW_X, SPCI_MEMORY_NORMAL_MEM, |
| SPCI_MEMORY_CACHE_WRITE_BACK, |
| SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| EXPECT_SPCI_ERROR(hf_spci_mem_relinquish(msg_size, msg_size, 0), |
| SPCI_INVALID_PARAMETERS); |
| |
| /* Ensure we still have access to the memory. */ |
| ptr[0] = 123; |
| |
| spci_yield(); |
| } |
| } |
| |
| /** |
| * Receive memory and attempt to donate from primary VM. |
| */ |
| TEST_SERVICE(spci_lend_invalid_source) |
| { |
| uint32_t msg_size; |
| struct spci_value ret = spci_msg_wait(); |
| |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct spci_memory_region *memory_region = |
| (struct spci_memory_region *)recv_buf; |
| struct spci_memory_region_constituent *constituents = |
| spci_memory_region_get_constituents(memory_region); |
| |
| EXPECT_EQ(ret.func, SPCI_MEM_LEND_32); |
| |
| /* Attempt to relinquish to this same VM. */ |
| msg_size = spci_memory_region_init( |
| send_buf, HF_PRIMARY_VM_ID, hf_vm_get_id(), constituents, |
| memory_region->constituent_count, 0, 0, SPCI_MEMORY_RW_X, |
| SPCI_MEMORY_NORMAL_MEM, SPCI_MEMORY_CACHE_WRITE_BACK, |
| SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_SPCI_ERROR(hf_spci_mem_relinquish(msg_size, msg_size, 0), |
| SPCI_INVALID_PARAMETERS); |
| |
| /* Give the memory back and notify the sender. */ |
| msg_size = spci_memory_region_init( |
| send_buf, hf_vm_get_id(), HF_PRIMARY_VM_ID, constituents, |
| memory_region->constituent_count, 0, 0, SPCI_MEMORY_RW_X, |
| SPCI_MEMORY_NORMAL_MEM, SPCI_MEMORY_CACHE_WRITE_BACK, |
| SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_EQ(hf_spci_mem_relinquish(msg_size, msg_size, 0).func, |
| SPCI_SUCCESS_32); |
| |
| /* Ensure we cannot lend from the primary to another secondary. */ |
| msg_size = spci_memory_region_init( |
| send_buf, HF_PRIMARY_VM_ID, SERVICE_VM2, constituents, |
| memory_region->constituent_count, 0, 0, SPCI_MEMORY_RW_X, |
| SPCI_MEMORY_NORMAL_MEM, SPCI_MEMORY_CACHE_WRITE_BACK, |
| SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_SPCI_ERROR(spci_mem_lend(msg_size, msg_size, 0), |
| SPCI_INVALID_PARAMETERS); |
| |
| /* Ensure we cannot share from the primary to another secondary. */ |
| msg_size = spci_memory_region_init( |
| send_buf, HF_PRIMARY_VM_ID, SERVICE_VM2, constituents, |
| memory_region->constituent_count, 0, 0, SPCI_MEMORY_RW_X, |
| SPCI_MEMORY_NORMAL_MEM, SPCI_MEMORY_CACHE_WRITE_BACK, |
| SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| EXPECT_SPCI_ERROR(spci_mem_share(msg_size, msg_size, 0), |
| SPCI_INVALID_PARAMETERS); |
| |
| spci_yield(); |
| } |
| |
| /** |
| * Attempt to execute an instruction from the lent memory. |
| */ |
| TEST_SERVICE(spci_memory_lend_relinquish_X) |
| { |
| exception_setup(NULL, exception_handler_yield_instruction_abort); |
| |
| for (;;) { |
| struct spci_value ret = spci_msg_wait(); |
| uint64_t *ptr; |
| uint32_t msg_size; |
| |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| void *send_buf = SERVICE_SEND_BUFFER(); |
| struct spci_memory_region *memory_region = |
| (struct spci_memory_region *)recv_buf; |
| struct spci_memory_region_constituent *constituents = |
| spci_memory_region_get_constituents(memory_region); |
| |
| EXPECT_EQ(ret.func, SPCI_MEM_LEND_32); |
| |
| ptr = (uint64_t *)spci_memory_region_constituent_get_address( |
| &constituents[0]); |
| /* |
| * 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. */ |
| msg_size = spci_memory_region_init( |
| send_buf, hf_vm_get_id(), HF_PRIMARY_VM_ID, |
| constituents, memory_region->constituent_count, 0, 0, |
| SPCI_MEMORY_RW_X, SPCI_MEMORY_NORMAL_MEM, |
| SPCI_MEMORY_CACHE_WRITE_BACK, |
| SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| EXPECT_EQ(hf_spci_mem_relinquish(msg_size, msg_size, 0).func, |
| SPCI_SUCCESS_32); |
| } |
| } |
| |
| /** |
| * Attempt to read and write to a shared page. |
| */ |
| TEST_SERVICE(spci_memory_lend_relinquish_RW) |
| { |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| for (;;) { |
| struct spci_value ret = spci_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 spci_memory_region *memory_region = |
| (struct spci_memory_region *)recv_buf; |
| struct spci_memory_region_constituent *constituents = |
| spci_memory_region_get_constituents(memory_region); |
| struct spci_memory_region_constituent constituent_copy = |
| constituents[0]; |
| |
| /* |
| * The memory may have been sent in one of several different |
| * ways. |
| */ |
| EXPECT_TRUE(ret.func == SPCI_MEM_DONATE_32 || |
| ret.func == SPCI_MEM_LEND_32 || |
| ret.func == SPCI_MEM_SHARE_32); |
| |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| |
| ptr = (uint8_t *)spci_memory_region_constituent_get_address( |
| &constituent_copy); |
| |
| /* 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. */ |
| spci_yield(); |
| |
| /* Attempt to modify the memory. */ |
| for (i = 0; i < PAGE_SIZE; ++i) { |
| ptr[i]++; |
| } |
| |
| msg_size = spci_memory_region_init( |
| send_buf, hf_vm_get_id(), HF_PRIMARY_VM_ID, |
| &constituent_copy, 1, 0, 0, SPCI_MEMORY_RW_X, |
| SPCI_MEMORY_NORMAL_MEM, SPCI_MEMORY_CACHE_WRITE_BACK, |
| SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_EQ(hf_spci_mem_relinquish(msg_size, msg_size, 0).func, |
| SPCI_SUCCESS_32); |
| } |
| } |
| |
| /** |
| * Attempt to modify above the upper bound for the lent memory. |
| */ |
| TEST_SERVICE(spci_lend_check_upper_bound) |
| { |
| struct spci_value ret = spci_msg_wait(); |
| uint8_t *ptr; |
| uint8_t index; |
| |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| struct spci_memory_region *memory_region = |
| (struct spci_memory_region *)recv_buf; |
| struct spci_memory_region_constituent *constituents = |
| spci_memory_region_get_constituents(memory_region); |
| |
| EXPECT_EQ(ret.func, SPCI_MEM_LEND_32); |
| |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| /* Choose which constituent we want to test. */ |
| index = *(uint8_t *)spci_memory_region_constituent_get_address( |
| &constituents[0]); |
| ptr = (uint8_t *)spci_memory_region_constituent_get_address( |
| &constituents[index]); |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| |
| /* Check that one cannot access after lent region. */ |
| ASSERT_EQ(ptr[PAGE_SIZE], 0); |
| |
| FAIL("Exception not generated by invalid access."); |
| } |
| |
| /** |
| * Attempt to modify below the lower bound for the lent memory. |
| */ |
| TEST_SERVICE(spci_lend_check_lower_bound) |
| { |
| struct spci_value ret = spci_msg_wait(); |
| uint8_t *ptr; |
| uint8_t index; |
| |
| void *recv_buf = SERVICE_RECV_BUFFER(); |
| struct spci_memory_region *memory_region = |
| (struct spci_memory_region *)recv_buf; |
| struct spci_memory_region_constituent *constituents = |
| spci_memory_region_get_constituents(memory_region); |
| |
| exception_setup(NULL, exception_handler_yield_data_abort); |
| |
| EXPECT_EQ(ret.func, SPCI_MEM_LEND_32); |
| |
| /* Choose which constituent we want to test. */ |
| index = *(uint8_t *)spci_memory_region_constituent_get_address( |
| &constituents[0]); |
| ptr = (uint8_t *)spci_memory_region_constituent_get_address( |
| &constituents[index]); |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| |
| /* Check that one cannot access after lent region. */ |
| ptr[-1]++; |
| |
| FAIL("Exception not generated by invalid access."); |
| } |
| |
| TEST_SERVICE(spci_memory_lend_twice) |
| { |
| struct spci_value ret = spci_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 spci_memory_region *memory_region = |
| (struct spci_memory_region *)recv_buf; |
| struct spci_memory_region_constituent *constituents = |
| spci_memory_region_get_constituents(memory_region); |
| struct spci_memory_region_constituent constituent_copy = |
| constituents[0]; |
| |
| /* The memory may have been sent in one of several different ways. */ |
| EXPECT_TRUE(ret.func == SPCI_MEM_DONATE_32 || |
| ret.func == SPCI_MEM_LEND_32 || |
| ret.func == SPCI_MEM_SHARE_32); |
| |
| EXPECT_EQ(spci_rx_release().func, SPCI_SUCCESS_32); |
| |
| ptr = (uint8_t *)spci_memory_region_constituent_get_address( |
| &constituent_copy); |
| |
| /* 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++) { |
| uint64_t address = (uint64_t)ptr + i; |
| constituent_copy.address_high = address << 32; |
| constituent_copy.address_low = (uint32_t)address; |
| |
| /* Fail to lend or share the memory from the primary. */ |
| msg_size = spci_memory_region_init( |
| send_buf, HF_PRIMARY_VM_ID, SERVICE_VM2, |
| &constituent_copy, 1, 0, 0, SPCI_MEMORY_RW_X, |
| SPCI_MEMORY_NORMAL_MEM, SPCI_MEMORY_CACHE_WRITE_BACK, |
| SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_SPCI_ERROR(spci_mem_lend(msg_size, msg_size, 0), |
| SPCI_INVALID_PARAMETERS); |
| msg_size = spci_memory_region_init( |
| send_buf, HF_PRIMARY_VM_ID, SERVICE_VM2, |
| &constituent_copy, 1, 0, 0, SPCI_MEMORY_RW_X, |
| SPCI_MEMORY_NORMAL_MEM, SPCI_MEMORY_CACHE_WRITE_BACK, |
| SPCI_MEMORY_OUTER_SHAREABLE); |
| EXPECT_SPCI_ERROR(spci_mem_share(msg_size, msg_size, 0), |
| SPCI_INVALID_PARAMETERS); |
| } |
| |
| /* Return control to primary. */ |
| spci_yield(); |
| } |