blob: 2116e7451f5a22ef77d25f8078e235e65688cb14 [file] [log] [blame]
// Copyright 2017 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 "mock/mock_bus_mapper.h"
#include "mock/mock_mmio.h"
#include "platform_mmio.h"
#include "ppgtt.h"
#include "registers.h"
#include "gtest/gtest.h"
class TestPerProcessGtt {
public:
class MockBusMapping : public magma::PlatformBusMapper::BusMapping {
public:
MockBusMapping(uint64_t page_offset, uint64_t page_count)
: page_offset_(page_offset), phys_addr_(page_count)
{
}
uint64_t page_offset() override { return page_offset_; }
uint64_t page_count() override { return phys_addr_.size(); }
std::vector<uint64_t>& Get() override { return phys_addr_; }
private:
uint64_t page_offset_;
std::vector<uint64_t> phys_addr_;
};
class AddressSpaceOwner : public PerProcessGtt::Owner {
public:
virtual ~AddressSpaceOwner() = default;
magma::PlatformBusMapper* GetBusMapper() override { return &bus_mapper_; }
private:
MockBusMapper bus_mapper_;
};
static uint32_t cache_bits(CachingType caching_type)
{
switch (caching_type) {
case CACHING_NONE:
return (1 << 3) | (1 << 4); // 3
case CACHING_WRITE_THROUGH:
return (1 << 4); // 3
case CACHING_LLC:
return 1 << 7; // 4
}
}
static void check_pte_entries_clear(PerProcessGtt* ppgtt, uint64_t gpu_addr, uint64_t size)
{
uint32_t page_count = size >> PAGE_SHIFT;
for (unsigned int i = 0; i < page_count; i++) {
uint64_t pte = ppgtt->get_pte(gpu_addr + i * PAGE_SIZE);
EXPECT_EQ(pte & ~(PAGE_SIZE - 1), ppgtt->pml4_table()->scratch_page_bus_addr());
EXPECT_TRUE(pte & (1 << 0)); // present
EXPECT_FALSE(pte & (1 << 1)); // not writeable
EXPECT_EQ(pte & cache_bits(CACHING_NONE), cache_bits(CACHING_NONE));
}
}
static void check_pte_entries(PerProcessGtt* ppgtt,
magma::PlatformBusMapper::BusMapping* bus_mapping,
uint64_t gpu_addr)
{
auto& bus_addr_array = bus_mapping->Get();
for (unsigned int i = 0; i < bus_addr_array.size() + PerProcessGtt::kOverfetchPageCount +
PerProcessGtt::kGuardPageCount;
i++) {
uint64_t pte = ppgtt->get_pte(gpu_addr + i * PAGE_SIZE);
if (i < bus_addr_array.size()) {
EXPECT_EQ(pte & ~(PAGE_SIZE - 1), bus_addr_array[i]);
} else {
EXPECT_EQ(pte & ~(PAGE_SIZE - 1), ppgtt->pml4_table()->scratch_page_bus_addr());
}
EXPECT_TRUE(pte & (1 << 0));
EXPECT_EQ(static_cast<bool>(pte & (1 << 1)), i < bus_addr_array.size()); // writeable
if (i < bus_addr_array.size()) {
EXPECT_EQ(pte & cache_bits(CACHING_LLC), cache_bits(CACHING_LLC));
} else {
EXPECT_EQ(pte & cache_bits(CACHING_NONE), cache_bits(CACHING_NONE));
}
}
}
static void Init()
{
auto owner = std::make_unique<AddressSpaceOwner>();
auto ppgtt = PerProcessGtt::Create(owner.get());
check_pte_entries_clear(ppgtt.get(), (1ull << 48) - PAGE_SIZE, PAGE_SIZE);
check_pte_entries_clear(ppgtt.get(), (1ull << 47) - PAGE_SIZE, PAGE_SIZE);
check_pte_entries_clear(ppgtt.get(), (1ull << 40) - PAGE_SIZE, PAGE_SIZE);
check_pte_entries_clear(ppgtt.get(), (1ull << 33) - PAGE_SIZE, PAGE_SIZE);
check_pte_entries_clear(ppgtt.get(), (1ull << 32) - PAGE_SIZE, PAGE_SIZE);
check_pte_entries_clear(ppgtt.get(), (1ull << 31) - PAGE_SIZE, PAGE_SIZE);
check_pte_entries_clear(ppgtt.get(), 0, ppgtt->Size());
}
static void Insert()
{
auto owner = std::make_unique<AddressSpaceOwner>();
auto ppgtt = PerProcessGtt::Create(owner.get());
std::vector<uint64_t> addr(2);
std::vector<std::unique_ptr<magma::PlatformBuffer>> buffer(2);
std::vector<std::unique_ptr<MockBusMapping>> bus_mapping(2);
// Placeholder occupies most of the first page directory
uint64_t placeholder_addr;
auto placeholder = magma::PlatformBuffer::Create(512 * 511 * PAGE_SIZE, "placeholder");
EXPECT_TRUE(ppgtt->Alloc(placeholder->size(), 0, &placeholder_addr));
buffer[0] = magma::PlatformBuffer::Create(513 * PAGE_SIZE, "test");
EXPECT_TRUE(ppgtt->Alloc(buffer[0]->size(), 0, &addr[0]));
buffer[1] = magma::PlatformBuffer::Create(10000, "test");
EXPECT_TRUE(ppgtt->Alloc(buffer[1]->size(), 0, &addr[1]));
bus_mapping[0] = std::make_unique<MockBusMapping>(0, buffer[0]->size() / PAGE_SIZE);
uint64_t phys_addr_base = 0xabcd1000;
for (auto& phys_addr : bus_mapping[0]->Get()) {
phys_addr = phys_addr_base += PAGE_SIZE;
}
bus_mapping[1] = std::make_unique<MockBusMapping>(0, buffer[1]->size() / PAGE_SIZE);
for (auto& phys_addr : bus_mapping[1]->Get()) {
phys_addr = phys_addr_base += PAGE_SIZE;
}
EXPECT_TRUE(ppgtt->Insert(addr[0], bus_mapping[0].get()));
check_pte_entries(ppgtt.get(), bus_mapping[0].get(), addr[0]);
EXPECT_TRUE(ppgtt->Insert(addr[1], bus_mapping[1].get()));
check_pte_entries(ppgtt.get(), bus_mapping[1].get(), addr[1]);
EXPECT_TRUE(ppgtt->Clear(addr[1], buffer[1]->size() / PAGE_SIZE));
check_pte_entries_clear(ppgtt.get(), addr[1], buffer[1]->size());
EXPECT_TRUE(ppgtt->Clear(addr[0], buffer[0]->size() / PAGE_SIZE));
check_pte_entries_clear(ppgtt.get(), addr[0], buffer[0]->size());
EXPECT_TRUE(ppgtt->Free(addr[0]));
EXPECT_TRUE(ppgtt->Free(addr[1]));
}
static void PrivatePat()
{
auto reg_io = std::make_unique<magma::RegisterIo>(MockMmio::Create(8ULL * 1024 * 1024));
PerProcessGtt::InitPrivatePat(reg_io.get());
EXPECT_EQ(0xA0907u, reg_io->Read32(registers::PatIndex::kOffsetLow));
EXPECT_EQ(0x3B2B1B0Bu, reg_io->Read32(registers::PatIndex::kOffsetHigh));
}
};
TEST(PerProcessGtt, Init) { TestPerProcessGtt::Init(); }
TEST(PerProcessGtt, Insert) { TestPerProcessGtt::Insert(); }
TEST(PerProcessGtt, PrivatePat) { TestPerProcessGtt::PrivatePat(); }