blob: f02b08b7b54696f7089e140dd0bb77dd08ed34fc [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/drivers/gpu/msd-vsl-gc/src/address_space.h"
#include "mock/mock_bus_mapper.h"
#include "gtest/gtest.h"
class TestAddressSpace {
public:
class MockAddressSpaceOwner : public AddressSpace::Owner {
public:
// Put bus addresses close to the 40 bit limit
MockAddressSpaceOwner() : bus_mapper_((1ul << (40 - 1))) {}
magma::PlatformBusMapper* bus_mapper() override { return &bus_mapper_; }
private:
MockBusMapper bus_mapper_;
};
static void check_pte_entries_clear(AddressSpace* address_space, uint64_t gpu_addr,
uint64_t page_count)
{
for (unsigned int i = 0; i < page_count; i++) {
uint64_t addr = gpu_addr + i * PAGE_SIZE;
uint64_t page_table_index = (addr >>= PAGE_SHIFT) & AddressSpace::kPageTableMask;
uint64_t page_directory_index =
(addr >>= AddressSpace::kPageTableShift) & AddressSpace::kPageDirectoryMask;
auto page_table = address_space->root_->GetPageTable(page_directory_index, false);
auto* pde = address_space->root_->entry(page_directory_index);
if (page_table) {
auto* pte = page_table->entry(page_table_index);
EXPECT_EQ(*pte, AddressSpace::kInvalidPte);
EXPECT_NE(*pde, AddressSpace::kInvalidPde);
} else {
EXPECT_EQ(*pde, AddressSpace::kInvalidPde);
}
}
}
static void check_pte_entries(AddressSpace* address_space,
magma::PlatformBusMapper::BusMapping* bus_mapping,
uint64_t gpu_addr, uint64_t mapping_page_count)
{
ASSERT_NE(address_space, nullptr);
std::vector<uint64_t>& bus_addr = bus_mapping->Get();
ASSERT_LE(mapping_page_count, bus_addr.size());
for (unsigned int i = 0; i < mapping_page_count; i++) {
uint64_t addr = gpu_addr + i * PAGE_SIZE;
uint64_t page_table_index = (addr >>= PAGE_SHIFT) & AddressSpace::kPageTableMask;
uint64_t page_directory_index =
(addr >>= AddressSpace::kPageTableShift) & AddressSpace::kPageDirectoryMask;
auto page_table = address_space->root_->GetPageTable(page_directory_index, false);
ASSERT_NE(page_table, nullptr);
auto* pde = address_space->root_->entry(page_directory_index);
EXPECT_NE(*pde, AddressSpace::kInvalidPde);
AddressSpace::pte_t expected_pte;
EXPECT_TRUE(AddressSpace::pte_encode(bus_addr[i], true, true, true, &expected_pte));
auto* pte = page_table->entry(page_table_index);
EXPECT_EQ(*pte, expected_pte);
}
}
static void Init()
{
MockAddressSpaceOwner owner;
auto address_space = AddressSpace::Create(&owner);
ASSERT_NE(nullptr, address_space);
constexpr uint32_t kPageCount = 1000;
check_pte_entries_clear(address_space.get(), 0, kPageCount);
check_pte_entries_clear(address_space.get(), (1 << 31) - kPageCount * PAGE_SIZE,
kPageCount);
}
static void Insert(AddressSpace::gpu_addr_t gpu_addr, uint32_t size_in_pages,
uint32_t mapping_page_count)
{
MockAddressSpaceOwner owner;
auto address_space = AddressSpace::Create(&owner);
auto buffer = magma::PlatformBuffer::Create(size_in_pages * PAGE_SIZE, "test");
auto bus_mapping = owner.bus_mapper()->MapPageRangeBus(buffer.get(), 0, size_in_pages);
EXPECT_TRUE(address_space->Insert(gpu_addr, bus_mapping.get(), mapping_page_count));
check_pte_entries(address_space.get(), bus_mapping.get(), gpu_addr, mapping_page_count);
}
static void Clear(AddressSpace::gpu_addr_t gpu_addr, uint32_t size_in_pages)
{
MockAddressSpaceOwner owner;
auto address_space = AddressSpace::Create(&owner);
EXPECT_TRUE(address_space->Clear(gpu_addr, size_in_pages));
check_pte_entries_clear(address_space.get(), gpu_addr, size_in_pages);
}
static void InsertAndClear(AddressSpace::gpu_addr_t gpu_addr, uint32_t size_in_pages,
uint32_t mapping_page_count)
{
MockAddressSpaceOwner owner;
auto address_space = AddressSpace::Create(&owner);
auto buffer = magma::PlatformBuffer::Create(size_in_pages * PAGE_SIZE, "test");
auto bus_mapping = owner.bus_mapper()->MapPageRangeBus(buffer.get(), 0, size_in_pages);
EXPECT_TRUE(address_space->Insert(gpu_addr, bus_mapping.get(), mapping_page_count));
EXPECT_TRUE(address_space->Clear(gpu_addr, mapping_page_count));
check_pte_entries_clear(address_space.get(), gpu_addr, mapping_page_count);
}
static void InsertManyClearOnce(AddressSpace::gpu_addr_t gpu_addr)
{
MockAddressSpaceOwner owner;
auto address_space = AddressSpace::Create(&owner);
auto buffer = magma::PlatformBuffer::Create(PAGE_SIZE, "test");
auto bus_mapping = owner.bus_mapper()->MapPageRangeBus(buffer.get(), 0, 1);
EXPECT_TRUE(address_space->Insert(gpu_addr, bus_mapping.get(), 1));
EXPECT_TRUE(address_space->Insert(gpu_addr + 2 * buffer->size(), bus_mapping.get(), 1));
EXPECT_TRUE(address_space->Insert(gpu_addr + 5 * buffer->size(), bus_mapping.get(), 1));
EXPECT_TRUE(address_space->Insert(gpu_addr + 15 * buffer->size(), bus_mapping.get(), 1));
EXPECT_TRUE(address_space->Clear(gpu_addr, 1024));
check_pte_entries_clear(address_space.get(), gpu_addr, 1024);
}
static void GarbageCollect()
{
MockAddressSpaceOwner owner;
auto address_space = AddressSpace::Create(&owner);
uint64_t gpu_addr = 0x1000000;
uint64_t page_directory_index = (gpu_addr >> (PAGE_SHIFT + AddressSpace::kPageTableShift)) &
AddressSpace::kPageDirectoryMask;
EXPECT_EQ(0u, address_space->root_->valid_count(page_directory_index));
uint32_t size_in_pages = 1024 + 1;
auto buffer = magma::PlatformBuffer::Create(size_in_pages * PAGE_SIZE, "test");
auto bus_mapping = owner.bus_mapper()->MapPageRangeBus(buffer.get(), 0, size_in_pages);
// Insert 1st
EXPECT_TRUE(address_space->Insert(gpu_addr, bus_mapping.get(), size_in_pages));
check_pte_entries(address_space.get(), bus_mapping.get(), gpu_addr, size_in_pages);
EXPECT_EQ(1024u, address_space->root_->valid_count(page_directory_index));
EXPECT_NE(nullptr, address_space->root_->GetPageTable(page_directory_index, false));
EXPECT_EQ(1u, address_space->root_->valid_count(page_directory_index + 1));
EXPECT_NE(nullptr, address_space->root_->GetPageTable(page_directory_index + 1, false));
// Insert 2nd
EXPECT_TRUE(address_space->Insert(gpu_addr + PAGE_SIZE * size_in_pages, bus_mapping.get(),
size_in_pages));
check_pte_entries(address_space.get(), bus_mapping.get(),
gpu_addr + PAGE_SIZE * size_in_pages, size_in_pages);
EXPECT_EQ(1024u, address_space->root_->valid_count(page_directory_index + 1));
EXPECT_NE(nullptr, address_space->root_->GetPageTable(page_directory_index + 1, false));
EXPECT_EQ(2u, address_space->root_->valid_count(page_directory_index + 2));
EXPECT_NE(nullptr, address_space->root_->GetPageTable(page_directory_index + 2, false));
// Clear 1st
EXPECT_TRUE(address_space->Clear(gpu_addr, size_in_pages));
check_pte_entries_clear(address_space.get(), gpu_addr, size_in_pages);
EXPECT_EQ(0u, address_space->root_->valid_count(page_directory_index));
EXPECT_EQ(nullptr, address_space->root_->GetPageTable(page_directory_index, false));
EXPECT_EQ(1023u, address_space->root_->valid_count(page_directory_index + 1));
EXPECT_NE(nullptr, address_space->root_->GetPageTable(page_directory_index + 1, false));
EXPECT_EQ(2u, address_space->root_->valid_count(page_directory_index + 2));
EXPECT_NE(nullptr, address_space->root_->GetPageTable(page_directory_index + 2, false));
// Clear 2nd
EXPECT_TRUE(address_space->Clear(gpu_addr + PAGE_SIZE * size_in_pages, size_in_pages));
check_pte_entries_clear(address_space.get(), gpu_addr + PAGE_SIZE * size_in_pages,
size_in_pages);
EXPECT_EQ(0u, address_space->root_->valid_count(page_directory_index + 1));
EXPECT_EQ(nullptr, address_space->root_->GetPageTable(page_directory_index + 1, false));
EXPECT_EQ(0u, address_space->root_->valid_count(page_directory_index + 2));
EXPECT_EQ(nullptr, address_space->root_->GetPageTable(page_directory_index + 2, false));
}
};
TEST(AddressSpace, Init) { TestAddressSpace::Init(); }
TEST(AddressSpace, InsertAtStart) { TestAddressSpace::Insert(0, 10, 10); }
TEST(AddressSpace, InsertAndClearAtStart) { TestAddressSpace::InsertAndClear(0, 10, 10); }
TEST(AddressSpace, InsertAtEnd) { TestAddressSpace::Insert((1ul << 32) - PAGE_SIZE, 1, 1); }
TEST(AddressSpace, InsertAndClearAtEnd)
{
TestAddressSpace::InsertAndClear((1ul << 32) - PAGE_SIZE, 1, 1);
}
TEST(AddressSpace, Clear) { TestAddressSpace::Clear(0, 10); }
TEST(AddressSpace, InsertShort) { TestAddressSpace::Insert(0, 10, 5); }
TEST(AddressSpace, InsertShortAndClear) { TestAddressSpace::InsertAndClear(0, 10, 5); }
TEST(AddressSpace, InsertManyClearOnce) { TestAddressSpace::InsertManyClearOnce(0); }
TEST(AddressSpace, GarbageCollect) { TestAddressSpace::GarbageCollect(); }