blob: b761ba3e4e4d457f1cd3d52563eb77cf5b39fde2 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <bits.h>
#include <lib/unittest/unittest.h>
#include <lib/zircon-internal/macros.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <arch/arm64/mmu.h>
#include <arch/aspace.h>
#include <vm/arch_vm_aspace.h>
#include <vm/physmap.h>
#include <vm/pmm.h>
#include <vm/vm_aspace.h>
namespace {
constexpr size_t kTestAspaceSize = (1UL << 48);
constexpr size_t kTestVirtualAddress = (1UL << 30); // arbitrary address
bool arm64_test_perms() {
BEGIN_TEST;
ArmArchVmAspace aspace(0, kTestAspaceSize, ArmAspaceType::kUser);
EXPECT_EQ(ZX_OK, aspace.Init());
auto map_query_test = [&](uint mmu_perms) -> bool {
paddr_t pa = 0;
size_t count;
vm_page_t* vm_page;
pmm_alloc_page(/*alloc_flags=*/0, &vm_page, &pa);
EXPECT_EQ(ZX_OK, aspace.Map(kTestVirtualAddress, &pa, 1, mmu_perms,
ArchVmAspaceInterface::ExistingEntryAction::Error, &count));
EXPECT_EQ(1u, count);
paddr_t query_pa;
uint query_flags;
EXPECT_EQ(ZX_OK, aspace.Query(kTestVirtualAddress, &query_pa, &query_flags));
EXPECT_EQ(pa, query_pa);
EXPECT_EQ(mmu_perms, query_flags);
// FUTURE ENHANCEMENT: use a private api to read the terminal page table entry
// and validate the bits that were set.
EXPECT_EQ(ZX_OK,
aspace.Unmap(kTestVirtualAddress, 1, ArchVmAspace::EnlargeOperation::No, &count));
EXPECT_EQ(1u, count);
return all_ok;
};
// map nox page, query to see that X bit isn't set
map_query_test(ARCH_MMU_FLAG_PERM_READ);
map_query_test(ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE);
map_query_test(ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_EXECUTE);
map_query_test(ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE | ARCH_MMU_FLAG_PERM_EXECUTE);
// map X page, query to see that X bit is set
map_query_test(ARCH_MMU_FLAG_PERM_USER | ARCH_MMU_FLAG_PERM_READ);
map_query_test(ARCH_MMU_FLAG_PERM_USER | ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE);
map_query_test(ARCH_MMU_FLAG_PERM_USER | ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_EXECUTE);
map_query_test(ARCH_MMU_FLAG_PERM_USER | ARCH_MMU_FLAG_PERM_READ | ARCH_MMU_FLAG_PERM_WRITE |
ARCH_MMU_FLAG_PERM_EXECUTE);
// TODO: fxbug.dev/88451 Add a more comprehensive test that reads back the page table entries
// and all the translation tables leading up to it to make sure the permission bits are set
// properly. Requires plumbing through some code to the ArmArchVmAspace to return a copy of all
// the levels of the translation tables and terminal entry.
EXPECT_EQ(ZX_OK, aspace.Destroy());
END_TEST;
}
bool arm64_test_destroy_without_init() {
BEGIN_TEST;
// See that it's OK to Destroy even if Init was never called.
ArmArchVmAspace aspace(0, kTestAspaceSize, ArmAspaceType::kUser);
ASSERT_OK(aspace.Destroy());
// See that double Destroy is also OK.
ASSERT_OK(aspace.Destroy());
END_TEST;
}
} // anonymous namespace
UNITTEST_START_TESTCASE(arm64_mmu_tests)
UNITTEST("perms", arm64_test_perms)
UNITTEST("destroy-without-init", arm64_test_destroy_without_init)
UNITTEST_END_TESTCASE(arm64_mmu_tests, "arm64_mmu", "arm64 mmu tests")