|  | // Copyright 2019 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 <gtest/gtest.h> | 
|  | #include <magma_util/address_space.h> | 
|  | #include <magma_util/gpu_mapping.h> | 
|  | #include <mock/fake_address_space.h> | 
|  | #include <mock/mock_bus_mapper.h> | 
|  |  | 
|  | using GpuMapping = magma::GpuMapping<magma::PlatformBuffer>; | 
|  | using AllocatingAddressSpace = | 
|  | FakeAllocatingAddressSpace<GpuMapping, magma::AddressSpace<GpuMapping>>; | 
|  | using NonAllocatingAddressSpace = | 
|  | FakeNonAllocatingAddressSpace<GpuMapping, magma::AddressSpace<GpuMapping>>; | 
|  |  | 
|  | // Testing for classes AddressSpace and, indirectly, GpuMapping | 
|  | class TestAddressSpace : public ::testing::Test { | 
|  | public: | 
|  | class AddressSpaceOwner : public magma::AddressSpaceOwner { | 
|  | public: | 
|  | magma::PlatformBusMapper* GetBusMapper() override { return &bus_mapper_; } | 
|  |  | 
|  | MockBusMapper bus_mapper_; | 
|  | }; | 
|  | }; | 
|  |  | 
|  | TEST_F(TestAddressSpace, AddMapping) { | 
|  | constexpr uint32_t kPageCount = 5; | 
|  | constexpr uint32_t kGpuAddr = 0x1000;    // arbitrary | 
|  | constexpr uint32_t kGpuAddr2 = 0x10000;  // non overlapped | 
|  |  | 
|  | auto owner = std::make_unique<AddressSpaceOwner>(); | 
|  | auto address_space = std::make_shared<NonAllocatingAddressSpace>(owner.get(), UINT32_MAX); | 
|  | auto buffer = std::shared_ptr<magma::PlatformBuffer>( | 
|  | magma::PlatformBuffer::Create(kPageCount * magma::page_size(), "Test")); | 
|  |  | 
|  | std::shared_ptr<GpuMapping> gpu_mapping; | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr,    // gpu addr | 
|  | 0,           // page offset | 
|  | kPageCount,  // page count | 
|  | &gpu_mapping)); | 
|  | EXPECT_EQ(2u, buffer.use_count()); | 
|  | EXPECT_EQ(kGpuAddr, gpu_mapping->gpu_addr()); | 
|  | EXPECT_EQ(0u, gpu_mapping->offset()); | 
|  | EXPECT_EQ(kPageCount * magma::page_size(), gpu_mapping->length()); | 
|  |  | 
|  | EXPECT_TRUE(address_space->AddMapping(gpu_mapping)); | 
|  |  | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr2,   // gpu addr | 
|  | 0,           // page offset | 
|  | kPageCount,  // page count | 
|  | &gpu_mapping)); | 
|  | EXPECT_EQ(3u, buffer.use_count()); | 
|  | EXPECT_EQ(kGpuAddr2, gpu_mapping->gpu_addr()); | 
|  | EXPECT_EQ(0u, gpu_mapping->offset()); | 
|  | EXPECT_EQ(kPageCount * magma::page_size(), gpu_mapping->length()); | 
|  |  | 
|  | EXPECT_TRUE(address_space->AddMapping(gpu_mapping)); | 
|  | } | 
|  |  | 
|  | TEST_F(TestAddressSpace, OverlappedMapping) { | 
|  | constexpr uint32_t kPageCount = 2; | 
|  | constexpr uint32_t kGpuAddr = 0x1000;  // arbitrary | 
|  |  | 
|  | auto owner = std::make_unique<AddressSpaceOwner>(); | 
|  | auto address_space = std::make_shared<NonAllocatingAddressSpace>(owner.get(), UINT32_MAX); | 
|  | auto buffer = std::shared_ptr<magma::PlatformBuffer>( | 
|  | magma::PlatformBuffer::Create(kPageCount * magma::page_size(), "Test")); | 
|  |  | 
|  | std::shared_ptr<GpuMapping> gpu_mapping; | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr,    // gpu addr | 
|  | 0,           // page offset | 
|  | kPageCount,  // page count | 
|  | &gpu_mapping)); | 
|  | EXPECT_TRUE(address_space->AddMapping(gpu_mapping)); | 
|  |  | 
|  | // Overlapped existing mappings should fail | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr - magma::page_size(),  // gpu addr | 
|  | 0,                              // page offset | 
|  | kPageCount,                     // page count | 
|  | &gpu_mapping)); | 
|  | EXPECT_FALSE(address_space->AddMapping(gpu_mapping)); | 
|  |  | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr + magma::page_size(),  // gpu addr | 
|  | 0,                              // page offset | 
|  | kPageCount,                     // page count | 
|  | &gpu_mapping)); | 
|  | EXPECT_FALSE(address_space->AddMapping(gpu_mapping)); | 
|  | } | 
|  |  | 
|  | TEST_F(TestAddressSpace, ReleaseMapping) { | 
|  | constexpr uint32_t kPageCount = 1; | 
|  | constexpr uint32_t kGpuAddr = 0x1000;  // arbitrary | 
|  |  | 
|  | auto owner = std::make_unique<AddressSpaceOwner>(); | 
|  | auto address_space = std::make_shared<NonAllocatingAddressSpace>(owner.get(), UINT32_MAX); | 
|  | auto buffer = std::shared_ptr<magma::PlatformBuffer>( | 
|  | magma::PlatformBuffer::Create(kPageCount * magma::page_size(), "Test")); | 
|  |  | 
|  | std::shared_ptr<GpuMapping> gpu_mapping; | 
|  | EXPECT_FALSE(address_space->ReleaseMapping(buffer.get(), kGpuAddr, &gpu_mapping)); | 
|  |  | 
|  | EXPECT_FALSE(address_space->FindGpuMapping(kGpuAddr)); | 
|  |  | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr,    // gpu addr | 
|  | 0,           // page offset | 
|  | kPageCount,  // page count | 
|  | &gpu_mapping)); | 
|  | EXPECT_EQ(2u, buffer.use_count()); | 
|  | EXPECT_TRUE(address_space->AddMapping(gpu_mapping)); | 
|  |  | 
|  | EXPECT_TRUE(address_space->FindGpuMapping(kGpuAddr)); | 
|  |  | 
|  | EXPECT_TRUE(address_space->ReleaseMapping(buffer.get(), kGpuAddr, &gpu_mapping)); | 
|  | gpu_mapping.reset(); | 
|  | EXPECT_EQ(1u, buffer.use_count()); | 
|  |  | 
|  | EXPECT_FALSE(address_space->FindGpuMapping(kGpuAddr)); | 
|  |  | 
|  | // Validate we can re-map | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr,    // gpu addr | 
|  | 0,           // page offset | 
|  | kPageCount,  // page count | 
|  | &gpu_mapping)); | 
|  | EXPECT_EQ(2u, buffer.use_count()); | 
|  |  | 
|  | EXPECT_FALSE(address_space->FindGpuMapping(kGpuAddr)); | 
|  | } | 
|  |  | 
|  | TEST_F(TestAddressSpace, ReleaseBuffer) { | 
|  | constexpr uint32_t kPageCount = 1; | 
|  | constexpr uint32_t kGpuAddr = 0x1000;  // arbitrary | 
|  |  | 
|  | auto owner = std::make_unique<AddressSpaceOwner>(); | 
|  | auto address_space = std::make_shared<NonAllocatingAddressSpace>(owner.get(), UINT32_MAX); | 
|  | auto buffer = std::shared_ptr<magma::PlatformBuffer>( | 
|  | magma::PlatformBuffer::Create(kPageCount * magma::page_size(), "Test")); | 
|  |  | 
|  | std::shared_ptr<GpuMapping> gpu_mapping; | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr,    // gpu addr | 
|  | 0,           // page offset | 
|  | kPageCount,  // page count | 
|  | &gpu_mapping)); | 
|  | EXPECT_EQ(2u, buffer.use_count()); | 
|  | EXPECT_TRUE(address_space->AddMapping(gpu_mapping)); | 
|  |  | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu( | 
|  | address_space, buffer, | 
|  | kGpuAddr + kPageCount * magma::page_size(),  // gpu addr | 
|  | 0,                                           // page offset | 
|  | kPageCount,                                  // page count | 
|  | &gpu_mapping)); | 
|  | EXPECT_EQ(3u, buffer.use_count()); | 
|  | EXPECT_TRUE(address_space->AddMapping(gpu_mapping)); | 
|  |  | 
|  | EXPECT_TRUE( | 
|  | NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr + 10 * magma::page_size(),  // gpu addr | 
|  | 0,                                   // page offset | 
|  | kPageCount,                          // page count | 
|  | &gpu_mapping)); | 
|  | EXPECT_EQ(4u, buffer.use_count()); | 
|  | EXPECT_TRUE(address_space->AddMapping(gpu_mapping)); | 
|  |  | 
|  | std::vector<std::shared_ptr<GpuMapping>> released_mappings; | 
|  | address_space->ReleaseBuffer(buffer.get(), &released_mappings); | 
|  |  | 
|  | EXPECT_EQ(3u, released_mappings.size()); | 
|  | released_mappings.clear(); | 
|  | gpu_mapping.reset(); | 
|  | EXPECT_EQ(1u, buffer.use_count()); | 
|  |  | 
|  | // Validate we can re-use the same addresses | 
|  | buffer = std::shared_ptr<magma::PlatformBuffer>( | 
|  | magma::PlatformBuffer::Create(kPageCount * magma::page_size(), "Test")); | 
|  |  | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr,    // gpu addr | 
|  | 0,           // page offset | 
|  | kPageCount,  // page count | 
|  | &gpu_mapping)); | 
|  | EXPECT_TRUE(address_space->AddMapping(gpu_mapping)); | 
|  |  | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu( | 
|  | address_space, buffer, | 
|  | kGpuAddr + kPageCount * magma::page_size(),  // gpu addr | 
|  | 0,                                           // page offset | 
|  | kPageCount,                                  // page count | 
|  | &gpu_mapping)); | 
|  | EXPECT_TRUE(address_space->AddMapping(gpu_mapping)); | 
|  |  | 
|  | EXPECT_TRUE( | 
|  | NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr + 10 * magma::page_size(),  // gpu addr | 
|  | 0,                                   // page offset | 
|  | kPageCount,                          // page count | 
|  | &gpu_mapping)); | 
|  | } | 
|  |  | 
|  | TEST_F(TestAddressSpace, AllocatingMap) { | 
|  | constexpr uint32_t kPageCount = 1; | 
|  | constexpr uint32_t kStartAddr = 0x1000; | 
|  |  | 
|  | auto owner = std::make_unique<AddressSpaceOwner>(); | 
|  | auto address_space = | 
|  | std::make_shared<AllocatingAddressSpace>(owner.get(), kStartAddr, UINT32_MAX - kStartAddr); | 
|  | auto buffer = std::shared_ptr<magma::PlatformBuffer>( | 
|  | magma::PlatformBuffer::Create(kPageCount * magma::page_size(), "Test")); | 
|  |  | 
|  | std::shared_ptr<GpuMapping> gpu_mapping = | 
|  | AllocatingAddressSpace::MapBufferGpu(address_space, buffer); | 
|  | EXPECT_TRUE(gpu_mapping); | 
|  | EXPECT_EQ(kStartAddr, gpu_mapping->gpu_addr()); | 
|  | EXPECT_EQ(2u, buffer.use_count()); | 
|  | EXPECT_TRUE(address_space->AddMapping(gpu_mapping)); | 
|  | } | 
|  |  | 
|  | TEST_F(TestAddressSpace, FindMapping) { | 
|  | constexpr uint32_t kPageCount = 5; | 
|  | constexpr uint32_t kPageOffset = 1; | 
|  | constexpr uint32_t kMappingPageCount = kPageCount - kPageOffset; | 
|  | constexpr uint32_t kGpuAddr = 0x1000;  // arbitrary | 
|  |  | 
|  | auto owner = std::make_unique<AddressSpaceOwner>(); | 
|  | auto address_space = std::make_shared<NonAllocatingAddressSpace>(owner.get(), UINT32_MAX); | 
|  | auto buffer = std::shared_ptr<magma::PlatformBuffer>( | 
|  | magma::PlatformBuffer::Create(kPageCount * magma::page_size(), "Test")); | 
|  |  | 
|  | std::shared_ptr<GpuMapping> gpu_mapping; | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr,           // gpu addr | 
|  | kPageOffset,        // page offset | 
|  | kMappingPageCount,  // page count | 
|  | &gpu_mapping)); | 
|  | EXPECT_TRUE(address_space->AddMapping(gpu_mapping)); | 
|  |  | 
|  | EXPECT_TRUE(address_space->FindGpuMapping(kGpuAddr)); | 
|  | EXPECT_TRUE(address_space->FindGpuMapping(buffer.get(), kPageOffset * magma::page_size(), | 
|  | kMappingPageCount * magma::page_size())); | 
|  | EXPECT_TRUE(address_space->FindGpuMapping(buffer.get(), kPageOffset * magma::page_size(), | 
|  | (kMappingPageCount - 1) * magma::page_size())); | 
|  |  | 
|  | // Incorrect page offset | 
|  | EXPECT_FALSE( | 
|  | address_space->FindGpuMapping(buffer.get(), 0, kMappingPageCount * magma::page_size())); | 
|  |  | 
|  | // Incorrect page count | 
|  | EXPECT_FALSE(address_space->FindGpuMapping(buffer.get(), kPageOffset * magma::page_size(), | 
|  | (kMappingPageCount + 1) * magma::page_size())); | 
|  | } | 
|  |  | 
|  | TEST_F(TestAddressSpace, GrowMapping) { | 
|  | constexpr uint32_t kGpuAddr = 0x1000; | 
|  | constexpr uint32_t kSpaceSizeInPages = 10; | 
|  | constexpr uint32_t kBufferSizeInPages = 8; | 
|  | constexpr uint32_t kBufferPagesToGrow = 1; | 
|  |  | 
|  | auto owner = std::make_unique<AddressSpaceOwner>(); | 
|  | auto address_space = std::make_shared<NonAllocatingAddressSpace>( | 
|  | owner.get(), magma::page_size() * kSpaceSizeInPages); | 
|  | auto buffer = std::shared_ptr<magma::PlatformBuffer>( | 
|  | magma::PlatformBuffer::Create(kBufferSizeInPages * magma::page_size(), "Test")); | 
|  |  | 
|  | std::shared_ptr<GpuMapping> mapping; | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu( | 
|  | address_space, buffer, | 
|  | kGpuAddr,                                 // gpu addr | 
|  | 0,                                        // page offset | 
|  | kBufferSizeInPages - kBufferPagesToGrow,  // page count | 
|  | &mapping)); | 
|  | ASSERT_TRUE(mapping); | 
|  |  | 
|  | uint64_t orig_length = (kBufferSizeInPages - kBufferPagesToGrow) * magma::page_size(); | 
|  | EXPECT_EQ(mapping->length(), orig_length); | 
|  |  | 
|  | EXPECT_TRUE(address_space->GrowMapping(mapping.get(), kBufferPagesToGrow)); | 
|  | EXPECT_EQ(mapping->length(), kBufferSizeInPages * magma::page_size()); | 
|  |  | 
|  | // Can't map on top of grown area | 
|  | std::shared_ptr<GpuMapping> mapping2; | 
|  | EXPECT_FALSE(NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr + orig_length,  // gpu addr | 
|  | 0,                       // page offset | 
|  | kBufferSizeInPages,      // page count | 
|  | &mapping2)); | 
|  |  | 
|  | std::vector<std::unique_ptr<magma::PlatformBusMapper::BusMapping>> mappings; | 
|  | EXPECT_TRUE(mapping->Release(&mappings)); | 
|  | EXPECT_EQ(2u, mappings.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(TestAddressSpace, GrowMappingErrorOutsideBuffer) { | 
|  | constexpr uint32_t kGpuAddr = 0x1000; | 
|  | constexpr uint32_t kSpaceSizeInPages = 10; | 
|  | constexpr uint32_t kBufferSizeInPages = 8; | 
|  | constexpr uint32_t kBufferPagesToGrow = 1; | 
|  |  | 
|  | auto owner = std::make_unique<AddressSpaceOwner>(); | 
|  | auto address_space = std::make_shared<NonAllocatingAddressSpace>( | 
|  | owner.get(), magma::page_size() * kSpaceSizeInPages); | 
|  | auto buffer = std::shared_ptr<magma::PlatformBuffer>( | 
|  | magma::PlatformBuffer::Create(kBufferSizeInPages * magma::page_size(), "Test")); | 
|  |  | 
|  | std::shared_ptr<GpuMapping> mapping; | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr,            // gpu addr | 
|  | 0,                   // page offset | 
|  | kBufferSizeInPages,  // page count | 
|  | &mapping)); | 
|  | ASSERT_TRUE(mapping); | 
|  |  | 
|  | EXPECT_FALSE(address_space->GrowMapping(mapping.get(), kBufferPagesToGrow)); | 
|  | } | 
|  |  | 
|  | TEST_F(TestAddressSpace, GrowMappingErrorOutsideSpace) { | 
|  | constexpr uint32_t kGpuAddr = 0; | 
|  | constexpr uint32_t kSpaceSizeInPages = 10; | 
|  | constexpr uint32_t kBufferSizeInPages = 12; | 
|  | constexpr uint32_t kBufferPagesToGrow = 1; | 
|  |  | 
|  | auto owner = std::make_unique<AddressSpaceOwner>(); | 
|  | auto address_space = std::make_shared<NonAllocatingAddressSpace>( | 
|  | owner.get(), magma::page_size() * kSpaceSizeInPages); | 
|  | auto buffer = std::shared_ptr<magma::PlatformBuffer>( | 
|  | magma::PlatformBuffer::Create(kBufferSizeInPages * magma::page_size(), "Test")); | 
|  |  | 
|  | std::shared_ptr<GpuMapping> mapping; | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr,           // gpu addr | 
|  | 0,                  // page offset | 
|  | kSpaceSizeInPages,  // page count | 
|  | &mapping)); | 
|  | ASSERT_TRUE(mapping); | 
|  |  | 
|  | EXPECT_FALSE(address_space->GrowMapping(mapping.get(), kBufferPagesToGrow)); | 
|  | } | 
|  |  | 
|  | TEST_F(TestAddressSpace, GrowMappingErrorOverlapped) { | 
|  | constexpr uint32_t kGpuAddr = 0x1000;  // arbitrary | 
|  | constexpr uint32_t kSpaceSizeInPages = 10; | 
|  | constexpr uint32_t kBufferSizeInPages = 4; | 
|  | constexpr uint32_t kBufferPagesToGrow = 1; | 
|  |  | 
|  | auto owner = std::make_unique<AddressSpaceOwner>(); | 
|  | auto address_space = std::make_shared<NonAllocatingAddressSpace>( | 
|  | owner.get(), magma::page_size() * kSpaceSizeInPages); | 
|  | auto buffer = std::shared_ptr<magma::PlatformBuffer>( | 
|  | magma::PlatformBuffer::Create(kBufferSizeInPages * magma::page_size(), "Test")); | 
|  |  | 
|  | std::shared_ptr<GpuMapping> mapping; | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu(address_space, buffer, | 
|  | kGpuAddr,            // gpu addr | 
|  | 0,                   // page offset | 
|  | kBufferSizeInPages,  // page count | 
|  | &mapping)); | 
|  | ASSERT_TRUE(mapping); | 
|  |  | 
|  | std::shared_ptr<GpuMapping> mapping2; | 
|  | EXPECT_TRUE(NonAllocatingAddressSpace::MapBufferGpu( | 
|  | address_space, buffer, | 
|  | kGpuAddr + kBufferSizeInPages * magma::page_size(),  // gpu addr | 
|  | 0,                                                   // page offset | 
|  | kBufferSizeInPages,                                  // page count | 
|  | &mapping2)); | 
|  | ASSERT_TRUE(mapping2); | 
|  |  | 
|  | EXPECT_FALSE(address_space->GrowMapping(mapping.get(), kBufferPagesToGrow)); | 
|  | } |