blob: 2942a97639c78528f0bc232eba3758353cec6bd0 [file] [edit]
// 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 <lib/magma_service/mock/mock_bus_mapper.h>
#include <gtest/gtest.h>
#include "src/graphics/drivers/msd-vsi-vip/src/address_space.h"
namespace {
const uint32_t kPageSize = zx_system_get_page_size();
} // namespace
class TestAddressSpace : public ::testing::Test {
public:
void SetUp() override {
address_space_ = AddressSpace::Create(&owner_, 0);
ASSERT_NE(nullptr, address_space_);
}
protected:
class MockAddressSpaceOwner : public AddressSpace::Owner {
public:
// Put bus addresses close to the 40 bit limit
MockAddressSpaceOwner() : bus_mapper_((1ul << (40 - 1))) {}
magma::PlatformBusMapper* GetBusMapper() override { return &bus_mapper_; }
void AddressSpaceReleased(AddressSpace* address_space) override {
address_space_released_ = address_space;
}
AddressSpace* address_space_released() { return address_space_released_; }
private:
MockBusMapper bus_mapper_;
AddressSpace* address_space_released_ = nullptr;
};
MockAddressSpaceOwner owner_;
std::shared_ptr<AddressSpace> address_space_;
void CheckPteEntriesClear(uint32_t gpu_addr, uint64_t page_count) {
for (unsigned int i = 0; i < page_count; i++) {
uint32_t addr = gpu_addr + i * kPageSize;
uint32_t page_table_index = (addr >>= PageShift()) & AddressSpace::kPageTableMask;
uint32_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);
}
}
}
void CheckPteEntries(magma::PlatformBusMapper::BusMapping* bus_mapping, uint32_t gpu_addr,
uint64_t mapping_page_count) {
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++) {
uint32_t addr = gpu_addr + i * kPageSize;
uint32_t page_table_index = (addr >>= PageShift()) & AddressSpace::kPageTableMask;
uint32_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);
}
}
void Insert(uint32_t gpu_addr, uint32_t size_in_pages, uint32_t mapping_page_count) {
auto buffer = magma::PlatformBuffer::Create(size_in_pages * kPageSize, "test");
auto bus_mapping = owner_.GetBusMapper()->MapPageRangeBus(buffer.get(), 0, mapping_page_count);
EXPECT_TRUE(address_space_->Insert(gpu_addr, bus_mapping.get()));
CheckPteEntries(bus_mapping.get(), gpu_addr, mapping_page_count);
}
void Clear(uint32_t gpu_addr, uint32_t size_in_pages) {
auto buffer = magma::PlatformBuffer::Create(size_in_pages * kPageSize, "test");
auto bus_mapping = owner_.GetBusMapper()->MapPageRangeBus(buffer.get(), 0, size_in_pages);
EXPECT_TRUE(address_space_->Clear(gpu_addr, bus_mapping.get()));
CheckPteEntriesClear(gpu_addr, size_in_pages);
}
void InsertAndClear(uint32_t gpu_addr, uint32_t size_in_pages, uint32_t mapping_page_count) {
auto buffer = magma::PlatformBuffer::Create(size_in_pages * kPageSize, "test");
auto bus_mapping = owner_.GetBusMapper()->MapPageRangeBus(buffer.get(), 0, mapping_page_count);
EXPECT_TRUE(address_space_->Insert(gpu_addr, bus_mapping.get()));
EXPECT_TRUE(address_space_->Clear(gpu_addr, bus_mapping.get()));
CheckPteEntriesClear(gpu_addr, mapping_page_count);
}
};
TEST_F(TestAddressSpace, Init) {
constexpr uint32_t kPageCount = 1000;
CheckPteEntriesClear(0, kPageCount);
CheckPteEntriesClear((1 << 31) - kPageCount * kPageSize, kPageCount);
}
TEST_F(TestAddressSpace, InsertAtStart) { Insert(0, 10, 10); }
TEST_F(TestAddressSpace, InsertAndClearAtStart) { InsertAndClear(0, 10, 10); }
TEST_F(TestAddressSpace, InsertAtEnd) {
Insert(static_cast<uint32_t>((1ul << 32) - kPageSize), 1, 1);
}
TEST_F(TestAddressSpace, InsertAndClearAtEnd) {
InsertAndClear(static_cast<uint32_t>((1ul << 32) - kPageSize), 1, 1);
}
TEST_F(TestAddressSpace, Clear) { Clear(0, 10); }
TEST_F(TestAddressSpace, InsertShort) { Insert(0, 10, 5); }
TEST_F(TestAddressSpace, InsertShortAndClear) { InsertAndClear(0, 10, 5); }
TEST_F(TestAddressSpace, GarbageCollect) {
uint32_t gpu_addr = 0x1000000;
uint32_t page_directory_index = (gpu_addr >> (PageShift() + 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 * kPageSize, "test");
auto bus_mapping = owner_.GetBusMapper()->MapPageRangeBus(buffer.get(), 0, size_in_pages);
// Insert 1st
EXPECT_TRUE(address_space_->Insert(gpu_addr, bus_mapping.get()));
CheckPteEntries(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 + kPageSize * size_in_pages, bus_mapping.get()));
CheckPteEntries(bus_mapping.get(), gpu_addr + kPageSize * 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, bus_mapping.get()));
CheckPteEntriesClear(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 + kPageSize * size_in_pages, bus_mapping.get()));
CheckPteEntriesClear(gpu_addr + kPageSize * 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_F(TestAddressSpace, Release) {
AddressSpace* address_space_ptr = address_space_.get();
address_space_.reset();
EXPECT_EQ(owner_.address_space_released(), address_space_ptr);
}
TEST_F(TestAddressSpace, ReleaseMapping) {
constexpr uint64_t kBufferSizeInPages = 1;
constexpr uint64_t kGpuAddr = 0x10000;
std::shared_ptr<MsdVsiBuffer> buffer =
MsdVsiBuffer::Create(kBufferSizeInPages * magma::page_size(), "test");
std::shared_ptr<GpuMapping> mapping;
magma::Status status = AddressSpace::MapBufferGpu(
address_space_, buffer, kGpuAddr, 0 /*page_offset */, kBufferSizeInPages, &mapping);
EXPECT_TRUE(status.ok());
EXPECT_TRUE(mapping->Release(nullptr /* bus_mappings_out */));
}