/* Allocate pages to hold the test code and create writable mapping */
ret = pmm_alloc(&vmm_obj, &vmm_obj_ref, alloc_len / PAGE_SIZE,
PMM_ALLOC_FLAG_CONTIGUOUS, 0);
ASSERT_EQ(NO_ERROR, ret, "pmm_alloc failed\n");
/* Populate the memory */
memcpy(ptr, mmutest_arch_nop, len);
arch_sync_cache_range((addr_t)ptr, len);
/* Now create a new mapping with the desired test arch_mmu_flags */
ret = vmm_alloc_obj(aspace, "mmutest_flags", vmm_obj, 0, alloc_len,
&execute_ptr, 0, 0, arch_mmu_flags);
ASSERT_EQ(NO_ERROR, ret, "vmm_alloc_obj failed\n");
/* Ensure the new mapping reflects the initialised memory */
EXPECT_EQ(0, memcmp(ptr, execute_ptr, alloc_len), "mapping contents mismatch\n");
/* Double check the flags are as expected on the new memory */
arch_mmu_query(&aspace->arch_aspace, (vaddr_t)execute_ptr, NULL,
&arch_mmu_flags_query);
ASSERT_EQ(arch_mmu_flags_query, arch_mmu_flags, "arch_mmu_query, 0x%x, does not match requested flags, 0x%x\n",
arch_mmu_flags_query, arch_mmu_flags);
/* Execute the test */
ret = mmutest_run_in_thread("mmu_test_execute",
mmu_test_execute_thread_func, execute_ptr);
test_abort: if (execute_ptr) { int tmp_ret = vmm_free_region(aspace, (vaddr_t)execute_ptr);
EXPECT_EQ(NO_ERROR, tmp_ret, "vmm_free_region failed\n");
}
if (ptr) { int tmp_ret = vmm_free_region(aspace, (vaddr_t)ptr);
EXPECT_EQ(NO_ERROR, tmp_ret, "vmm_free_region failed\n");
}
if (vmm_obj) {
vmm_obj_del_ref(vmm_obj, &vmm_obj_ref);
}
return ret;
}
/* Skip kernel permission tests on ARM as it uses 1MB mappings */ #if ARCH_ARM #define DISABLED_ON_ARM_NAME(name) DISABLED_##name #else #define DISABLED_ON_ARM_NAME(name) name #endif
/* Allocate last kernel aspace page. */
ptr1 = (void*)(aspace->base + (aspace->size - PAGE_SIZE));
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr1, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_START_GUARD |
VMM_FLAG_NO_END_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE); /* TODO: allow this to fail as page could already be in use */
ASSERT_EQ(NO_ERROR, ret, "vmm_alloc failed last page\n");
/* While the last page is allocated, get an object corresponding to it */
ret = vmm_get_obj(aspace, (vaddr_t)ptr1, PAGE_SIZE, &slice);
EXPECT_EQ(NO_ERROR, ret, "vmm_get_obj failed to get last page object"); /* Check the slice we got back */
EXPECT_NE(NULL, slice.obj);
EXPECT_EQ(PAGE_SIZE, slice.size);
EXPECT_EQ(NO_ERROR, slice.offset);
vmm_obj_slice_release(&slice);
/* Allocate page anywhere, while the last page is allocated. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr2, 0, 0,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(NO_ERROR, ret, "vmm_alloc failed anywhere page\n");
/* Try to allocate last kernel aspace page again, should fail */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr1, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_END_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
EXPECT_EQ(ERR_NO_MEMORY, ret, "vmm_alloc last page\n");
/* Allocate 2nd last kernel aspace page, while last page is allocated. */
ptr3 = (void*)(aspace->base + (aspace->size - 2 * PAGE_SIZE));
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr3, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_END_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE); /* TODO: allow this to fail as page could already be in use */
ASSERT_EQ(NO_ERROR, ret, "vmm_alloc failed 2nd last page\n");
/* Free allocated pages */
ret = vmm_free_region(aspace, (vaddr_t)ptr1);
EXPECT_EQ(NO_ERROR, ret, "vmm_free_region failed\n");
ret = vmm_free_region(aspace, (vaddr_t)ptr2);
EXPECT_EQ(NO_ERROR, ret, "vmm_free_region failed\n");
ret = vmm_free_region(aspace, (vaddr_t)ptr3);
EXPECT_EQ(NO_ERROR, ret, "vmm_free_region failed\n");
/* Try to allocate last page without VMM_FLAG_NO_END_GUARD flag */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr1, 0,
VMM_FLAG_VALLOC_SPECIFIC, ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(ERR_OUT_OF_RANGE, ret, "vmm_alloc succeeded unexpectedly\n");
/* Allocate and free last page */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr1, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_END_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE); /* TODO: allow this to fail as page could be in use */
ASSERT_EQ(NO_ERROR, ret, "vmm_alloc failed last page\n");
ret = vmm_free_region(aspace, (vaddr_t)ptr1);
EXPECT_EQ(NO_ERROR, ret, "vmm_free_region failed\n");
/* Allocate and free page anywhere, while last page is free */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr2, 0, 0,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(NO_ERROR, ret, "vmm_alloc failed anywhere page\n");
ret = vmm_free_region(aspace, (vaddr_t)ptr2);
EXPECT_EQ(NO_ERROR, ret, "vmm_free_region failed\n");
/* Allocate a page at a random spot with guard pages. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr1, 0, 0,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(NO_ERROR, ret);
/* Check that there are no existing adjacent allocations. */
ret = vmm_get_obj(aspace, (vaddr_t)ptr1 - PAGE_SIZE, PAGE_SIZE, &slice);
EXPECT_EQ(ERR_NOT_FOUND, ret);
vmm_obj_slice_release(&slice);
/* Check that we cannot allocate at a random spot without guard page */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr1, 0,
VMM_FLAG_NO_START_GUARD | VMM_FLAG_NO_END_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(ERR_INVALID_ARGS, ret);
/* Find a range to to more specific tests in. */
retb = vmm_find_spot(aspace, size, &base);
ASSERT_EQ(true, retb, "failed to find region for test\n");
/* Allocate first test page. */
ptr1 = (void*)base;
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr1, 0,
VMM_FLAG_VALLOC_SPECIFIC, ARCH_MMU_FLAG_PERM_NO_EXECUTE); if (ret) { /* *Thisallocationcanfailifanotherthreadallocatedthepageafter *vmm_find_spotreturnedasthatcalldoesnotreservethememory. *Setptr1toNULLsowedon'tfreememorybelongingtosomeoneelse.
*/
ptr1 = NULL;
}
ASSERT_EQ(NO_ERROR, ret);
/* Test adjacent page. Should all fail as ptr1 has guard on both sides. */
ptr2 = (void*)(base + PAGE_SIZE);
/* No flags. Should fail as both regions have a guard page. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr2, 0,
VMM_FLAG_VALLOC_SPECIFIC, 0);
ASSERT_EQ(ERR_NO_MEMORY, ret);
/* No start guard. Should fail as first region has a guard page. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr2, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_START_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(ERR_NO_MEMORY, ret);
/* No end guard. Should fail as both regions have a guard page. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr2, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_END_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(ERR_NO_MEMORY, ret);
/* No guard pages. Should fail as first region has a guard page. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr2, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_START_GUARD |
VMM_FLAG_NO_END_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(ERR_NO_MEMORY, ret);
/* Allocate page after guard page with no end guard */
ptr2 = (void*)(base + PAGE_SIZE * 2);
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr2, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_END_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE); if (ret) {
ptr2 = NULL;
}
ASSERT_EQ(NO_ERROR, ret);
/* Test page directly after ptr2 */
ptr3 = (void*)(base + PAGE_SIZE * 3);
/* No flags. Should fail as second region has a guard page. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr3, 0,
VMM_FLAG_VALLOC_SPECIFIC, ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(ERR_NO_MEMORY, ret);
/* No end guard. Should fail as second region has a guard page. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr3, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_END_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(ERR_NO_MEMORY, ret);
/* No guard pages. Should succeed as neither region has a guard page. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr3, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_START_GUARD |
VMM_FLAG_NO_END_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE); if (ret) {
ptr3 = NULL;
}
ASSERT_EQ(NO_ERROR, ret);
/* Test page directly after ptr3 */
ptr4 = (void*)(base + PAGE_SIZE * 4);
/* No flags. Should fail as second region has a guard page. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr4, 0,
VMM_FLAG_VALLOC_SPECIFIC, ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(ERR_NO_MEMORY, ret);
/* No end guard. Should fail as second region has a guard page. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr4, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_END_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(ERR_NO_MEMORY, ret);
/* No start guard. Should succeed as neither region has a guard page. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr4, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_START_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE); if (ret) {
ptr4 = NULL;
}
ASSERT_EQ(NO_ERROR, ret);
/* No flags. Should fail as both regions have a guard page. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr5, 0,
VMM_FLAG_VALLOC_SPECIFIC, ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(ERR_NO_MEMORY, ret);
/* No start guard. Should fail as first region has a guard page. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr5, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_START_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(ERR_NO_MEMORY, ret);
/* No end guard. Should fail as both regions have a guard page. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr5, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_END_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(ERR_NO_MEMORY, ret);
/* No guard pages. Should fail as first region has a guard page. */
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr5, 0,
VMM_FLAG_VALLOC_SPECIFIC | VMM_FLAG_NO_START_GUARD |
VMM_FLAG_NO_END_GUARD,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(ERR_NO_MEMORY, ret);
for (size_t i = 0; i < num_regions; i++) {
ptr[i] = NULL;
}
retb = vmm_find_spot(aspace, size, &base);
ASSERT_EQ(true, retb, "failed to find region for test\n");
for (int i = num_regions - 1; i >= 0; --i) {
ptr[i] = (void*)(base + PAGE_SIZE * i);
ret = vmm_alloc(aspace, "mmutest", PAGE_SIZE, &ptr[i], 0, vmm_flags,
ARCH_MMU_FLAG_PERM_NO_EXECUTE); if (ret) {
ptr[i] = NULL;
}
if (ptr[i]) { /* Test that we can find slice corresponding to allocated page. */
ret = vmm_get_obj(aspace, (vaddr_t)ptr[i], PAGE_SIZE, &slice);
ASSERT_EQ(NO_ERROR, ret);
vmm_obj_slice_release(&slice);
}
}
test_abort: for (size_t i = 0; i < num_regions; i++) {
vmm_free_region(aspace, (vaddr_t)ptr[i]);
}
}
INSTANTIATE_TEST_SUITE_P(aspacetype,
mmutestaspace, /* user(false) and kernel(true) aspaces */
testing_Bool());
TEST(mmutest, pan) { if (!mmutest_arch_pan_supported()) {
trusty_unittest_printf("[ INFO ] PAN is not supported\n");
GTEST_SKIP();
}
EXPECT_EQ(true, mmutest_arch_pan_enabled());
test_abort:;
}
TEST(mmutest, store_kernel) { int expected_user_rw_access; int expected_user_ro_access;
/* Allocate a single page */
ret = pmm_alloc(&vmm_obj, &vmm_obj_ref, 1, PMM_ALLOC_FLAG_CONTIGUOUS, 0);
ASSERT_EQ(NO_ERROR, ret, "pmm_alloc failed\n");
/* Try to map as w+x and check it fails */
ret = vmm_alloc_obj(aspace, "mmutest_wx", vmm_obj, 0, PAGE_SIZE, &ptr, 0, 0, 0);
EXPECT_EQ(ERR_INVALID_ARGS, ret);
/* Validate that we will reject an attempt to span two slices */
TEST_F(mmutest_slice, two_objs) {
vaddr_t base;
size_t size;
vaddr_t spot_a = _state->spot_a_2_page;
vaddr_t spot_b = _state->spot_b_1_page;
base = MIN(spot_a, spot_b);
size = MAX(spot_a, spot_b) - base + PAGE_SIZE;
/* We should not be able to create a slice spanning both objects */
EXPECT_EQ(vmm_get_obj(_state->aspace, base, size, &_state->slice),
ERR_OUT_OF_RANGE);
test_abort:;
}
/* Check we can acquire a subslice of a mapped object */
TEST_F(mmutest_slice, subobj) {
ASSERT_EQ(vmm_get_obj(_state->aspace, _state->spot_a_2_page + PAGE_SIZE,
PAGE_SIZE, &_state->slice),
NO_ERROR);
/* Check for rejection of the requested range overflows */
TEST_F(mmutest_slice, overflow) {
EXPECT_EQ(vmm_get_obj(_state->aspace, _state->spot_a_2_page, SIZE_MAX,
&_state->slice),
ERR_INVALID_ARGS);
}
/* Allocate virtual space without quota or pmm, which should pass */
ret = vmm_alloc(_state->aspace, "test_reserve",
PAGE_SIZE * (RESERVE_PAGES + 2), &ptr, 0,
VMM_FLAG_NO_PHYSICAL, ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(NO_ERROR, ret);
/* Allocate all quota pages at previous virtual address */
ret = vmm_alloc(_state->aspace, "test_from_reserved", PAGE_SIZE * 2, &ptr, 0, VMM_FLAG_QUOTA | VMM_FLAG_VALLOC_SPECIFIC,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(NO_ERROR, ret);
/* Check the maximum quota that can be allocated to an aspace */
max_pages = probe_max_aspace_quota_pages();
ASSERT_GT(max_pages, RESERVE_PAGES);
/* Reserve most pages for temp_aspace, leaving RESERVE_PAGES / 2 free */
temp_aspace_pages = max_pages - (RESERVE_PAGES / 2);
ret = vmm_create_aspace_with_quota(&temp_aspace, "temp_aspace",
PAGE_SIZE * temp_aspace_pages, 0);
ASSERT_EQ(NO_ERROR, ret);
/* Almost all pages are reserved for temp_aspace quota; this should fail */
ret = vmm_alloc(_state->aspace, "test_failure", PAGE_SIZE * RESERVE_PAGES,
&ptr_unused, 0, 0, ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(ERR_NO_MEMORY, ret);
/* Allocate from the temp_aspace quota reservation; should succeed */
ptr += PAGE_SIZE * 2;
ret = vmm_alloc(temp_aspace, "test_from_reserved_success",
PAGE_SIZE * MIN(temp_aspace_pages, RESERVE_PAGES), &ptr, 0,
VMM_FLAG_QUOTA | VMM_FLAG_VALLOC_SPECIFIC,
ARCH_MMU_FLAG_PERM_NO_EXECUTE);
ASSERT_EQ(NO_ERROR, ret);
test_abort: if (temp_aspace)
vmm_free_aspace(temp_aspace);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.