blob: 396bffb206914ef1c3d46a35ad64c12ca6d2640f [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 "lookup.h"
#include <lib/page-table/types.h>
#include <stdlib.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "mmu.h"
#include "testing/test_util.h"
namespace page_table::x86 {
namespace {
// GMock matcher to check that a std::optional<LookupResult> evaluates to the given
// physical address.
MATCHER_P(MapsToPaddr, paddr, "") { return arg.has_value() && arg->phys_addr == Paddr(paddr); }
TEST(LookupPage, LookupZero) {
TestMemoryManager allocator;
PageTableNode nodes[4];
// Construct the 4 kiB page at vaddr 0.
nodes[0].set(0, PageTableEntry{}
.set_present(1)
.set_is_page(/*level=*/3, false)
.set_child_paddr(PaddrOf(&nodes[1]).value()));
nodes[1].set(0, PageTableEntry{}
.set_present(1)
.set_is_page(/*level=*/2, false)
.set_child_paddr(PaddrOf(&nodes[2]).value()));
nodes[2].set(0, PageTableEntry{}
.set_present(1)
.set_is_page(/*level=*/1, false)
.set_child_paddr(PaddrOf(&nodes[3]).value()));
nodes[3].set(0, PageTableEntry{}
.set_present(1)
.set_is_page(/*level=*/0, true)
.set_page_paddr(/*level=*/0, 0xabcd'e000));
// Ensure the returned physical addresses are valid.
EXPECT_THAT(LookupPage(allocator, &nodes[0], Vaddr(0x0)), MapsToPaddr(0xabcd'e000));
EXPECT_THAT(LookupPage(allocator, &nodes[0], Vaddr(0x123)), MapsToPaddr(0xabcd'e123));
EXPECT_THAT(LookupPage(allocator, &nodes[0], Vaddr(0xfff)), MapsToPaddr(0xabcd'efff));
EXPECT_EQ(LookupPage(allocator, &nodes[0], Vaddr(0x1000)), std::nullopt);
// Ensure that the returned level and PTE values are correct.
std::optional<LookupResult> result = LookupPage(allocator, &nodes[0], Vaddr(0x0));
ASSERT_TRUE(result.has_value());
EXPECT_EQ(result->phys_addr, Paddr(0xabcd'e000));
EXPECT_EQ(result->level, 0);
EXPECT_EQ(result->entry, nodes[3].at(0));
}
TEST(LookupPage, LookupLast) {
TestMemoryManager allocator;
PageTableNode nodes[4];
// Construct the 4 kiB page at vaddr 0xffff'ffff'ffff'f000.
nodes[0].set(kEntriesPerNode - 1, PageTableEntry{}
.set_present(1)
.set_is_page(/*level=*/3, false)
.set_child_paddr(PaddrOf(&nodes[1]).value()));
nodes[1].set(kEntriesPerNode - 1, PageTableEntry{}
.set_present(1)
.set_is_page(/*level=*/2, false)
.set_child_paddr(PaddrOf(&nodes[2]).value()));
nodes[2].set(kEntriesPerNode - 1, PageTableEntry{}
.set_present(1)
.set_is_page(/*level=*/1, false)
.set_child_paddr(PaddrOf(&nodes[3]).value()));
nodes[3].set(kEntriesPerNode - 1, PageTableEntry{}
.set_present(1)
.set_is_page(/*level=*/0, true)
.set_page_paddr(/*level=*/0, 0xabcd'e000));
EXPECT_THAT(LookupPage(allocator, &nodes[0], Vaddr(0xffff'ffff'ffff'f000)),
MapsToPaddr(0xabcd'e000));
EXPECT_THAT(LookupPage(allocator, &nodes[0], Vaddr(0xffff'ffff'ffff'f123)),
MapsToPaddr(0xabcd'e123));
EXPECT_THAT(LookupPage(allocator, &nodes[0], Vaddr(0xffff'ffff'ffff'ffff)),
MapsToPaddr(0xabcd'efff));
EXPECT_EQ(LookupPage(allocator, &nodes[0], Vaddr(0xffff'ffff'ffff'efff)), std::nullopt);
}
TEST(LookupPage, LookupLargePages) {
TestMemoryManager allocator;
PageTableNode nodes[4];
// Construct the 4 kiB page at vaddr 0.
nodes[0].set(0, PageTableEntry{}
.set_present(1)
.set_is_page(/*level=*/3, false)
.set_child_paddr(PaddrOf(&nodes[1]).value()));
nodes[1].set(0, PageTableEntry{}
.set_present(1)
.set_is_page(/*level=*/2, true)
.set_page_paddr(/*level=*/2, 0x000a'bcde'c000'0000));
// Expect the lookup to return the correct address, level, and PTE.
std::optional<LookupResult> result = LookupPage(allocator, &nodes[0], Vaddr(0x0000'0000u));
ASSERT_TRUE(result.has_value());
EXPECT_THAT(result, MapsToPaddr(0x000a'bcde'c000'0000));
EXPECT_EQ(result->level, 2);
EXPECT_EQ(result->entry, nodes[1].at(0));
// Also check the last byte of the page.
result = LookupPage(allocator, &nodes[0], Vaddr(0x3fff'ffffu));
ASSERT_TRUE(result.has_value());
EXPECT_THAT(result, MapsToPaddr(0x000a'bcde'ffff'ffff));
EXPECT_EQ(result->level, 2);
EXPECT_EQ(result->entry, nodes[1].at(0));
}
TEST(MapPage, SingleMapping) {
TestMemoryManager allocator;
PageTableNode pml4;
EXPECT_EQ(MapPage(allocator, &pml4, Vaddr(0x0000'1234'5678'9000), Paddr(0x0001'2345'6789'a000),
/*page_size=*/PageSize::k4KiB),
ZX_OK);
EXPECT_THAT(LookupPage(allocator, &pml4, Vaddr(0x0000'1234'5678'9000)),
MapsToPaddr(0x0001'2345'6789'a000u));
}
TEST(MapPage, ReplaceMapping) {
TestMemoryManager allocator;
PageTableNode pml4{};
// Attempt to map the same vaddr twice.
EXPECT_EQ(MapPage(allocator, &pml4, Vaddr(0x0), Paddr(0xaaaa'0000),
/*page_size=*/PageSize::k4KiB),
ZX_OK);
EXPECT_EQ(MapPage(allocator, &pml4, Vaddr(0x0), Paddr(0xbbbb'0000),
/*page_size=*/PageSize::k4KiB),
ZX_ERR_ALREADY_EXISTS);
// Should still have the original mapping.
EXPECT_THAT(LookupPage(allocator, &pml4, Vaddr(0)), MapsToPaddr(0xaaaa'0000));
}
TEST(MapPage, MultipleMappings) {
TestMemoryManager allocator;
PageTableNode pml4{};
EXPECT_EQ(MapPage(allocator, &pml4, Vaddr(0x0000), Paddr(0xaaaa'0000),
/*page_size=*/PageSize::k4KiB),
ZX_OK);
EXPECT_EQ(MapPage(allocator, &pml4, Vaddr(0x1000), Paddr(0xbbbb'0000),
/*page_size=*/PageSize::k4KiB),
ZX_OK);
EXPECT_THAT(LookupPage(allocator, &pml4, Vaddr(0x0000)), MapsToPaddr(0xaaaa'0000));
EXPECT_THAT(LookupPage(allocator, &pml4, Vaddr(0x1000)), MapsToPaddr(0xbbbb'0000));
}
TEST(MapPage, LargePage) {
TestMemoryManager allocator;
PageTableNode pml4{};
// Map in a 2MiB page.
EXPECT_EQ(MapPage(allocator, &pml4, Vaddr(0x0000), Paddr(0xaaa0'0000),
/*page_size=*/PageSize::k2MiB),
ZX_OK);
// We shouldn't be able to map in a smaller page in the middle.
EXPECT_EQ(MapPage(allocator, &pml4, Vaddr(0x1000), Paddr(0xbbbb'0000),
/*page_size=*/PageSize::k4KiB),
ZX_ERR_ALREADY_EXISTS);
// We should be able to lookup different parts of the page.
EXPECT_THAT(LookupPage(allocator, &pml4, Vaddr(0x0000)), MapsToPaddr(0xaaa0'0000u));
EXPECT_THAT(LookupPage(allocator, &pml4, Vaddr(0x1000)), MapsToPaddr(0xaaa0'1000u));
EXPECT_THAT(LookupPage(allocator, &pml4, Vaddr(0x1f'ffff)), MapsToPaddr(0xaabf'ffffu));
EXPECT_EQ(LookupPage(allocator, &pml4, Vaddr(0x20'0000)), std::nullopt);
}
} // namespace
} // namespace page_table::x86