blob: fa405efefed0183ec51f9ecf8fef30a81e7134b1 [file] [log] [blame]
// 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 <fidl/fuchsia.hardware.goldfish/cpp/wire.h>
#include <fidl/fuchsia.sysmem/cpp/wire.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fdio.h>
#include <lib/zx/channel.h>
#include <lib/zx/time.h>
#include <lib/zx/vmar.h>
#include <lib/zx/vmo.h>
#include <unistd.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#include <gtest/gtest.h>
#include "src/lib/fsl/handles/object_info.h"
namespace {
// TODO(https://fxbug.dev/42065067): Stop hardcoding the 000 in this path.
zx::result<fidl::ClientEnd<fuchsia_hardware_goldfish::Controller>> ConnectToPipe() {
return component::Connect<fuchsia_hardware_goldfish::Controller>("/dev/class/goldfish-pipe/000");
}
// TODO(https://fxbug.dev/42065067): Stop hardcoding the 000 in this path.
zx::result<fidl::ClientEnd<fuchsia_hardware_goldfish::ControlDevice>> ConnectToControl() {
return component::Connect<fuchsia_hardware_goldfish::ControlDevice>(
"/dev/class/goldfish-control/000");
}
// TODO(https://fxbug.dev/42065067): Stop hardcoding the 000 in this path.
zx::result<fidl::ClientEnd<fuchsia_hardware_goldfish::AddressSpaceDevice>> ConnectToAddress() {
return component::Connect<fuchsia_hardware_goldfish::AddressSpaceDevice>(
"/dev/class/goldfish-address-space/000");
}
fidl::WireSyncClient<fuchsia_sysmem::Allocator> CreateSysmemAllocator() {
zx::result client_end = component::Connect<fuchsia_sysmem::Allocator>();
EXPECT_EQ(client_end.status_value(), ZX_OK);
if (!client_end.is_ok()) {
return {};
}
fidl::WireSyncClient allocator(std::move(*client_end));
// TODO(https://fxbug.dev/42180237) Consider handling the error instead of ignoring it.
(void)allocator->SetDebugClientInfo(fidl::StringView::FromExternal(fsl::GetCurrentProcessName()),
fsl::GetCurrentProcessKoid());
return allocator;
}
void SetDefaultCollectionName(fidl::WireSyncClient<fuchsia_sysmem::BufferCollection>& collection) {
constexpr uint32_t kTestNamePriority = 1000u;
std::string test_name = ::testing::UnitTest::GetInstance()->current_test_info()->name();
EXPECT_TRUE(
collection->SetName(kTestNamePriority, fidl::StringView::FromExternal(test_name)).ok());
}
} // namespace
TEST(GoldfishPipeTests, GoldfishPipeTest) {
zx::result controller = ConnectToPipe();
ASSERT_EQ(controller.status_value(), ZX_OK);
auto [channel, server] = fidl::Endpoints<fuchsia_hardware_goldfish::PipeDevice>::Create();
ASSERT_EQ(fidl::WireCall(controller.value())->OpenSession(std::move(server)).status(), ZX_OK);
auto [pipe_client, pipe_server] = fidl::Endpoints<fuchsia_hardware_goldfish::Pipe>::Create();
fidl::WireSyncClient pipe_device(std::move(channel));
ASSERT_EQ(pipe_device->OpenPipe(std::move(pipe_server)).status(), ZX_OK);
fidl::WireSyncClient pipe(std::move(pipe_client));
const size_t kSize = 3 * 4096;
{
auto result = pipe->SetBufferSize(kSize);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
}
zx::vmo vmo;
{
auto result = pipe->GetBuffer();
ASSERT_TRUE(result.ok());
vmo = std::move(result->vmo);
}
// Connect to pingpong service.
constexpr char kPipeName[] = "pipe:pingpong";
size_t bytes = strlen(kPipeName) + 1;
EXPECT_EQ(vmo.write(kPipeName, 0, bytes), ZX_OK);
{
auto result = pipe->Write(bytes, 0);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
EXPECT_EQ(result->actual, bytes);
}
// Write 1 byte.
const uint8_t kSentinel = 0xaa;
EXPECT_EQ(vmo.write(&kSentinel, 0, 1), ZX_OK);
{
auto result = pipe->Write(1, 0);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
EXPECT_EQ(result->actual, 1U);
}
// Read 1 byte result.
{
auto result = pipe->Read(1, 0);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
EXPECT_EQ(result->actual, 1U);
}
uint8_t result = 0;
EXPECT_EQ(vmo.read(&result, 0, 1), ZX_OK);
// pingpong service should have returned the data received.
EXPECT_EQ(result, kSentinel);
// Write 3 * 4096 bytes.
uint8_t send_buffer[kSize];
memset(send_buffer, kSentinel, kSize);
EXPECT_EQ(vmo.write(send_buffer, 0, kSize), ZX_OK);
{
auto result = pipe->Write(kSize, 0);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
EXPECT_EQ(result->actual, kSize);
}
// Read 3 * 4096 bytes.
{
auto result = pipe->Read(kSize, 0);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
EXPECT_EQ(result->actual, kSize);
}
uint8_t recv_buffer[kSize];
EXPECT_EQ(vmo.read(recv_buffer, 0, kSize), ZX_OK);
// pingpong service should have returned the data received.
EXPECT_EQ(memcmp(send_buffer, recv_buffer, kSize), 0);
// Write & Read 4096 bytes.
const size_t kSmallSize = kSize / 3;
const size_t kRecvOffset = kSmallSize;
memset(send_buffer, kSentinel, kSmallSize);
EXPECT_EQ(vmo.write(send_buffer, 0, kSmallSize), ZX_OK);
{
auto result = pipe->DoCall(kSmallSize, 0u, kSmallSize, kRecvOffset);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
EXPECT_EQ(result->actual, 2 * kSmallSize);
}
EXPECT_EQ(vmo.read(recv_buffer, kRecvOffset, kSmallSize), ZX_OK);
// pingpong service should have returned the data received.
EXPECT_EQ(memcmp(send_buffer, recv_buffer, kSmallSize), 0);
}
TEST(GoldfishControlTests, GoldfishControlTest) {
zx::result control_connection = ConnectToControl();
ASSERT_EQ(control_connection.status_value(), ZX_OK);
fidl::WireSyncClient<fuchsia_hardware_goldfish::ControlDevice> control(
std::move(control_connection.value()));
auto allocator = CreateSysmemAllocator();
ASSERT_TRUE(allocator.is_valid());
auto token_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollectionToken>::Create();
ASSERT_EQ(allocator->AllocateSharedCollection(std::move(token_endpoints.server)).status(), ZX_OK);
auto collection_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollection>::Create();
ASSERT_EQ(allocator
->BindSharedCollection(std::move(token_endpoints.client),
std::move(collection_endpoints.server))
.status(),
ZX_OK);
fuchsia_sysmem::wire::BufferCollectionConstraints constraints;
constraints.usage.vulkan = fuchsia_sysmem::wire::kVulkanImageUsageTransferDst;
constraints.min_buffer_count_for_camping = 1;
constraints.has_buffer_memory_constraints = true;
constraints.buffer_memory_constraints = fuchsia_sysmem::wire::BufferMemoryConstraints{
.min_size_bytes = 4 * 1024,
.max_size_bytes = 4 * 1024,
.physically_contiguous_required = false,
.secure_required = false,
.ram_domain_supported = false,
.cpu_domain_supported = false,
.inaccessible_domain_supported = true,
.heap_permitted_count = 1,
.heap_permitted = {fuchsia_sysmem::wire::HeapType::kGoldfishDeviceLocal}};
fidl::WireSyncClient<fuchsia_sysmem::BufferCollection> collection(
std::move(collection_endpoints.client));
SetDefaultCollectionName(collection);
EXPECT_TRUE(collection->SetConstraints(true, std::move(constraints)).ok());
fuchsia_sysmem::wire::BufferCollectionInfo2 info;
{
auto result = collection->WaitForBuffersAllocated();
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->status, ZX_OK);
info = std::move(result->buffer_collection_info);
EXPECT_EQ(info.buffer_count, 1U);
EXPECT_TRUE(info.buffers[0].vmo.is_valid());
}
zx::vmo vmo = std::move(info.buffers[0].vmo);
EXPECT_TRUE(vmo.is_valid());
EXPECT_TRUE(collection->Close().ok());
zx::vmo vmo_copy;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
{
fidl::Arena allocator;
fuchsia_hardware_goldfish::wire::CreateColorBuffer2Params create_params(allocator);
create_params.set_width(64)
.set_height(64)
.set_format(fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kBgra)
.set_memory_property(fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal);
auto result = control->CreateColorBuffer2(std::move(vmo_copy), std::move(create_params));
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
}
zx::vmo vmo_copy2;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy2), ZX_OK);
{
auto result = control->GetBufferHandle(std::move(vmo_copy2));
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
EXPECT_NE(result->id, 0u);
EXPECT_EQ(result->type, fuchsia_hardware_goldfish::wire::BufferHandleType::kColorBuffer);
}
zx::vmo vmo_copy3;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy3), ZX_OK);
{
fidl::Arena allocator;
fuchsia_hardware_goldfish::wire::CreateColorBuffer2Params create_params(allocator);
create_params.set_width(64)
.set_height(64)
.set_format(fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kBgra)
.set_memory_property(fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal);
auto result = control->CreateColorBuffer2(std::move(vmo_copy3), std::move(create_params));
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_ERR_ALREADY_EXISTS);
}
}
TEST(GoldfishControlTests, GoldfishControlTest_HostVisible) {
zx::result control_connection = ConnectToControl();
ASSERT_EQ(control_connection.status_value(), ZX_OK);
fidl::WireSyncClient<fuchsia_hardware_goldfish::ControlDevice> control(
std::move(control_connection.value()));
auto allocator = CreateSysmemAllocator();
ASSERT_TRUE(allocator.is_valid());
auto token_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollectionToken>::Create();
ASSERT_EQ(allocator->AllocateSharedCollection(std::move(token_endpoints.server)).status(), ZX_OK);
auto collection_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollection>::Create();
ASSERT_EQ(allocator
->BindSharedCollection(std::move(token_endpoints.client),
std::move(collection_endpoints.server))
.status(),
ZX_OK);
const size_t kMinSizeBytes = 4 * 1024;
const size_t kMaxSizeBytes = 4 * 4096;
fuchsia_sysmem::wire::BufferCollectionConstraints constraints;
constraints.usage.vulkan = fuchsia_sysmem::wire::kVulkanImageUsageTransferDst;
constraints.min_buffer_count_for_camping = 1;
constraints.has_buffer_memory_constraints = true;
constraints.buffer_memory_constraints = fuchsia_sysmem::wire::BufferMemoryConstraints{
.min_size_bytes = kMinSizeBytes,
.max_size_bytes = kMaxSizeBytes,
.physically_contiguous_required = false,
.secure_required = false,
.ram_domain_supported = false,
.cpu_domain_supported = true,
.inaccessible_domain_supported = false,
.heap_permitted_count = 1,
.heap_permitted = {fuchsia_sysmem::wire::HeapType::kGoldfishHostVisible}};
constraints.image_format_constraints_count = 1;
constraints.image_format_constraints[0] = fuchsia_sysmem::wire::ImageFormatConstraints{
.pixel_format =
fuchsia_sysmem::wire::PixelFormat{
.type = fuchsia_sysmem::wire::PixelFormatType::kBgra32,
.has_format_modifier = false,
.format_modifier = {},
},
.color_spaces_count = 1,
.color_space =
{
fuchsia_sysmem::wire::ColorSpace{.type = fuchsia_sysmem::wire::ColorSpaceType::kSrgb},
},
.min_coded_width = 32,
.min_coded_height = 32,
};
fidl::WireSyncClient<fuchsia_sysmem::BufferCollection> collection(
std::move(collection_endpoints.client));
SetDefaultCollectionName(collection);
EXPECT_TRUE(collection->SetConstraints(true, std::move(constraints)).ok());
fuchsia_sysmem::wire::BufferCollectionInfo2 info;
{
auto result = collection->WaitForBuffersAllocated();
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->status, ZX_OK);
info = std::move(result->buffer_collection_info);
EXPECT_EQ(info.buffer_count, 1U);
EXPECT_TRUE(info.buffers[0].vmo.is_valid());
EXPECT_EQ(info.settings.buffer_settings.coherency_domain,
fuchsia_sysmem::wire::CoherencyDomain::kCpu);
}
zx::vmo vmo = std::move(info.buffers[0].vmo);
EXPECT_TRUE(vmo.is_valid());
uint64_t vmo_size;
EXPECT_EQ(vmo.get_size(&vmo_size), ZX_OK);
EXPECT_GE(vmo_size, kMinSizeBytes);
EXPECT_LE(vmo_size, kMaxSizeBytes);
// Test if the vmo is mappable.
zx_vaddr_t addr;
EXPECT_EQ(zx::vmar::root_self()->map(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, /*vmar_offset*/ 0, vmo,
/*vmo_offset*/ 0, vmo_size, &addr),
ZX_OK);
// Test if write and read works correctly.
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
std::vector<uint8_t> copy_target(vmo_size, 0u);
for (uint32_t trial = 0; trial < 10u; trial++) {
memset(ptr, trial, vmo_size);
memcpy(copy_target.data(), ptr, vmo_size);
zx_cache_flush(ptr, vmo_size, ZX_CACHE_FLUSH_DATA | ZX_CACHE_FLUSH_INVALIDATE);
EXPECT_EQ(memcmp(copy_target.data(), ptr, vmo_size), 0);
}
EXPECT_EQ(zx::vmar::root_self()->unmap(addr, PAGE_SIZE), ZX_OK);
EXPECT_TRUE(collection->Close().ok());
}
TEST(GoldfishControlTests, GoldfishControlTest_HostVisible_MultiClients) {
using fuchsia_sysmem::BufferCollection;
using fuchsia_sysmem::wire::BufferCollectionConstraints;
zx::result control = ConnectToControl();
EXPECT_EQ(control.status_value(), ZX_OK);
auto allocator = CreateSysmemAllocator();
ASSERT_TRUE(allocator.is_valid());
constexpr size_t kNumClients = 2;
fidl::ClientEnd<fuchsia_sysmem::BufferCollectionToken> token_client[kNumClients];
fidl::ClientEnd<fuchsia_sysmem::BufferCollection> collection_client[kNumClients];
// Client 0.
{
auto endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollectionToken>::Create();
ASSERT_EQ(allocator->AllocateSharedCollection(std::move(endpoints.server)).status(), ZX_OK);
token_client[0] = std::move(endpoints.client);
}
// Client 1.
{
auto endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollectionToken>::Create();
ASSERT_EQ(fidl::WireCall(token_client[0].borrow())
->Duplicate(0, std::move(endpoints.server))
.status(),
ZX_OK);
ASSERT_EQ(fidl::WireCall(token_client[0].borrow())->Sync().status(), ZX_OK);
token_client[1] = std::move(endpoints.client);
}
for (size_t i = 0; i < kNumClients; i++) {
auto endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollection>::Create();
ASSERT_EQ(
allocator->BindSharedCollection(std::move(token_client[i]), std::move(endpoints.server))
.status(),
ZX_OK);
collection_client[i] = std::move(endpoints.client);
}
const size_t kMinSizeBytes = 4 * 1024;
const size_t kMaxSizeBytes = 4 * 1024 * 512;
const size_t kTargetSizeBytes = 4 * 1024 * 512;
BufferCollectionConstraints constraints[kNumClients];
for (size_t i = 0; i < kNumClients; i++) {
constraints[i].usage.vulkan = fuchsia_sysmem::wire::kVulkanImageUsageTransferDst;
constraints[i].min_buffer_count = 1;
constraints[i].has_buffer_memory_constraints = true;
constraints[i].buffer_memory_constraints = fuchsia_sysmem::wire::BufferMemoryConstraints{
.min_size_bytes = kMinSizeBytes,
.max_size_bytes = kMaxSizeBytes,
.physically_contiguous_required = false,
.secure_required = false,
.ram_domain_supported = false,
.cpu_domain_supported = true,
.inaccessible_domain_supported = false,
.heap_permitted_count = 1,
.heap_permitted = {fuchsia_sysmem::wire::HeapType::kGoldfishHostVisible}};
constraints[i].image_format_constraints_count = 1;
constraints[i].image_format_constraints[0] = fuchsia_sysmem::wire::ImageFormatConstraints{
.pixel_format =
fuchsia_sysmem::wire::PixelFormat{
.type = fuchsia_sysmem::wire::PixelFormatType::kBgra32,
.has_format_modifier = false,
.format_modifier = {},
},
.color_spaces_count = 1,
.color_space =
{
fuchsia_sysmem::wire::ColorSpace{.type =
fuchsia_sysmem::wire::ColorSpaceType::kSrgb},
},
};
}
// Set different min_coded_width and required_max_coded_width for each client.
constraints[0].image_format_constraints[0].min_coded_width = 32;
constraints[0].image_format_constraints[0].min_coded_height = 64;
constraints[1].image_format_constraints[0].min_coded_width = 16;
constraints[1].image_format_constraints[0].min_coded_height = 512;
constraints[1].image_format_constraints[0].required_max_coded_width = 1024;
constraints[1].image_format_constraints[0].required_max_coded_height = 256;
fidl::WireSyncClient<BufferCollection> collection[kNumClients];
for (size_t i = 0; i < kNumClients; i++) {
collection[i] = fidl::WireSyncClient<BufferCollection>(std::move(collection_client[i])),
SetDefaultCollectionName(collection[i]);
EXPECT_TRUE(collection[i]->SetConstraints(true, std::move(constraints[i])).ok());
};
fuchsia_sysmem::wire::BufferCollectionInfo2 info;
{
auto result = collection[0]->WaitForBuffersAllocated();
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->status, ZX_OK);
info = std::move(result->buffer_collection_info);
EXPECT_EQ(info.buffer_count, 1u);
EXPECT_TRUE(info.buffers[0].vmo.is_valid());
EXPECT_EQ(info.settings.buffer_settings.coherency_domain,
fuchsia_sysmem::wire::CoherencyDomain::kCpu);
const auto& image_format_constraints =
result.value().buffer_collection_info.settings.image_format_constraints;
EXPECT_EQ(image_format_constraints.min_coded_width, 32u);
EXPECT_EQ(image_format_constraints.min_coded_height, 512u);
EXPECT_EQ(image_format_constraints.required_max_coded_width, 1024u);
EXPECT_EQ(image_format_constraints.required_max_coded_height, 256u);
// Expected coded_width = max(min_coded_width, required_max_coded_width);
// Expected coded_height = max(min_coded_height, required_max_coded_height).
// Thus target size should be 1024 x 512 x 4.
EXPECT_GE(info.settings.buffer_settings.size_bytes, kTargetSizeBytes);
}
zx::vmo vmo = std::move(info.buffers[0].vmo);
EXPECT_TRUE(vmo.is_valid());
uint64_t vmo_size;
EXPECT_EQ(vmo.get_size(&vmo_size), ZX_OK);
EXPECT_GE(vmo_size, kTargetSizeBytes);
EXPECT_LE(vmo_size, kMaxSizeBytes);
// Test if the vmo is mappable.
zx_vaddr_t addr;
EXPECT_EQ(zx::vmar::root_self()->map(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, /*vmar_offset*/ 0, vmo,
/*vmo_offset*/ 0, vmo_size, &addr),
ZX_OK);
// Test if write and read works correctly.
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
std::vector<uint8_t> copy_target(vmo_size, 0u);
for (uint32_t trial = 0; trial < 10u; trial++) {
memset(ptr, trial, vmo_size);
memcpy(copy_target.data(), ptr, vmo_size);
zx_cache_flush(ptr, vmo_size, ZX_CACHE_FLUSH_DATA | ZX_CACHE_FLUSH_INVALIDATE);
EXPECT_EQ(memcmp(copy_target.data(), ptr, vmo_size), 0);
}
EXPECT_EQ(zx::vmar::root_self()->unmap(addr, PAGE_SIZE), ZX_OK);
for (size_t i = 0; i < kNumClients; i++) {
EXPECT_TRUE(collection[i]->Close().ok());
}
}
TEST(GoldfishControlTests, GoldfishControlTest_HostVisibleBuffer) {
zx::result control_connection = ConnectToControl();
ASSERT_EQ(control_connection.status_value(), ZX_OK);
fidl::WireSyncClient<fuchsia_hardware_goldfish::ControlDevice> control(
std::move(control_connection.value()));
auto allocator = CreateSysmemAllocator();
ASSERT_TRUE(allocator.is_valid());
auto token_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollectionToken>::Create();
ASSERT_EQ(allocator->AllocateSharedCollection(std::move(token_endpoints.server)).status(), ZX_OK);
auto collection_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollection>::Create();
ASSERT_EQ(allocator
->BindSharedCollection(std::move(token_endpoints.client),
std::move(collection_endpoints.server))
.status(),
ZX_OK);
const size_t kMinSizeBytes = 4 * 1024;
const size_t kMaxSizeBytes = 4 * 4096;
fuchsia_sysmem::wire::BufferCollectionConstraints constraints;
constraints.usage.vulkan = fuchsia_sysmem::wire::kVulkanUsageTransferDst;
constraints.min_buffer_count_for_camping = 1;
constraints.has_buffer_memory_constraints = true;
constraints.buffer_memory_constraints = fuchsia_sysmem::wire::BufferMemoryConstraints{
.min_size_bytes = kMinSizeBytes,
.max_size_bytes = kMaxSizeBytes,
.physically_contiguous_required = false,
.secure_required = false,
.ram_domain_supported = false,
.cpu_domain_supported = true,
.inaccessible_domain_supported = false,
.heap_permitted_count = 1,
.heap_permitted = {fuchsia_sysmem::wire::HeapType::kGoldfishHostVisible}};
constraints.image_format_constraints_count = 0;
fidl::WireSyncClient<fuchsia_sysmem::BufferCollection> collection(
std::move(collection_endpoints.client));
SetDefaultCollectionName(collection);
EXPECT_TRUE(collection->SetConstraints(true, std::move(constraints)).ok());
fuchsia_sysmem::wire::BufferCollectionInfo2 info;
{
auto result = collection->WaitForBuffersAllocated();
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->status, ZX_OK);
info = std::move(result->buffer_collection_info);
EXPECT_EQ(info.buffer_count, 1U);
EXPECT_TRUE(info.buffers[0].vmo.is_valid());
EXPECT_EQ(info.settings.buffer_settings.coherency_domain,
fuchsia_sysmem::wire::CoherencyDomain::kCpu);
}
zx::vmo vmo = std::move(info.buffers[0].vmo);
EXPECT_TRUE(vmo.is_valid());
uint64_t vmo_size;
EXPECT_EQ(vmo.get_size(&vmo_size), ZX_OK);
EXPECT_GE(vmo_size, kMinSizeBytes);
EXPECT_LE(vmo_size, kMaxSizeBytes);
// Test if the vmo is mappable.
zx_vaddr_t addr;
EXPECT_EQ(zx::vmar::root_self()->map(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, /*vmar_offset*/ 0, vmo,
/*vmo_offset*/ 0, vmo_size, &addr),
ZX_OK);
// Test if write and read works correctly.
uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
std::vector<uint8_t> copy_target(vmo_size, 0u);
for (uint32_t trial = 0; trial < 10u; trial++) {
memset(ptr, trial, vmo_size);
memcpy(copy_target.data(), ptr, vmo_size);
zx_cache_flush(ptr, vmo_size, ZX_CACHE_FLUSH_DATA | ZX_CACHE_FLUSH_INVALIDATE);
EXPECT_EQ(memcmp(copy_target.data(), ptr, vmo_size), 0);
}
EXPECT_EQ(zx::vmar::root_self()->unmap(addr, PAGE_SIZE), ZX_OK);
EXPECT_TRUE(collection->Close().ok());
}
TEST(GoldfishControlTests, GoldfishControlTest_DataBuffer) {
zx::result control_connection = ConnectToControl();
ASSERT_EQ(control_connection.status_value(), ZX_OK);
fidl::WireSyncClient<fuchsia_hardware_goldfish::ControlDevice> control(
std::move(control_connection.value()));
auto allocator = CreateSysmemAllocator();
ASSERT_TRUE(allocator.is_valid());
auto token_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollectionToken>::Create();
ASSERT_EQ(allocator->AllocateSharedCollection(std::move(token_endpoints.server)).status(), ZX_OK);
auto collection_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollection>::Create();
ASSERT_EQ(allocator
->BindSharedCollection(std::move(token_endpoints.client),
std::move(collection_endpoints.server))
.status(),
ZX_OK);
constexpr size_t kBufferSizeBytes = 4 * 1024;
fuchsia_sysmem::wire::BufferCollectionConstraints constraints;
constraints.usage.vulkan = fuchsia_sysmem::wire::kVulkanBufferUsageTransferDst;
constraints.min_buffer_count_for_camping = 1;
constraints.has_buffer_memory_constraints = true;
constraints.buffer_memory_constraints = fuchsia_sysmem::wire::BufferMemoryConstraints{
.min_size_bytes = kBufferSizeBytes,
.max_size_bytes = kBufferSizeBytes,
.physically_contiguous_required = false,
.secure_required = false,
.ram_domain_supported = false,
.cpu_domain_supported = false,
.inaccessible_domain_supported = true,
.heap_permitted_count = 1,
.heap_permitted = {fuchsia_sysmem::wire::HeapType::kGoldfishDeviceLocal}};
fidl::WireSyncClient<fuchsia_sysmem::BufferCollection> collection(
std::move(collection_endpoints.client));
SetDefaultCollectionName(collection);
EXPECT_TRUE(collection->SetConstraints(true, std::move(constraints)).ok());
fuchsia_sysmem::wire::BufferCollectionInfo2 info;
{
auto result = collection->WaitForBuffersAllocated();
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->status, ZX_OK);
info = std::move(result->buffer_collection_info);
EXPECT_EQ(info.buffer_count, 1u);
EXPECT_TRUE(info.buffers[0].vmo.is_valid());
}
zx::vmo vmo = std::move(info.buffers[0].vmo);
EXPECT_TRUE(vmo.is_valid());
EXPECT_TRUE(collection->Close().ok());
zx::vmo vmo_copy;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
{
fidl::Arena allocator;
fuchsia_hardware_goldfish::wire::CreateBuffer2Params create_params(allocator);
create_params.set_size(allocator, kBufferSizeBytes)
.set_memory_property(fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal);
auto result = control->CreateBuffer2(std::move(vmo_copy), std::move(create_params));
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_ok());
}
zx::vmo vmo_copy2;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy2), ZX_OK);
{
auto result = control->GetBufferHandle(std::move(vmo_copy2));
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
EXPECT_NE(result->id, 0u);
EXPECT_EQ(result->type, fuchsia_hardware_goldfish::wire::BufferHandleType::kBuffer);
}
zx::vmo vmo_copy3;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy3), ZX_OK);
{
fidl::Arena allocator;
fuchsia_hardware_goldfish::wire::CreateColorBuffer2Params create_params(allocator);
create_params.set_width(64)
.set_height(64)
.set_format(fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kBgra)
.set_memory_property(fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal);
auto result = control->CreateColorBuffer2(std::move(vmo_copy3), std::move(create_params));
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_ERR_ALREADY_EXISTS);
}
zx::vmo vmo_copy4;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy4), ZX_OK);
{
fidl::Arena allocator;
fuchsia_hardware_goldfish::wire::CreateBuffer2Params create_params(allocator);
create_params.set_size(allocator, kBufferSizeBytes)
.set_memory_property(fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal);
auto result = control->CreateBuffer2(std::move(vmo_copy4), std::move(create_params));
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_error());
EXPECT_EQ(result->error_value(), ZX_ERR_ALREADY_EXISTS);
}
}
// In this test case we call CreateColorBuffer() and GetBufferHandle()
// on VMOs not registered with goldfish sysmem heap.
//
// The IPC transmission should succeed but FIDL interface should
// return ZX_ERR_INVALID_ARGS.
TEST(GoldfishControlTests, GoldfishControlTest_InvalidVmo) {
zx::result control_connection = ConnectToControl();
ASSERT_EQ(control_connection.status_value(), ZX_OK);
fidl::WireSyncClient<fuchsia_hardware_goldfish::ControlDevice> control(
std::move(control_connection.value()));
zx::vmo non_sysmem_vmo;
EXPECT_EQ(zx::vmo::create(1024u, 0u, &non_sysmem_vmo), ZX_OK);
// Call CreateColorBuffer() using vmo not registered with goldfish
// sysmem heap.
zx::vmo vmo_copy;
EXPECT_EQ(non_sysmem_vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
{
fidl::Arena allocator;
fuchsia_hardware_goldfish::wire::CreateColorBuffer2Params create_params(allocator);
create_params.set_width(16)
.set_height(16)
.set_format(fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kBgra)
.set_memory_property(fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal);
auto result = control->CreateColorBuffer2(std::move(vmo_copy), std::move(create_params));
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_ERR_INVALID_ARGS);
}
// Call GetBufferHandle() using vmo not registered with goldfish
// sysmem heap.
zx::vmo vmo_copy2;
EXPECT_EQ(non_sysmem_vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy2), ZX_OK);
{
auto result = control->GetBufferHandle(std::move(vmo_copy2));
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_ERR_INVALID_ARGS);
}
}
// In this test case we test arguments of CreateColorBuffer2() method.
// If a mandatory field is missing, it should return "ZX_ERR_INVALID_ARGS".
TEST(GoldfishControlTests, GoldfishControlTest_CreateColorBuffer2Args) {
// Setup control device.
zx::result control_connection = ConnectToControl();
ASSERT_EQ(control_connection.status_value(), ZX_OK);
fidl::WireSyncClient<fuchsia_hardware_goldfish::ControlDevice> control(
std::move(control_connection.value()));
// ----------------------------------------------------------------------//
// Setup sysmem allocator and buffer collection.
auto allocator = CreateSysmemAllocator();
ASSERT_TRUE(allocator.is_valid());
auto token_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollectionToken>::Create();
ASSERT_EQ(allocator->AllocateSharedCollection(std::move(token_endpoints.server)).status(), ZX_OK);
auto collection_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollection>::Create();
ASSERT_EQ(allocator
->BindSharedCollection(std::move(token_endpoints.client),
std::move(collection_endpoints.server))
.status(),
ZX_OK);
// ----------------------------------------------------------------------//
// Use device local heap which only *registers* the koid of vmo to control
// device.
fuchsia_sysmem::wire::BufferCollectionConstraints constraints;
constraints.usage.vulkan = fuchsia_sysmem::wire::kVulkanImageUsageTransferDst;
constraints.min_buffer_count_for_camping = 1;
constraints.has_buffer_memory_constraints = true;
constraints.buffer_memory_constraints = fuchsia_sysmem::wire::BufferMemoryConstraints{
.min_size_bytes = 4 * 1024,
.max_size_bytes = 4 * 1024,
.physically_contiguous_required = false,
.secure_required = false,
.ram_domain_supported = false,
.cpu_domain_supported = false,
.inaccessible_domain_supported = true,
.heap_permitted_count = 1,
.heap_permitted = {fuchsia_sysmem::wire::HeapType::kGoldfishDeviceLocal}};
fidl::WireSyncClient<fuchsia_sysmem::BufferCollection> collection(
std::move(collection_endpoints.client));
SetDefaultCollectionName(collection);
EXPECT_TRUE(collection->SetConstraints(true, std::move(constraints)).ok());
fuchsia_sysmem::wire::BufferCollectionInfo2 info;
{
auto result = collection->WaitForBuffersAllocated();
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->status, ZX_OK);
info = std::move(result->buffer_collection_info);
EXPECT_EQ(info.buffer_count, 1u);
EXPECT_TRUE(info.buffers[0].vmo.is_valid());
}
zx::vmo vmo = std::move(info.buffers[0].vmo);
EXPECT_TRUE(vmo.is_valid());
EXPECT_TRUE(collection->Close().ok());
// ----------------------------------------------------------------------//
// Try creating color buffer.
zx::vmo vmo_copy;
{
// Verify that a CreateColorBuffer2() call without width will fail.
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
fidl::Arena allocator;
fuchsia_hardware_goldfish::wire::CreateColorBuffer2Params create_params(allocator);
// Without width
create_params.set_height(64)
.set_format(fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kBgra)
.set_memory_property(fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal);
auto result = control->CreateColorBuffer2(std::move(vmo_copy), std::move(create_params));
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_ERR_INVALID_ARGS);
EXPECT_LT(result->hw_address_page_offset, 0);
}
{
// Verify that a CreateColorBuffer2() call without height will fail.
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
fidl::Arena allocator;
fuchsia_hardware_goldfish::wire::CreateColorBuffer2Params create_params(allocator);
// Without height
create_params.set_width(64)
.set_format(fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kBgra)
.set_memory_property(fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal);
auto result = control->CreateColorBuffer2(std::move(vmo_copy), std::move(create_params));
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_ERR_INVALID_ARGS);
EXPECT_LT(result->hw_address_page_offset, 0);
}
{
// Verify that a CreateColorBuffer2() call without color format will fail.
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
fidl::Arena allocator;
fuchsia_hardware_goldfish::wire::CreateColorBuffer2Params create_params(allocator);
// Without format
create_params.set_width(64).set_height(64).set_memory_property(
fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal);
auto result = control->CreateColorBuffer2(std::move(vmo_copy), std::move(create_params));
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_ERR_INVALID_ARGS);
EXPECT_LT(result->hw_address_page_offset, 0);
}
{
// Verify that a CreateColorBuffer2() call without memory property will fail.
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
fidl::Arena allocator;
fuchsia_hardware_goldfish::wire::CreateColorBuffer2Params create_params(allocator);
// Without memory property
create_params.set_width(64).set_height(64).set_format(
fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kBgra);
auto result = control->CreateColorBuffer2(std::move(vmo_copy), std::move(create_params));
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_ERR_INVALID_ARGS);
EXPECT_LT(result->hw_address_page_offset, 0);
}
}
// In this test case we test arguments of CreateBuffer2() method.
// If a mandatory field is missing, it should return "ZX_ERR_INVALID_ARGS".
TEST(GoldfishControlTests, GoldfishControlTest_CreateBuffer2Args) {
// Setup control device.
zx::result control_connection = ConnectToControl();
ASSERT_EQ(control_connection.status_value(), ZX_OK);
fidl::WireSyncClient<fuchsia_hardware_goldfish::ControlDevice> control(
std::move(control_connection.value()));
// ----------------------------------------------------------------------//
// Setup sysmem allocator and buffer collection.
auto allocator = CreateSysmemAllocator();
ASSERT_TRUE(allocator.is_valid());
auto token_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollectionToken>::Create();
ASSERT_EQ(allocator->AllocateSharedCollection(std::move(token_endpoints.server)).status(), ZX_OK);
auto collection_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollection>::Create();
ASSERT_EQ(allocator
->BindSharedCollection(std::move(token_endpoints.client),
std::move(collection_endpoints.server))
.status(),
ZX_OK);
// ----------------------------------------------------------------------//
// Use device local heap which only *registers* the koid of vmo to control
// device.
fuchsia_sysmem::wire::BufferCollectionConstraints constraints;
constraints.usage.vulkan = fuchsia_sysmem::wire::kVulkanImageUsageTransferDst;
constraints.min_buffer_count_for_camping = 1;
constraints.has_buffer_memory_constraints = true;
constraints.buffer_memory_constraints = fuchsia_sysmem::wire::BufferMemoryConstraints{
.min_size_bytes = 4 * 1024,
.max_size_bytes = 4 * 1024,
.physically_contiguous_required = false,
.secure_required = false,
.ram_domain_supported = false,
.cpu_domain_supported = false,
.inaccessible_domain_supported = true,
.heap_permitted_count = 1,
.heap_permitted = {fuchsia_sysmem::wire::HeapType::kGoldfishDeviceLocal}};
fidl::WireSyncClient<fuchsia_sysmem::BufferCollection> collection(
std::move(collection_endpoints.client));
SetDefaultCollectionName(collection);
EXPECT_TRUE(collection->SetConstraints(true, std::move(constraints)).ok());
fuchsia_sysmem::wire::BufferCollectionInfo2 info;
{
auto result = collection->WaitForBuffersAllocated();
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->status, ZX_OK);
info = std::move(result->buffer_collection_info);
EXPECT_EQ(info.buffer_count, 1u);
EXPECT_TRUE(info.buffers[0].vmo.is_valid());
}
zx::vmo vmo = std::move(info.buffers[0].vmo);
EXPECT_TRUE(vmo.is_valid());
EXPECT_TRUE(collection->Close().ok());
// ----------------------------------------------------------------------//
// Try creating data buffers.
zx::vmo vmo_copy;
{
// Verify that a CreateBuffer2() call without width will fail.
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
fidl::Arena allocator;
fuchsia_hardware_goldfish::wire::CreateBuffer2Params create_params(allocator);
// Without size
create_params.set_memory_property(fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal);
auto result = control->CreateBuffer2(std::move(vmo_copy), std::move(create_params));
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_error());
EXPECT_EQ(result->error_value(), ZX_ERR_INVALID_ARGS);
}
{
// Verify that a CreateBuffer2() call without memory property will fail.
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
fidl::Arena allocator;
fuchsia_hardware_goldfish::wire::CreateBuffer2Params create_params(allocator);
// Without memory property
create_params.set_size(allocator, 4096);
auto result = control->CreateBuffer2(std::move(vmo_copy), std::move(create_params));
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result->is_error());
EXPECT_EQ(result->error_value(), ZX_ERR_INVALID_ARGS);
}
}
// In this test case we call GetBufferHandle() on a vmo
// registered to the control device but we haven't created
// the color buffer yet.
//
// The FIDL interface should return ZX_ERR_NOT_FOUND.
TEST(GoldfishControlTests, GoldfishControlTest_GetNotCreatedColorBuffer) {
zx::result control_connection = ConnectToControl();
ASSERT_EQ(control_connection.status_value(), ZX_OK);
fidl::WireSyncClient<fuchsia_hardware_goldfish::ControlDevice> control(
std::move(control_connection.value()));
auto allocator = CreateSysmemAllocator();
ASSERT_TRUE(allocator.is_valid());
auto token_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollectionToken>::Create();
ASSERT_EQ(allocator->AllocateSharedCollection(std::move(token_endpoints.server)).status(), ZX_OK);
auto collection_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollection>::Create();
ASSERT_EQ(allocator
->BindSharedCollection(std::move(token_endpoints.client),
std::move(collection_endpoints.server))
.status(),
ZX_OK);
fuchsia_sysmem::wire::BufferCollectionConstraints constraints;
constraints.usage.vulkan = fuchsia_sysmem::wire::kVulkanImageUsageTransferDst;
constraints.min_buffer_count_for_camping = 1;
constraints.has_buffer_memory_constraints = true;
constraints.buffer_memory_constraints = fuchsia_sysmem::wire::BufferMemoryConstraints{
.min_size_bytes = 4 * 1024,
.max_size_bytes = 4 * 1024,
.physically_contiguous_required = false,
.secure_required = false,
.ram_domain_supported = false,
.cpu_domain_supported = false,
.inaccessible_domain_supported = true,
.heap_permitted_count = 1,
.heap_permitted = {fuchsia_sysmem::wire::HeapType::kGoldfishDeviceLocal}};
fidl::WireSyncClient<fuchsia_sysmem::BufferCollection> collection(
std::move(collection_endpoints.client));
SetDefaultCollectionName(collection);
EXPECT_TRUE(collection->SetConstraints(true, constraints).ok());
fuchsia_sysmem::wire::BufferCollectionInfo2 info;
{
auto result = collection->WaitForBuffersAllocated();
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->status, ZX_OK);
info = std::move(result->buffer_collection_info);
EXPECT_EQ(info.buffer_count, 1u);
EXPECT_TRUE(info.buffers[0].vmo.is_valid());
}
zx::vmo vmo = std::move(info.buffers[0].vmo);
EXPECT_TRUE(vmo.is_valid());
EXPECT_TRUE(collection->Close().ok());
zx::vmo vmo_copy;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
{
auto result = control->GetBufferHandle(std::move(vmo_copy));
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_ERR_NOT_FOUND);
}
}
TEST(GoldfishAddressSpaceTests, GoldfishAddressSpaceTest) {
zx::result asd_connection = ConnectToAddress();
ASSERT_EQ(asd_connection.status_value(), ZX_OK);
fidl::WireSyncClient asd_parent(std::move(asd_connection.value()));
auto child_endpoints =
fidl::Endpoints<fuchsia_hardware_goldfish::AddressSpaceChildDriver>::Create();
{
auto result = asd_parent->OpenChildDriver(
fuchsia_hardware_goldfish::wire::AddressSpaceChildDriverType::kDefault,
std::move(child_endpoints.server));
ASSERT_TRUE(result.ok());
}
constexpr uint64_t kHeapSize = 16ULL * 1048576ULL;
fidl::WireSyncClient asd_child(std::move(child_endpoints.client));
uint64_t paddr = 0;
zx::vmo vmo;
{
auto result = asd_child->AllocateBlock(kHeapSize);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
paddr = result->paddr;
EXPECT_NE(paddr, 0U);
vmo = std::move(result->vmo);
EXPECT_EQ(vmo.is_valid(), true);
uint64_t actual_size = 0;
EXPECT_EQ(vmo.get_size(&actual_size), ZX_OK);
EXPECT_GE(actual_size, kHeapSize);
}
zx::vmo vmo2;
uint64_t paddr2 = 0;
{
auto result = asd_child->AllocateBlock(kHeapSize);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
paddr2 = result->paddr;
EXPECT_NE(paddr2, 0U);
EXPECT_NE(paddr2, paddr);
vmo2 = std::move(result->vmo);
EXPECT_EQ(vmo2.is_valid(), true);
uint64_t actual_size = 0;
EXPECT_EQ(vmo2.get_size(&actual_size), ZX_OK);
EXPECT_GE(actual_size, kHeapSize);
}
{
auto result = asd_child->DeallocateBlock(paddr);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
}
{
auto result = asd_child->DeallocateBlock(paddr2);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
}
// No testing into this too much, as it's going to be child driver-specific.
// Use fixed values for shared offset/size and ping metadata.
const uint64_t shared_offset = 4096;
const uint64_t shared_size = 4096;
const uint64_t overlap_offsets[] = {
4096,
0,
8191,
};
const uint64_t overlap_sizes[] = {
2048,
4097,
4096,
};
const size_t overlaps_to_test = sizeof(overlap_offsets) / sizeof(overlap_offsets[0]);
using fuchsia_hardware_goldfish::wire::AddressSpaceChildDriverPingMessage;
AddressSpaceChildDriverPingMessage msg;
msg.metadata = 0;
EXPECT_TRUE(asd_child->Ping(msg).ok());
{
auto result = asd_child->ClaimSharedBlock(shared_offset, shared_size);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
}
// Test that overlapping blocks cannot be claimed in the same connection.
for (size_t i = 0; i < overlaps_to_test; ++i) {
auto result = asd_child->ClaimSharedBlock(overlap_offsets[i], overlap_sizes[i]);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_ERR_INVALID_ARGS);
}
{
auto result = asd_child->UnclaimSharedBlock(shared_offset);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
}
// Test that removed or unknown offsets cannot be unclaimed.
{
auto result = asd_child->UnclaimSharedBlock(shared_offset);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_ERR_INVALID_ARGS);
}
{
auto result = asd_child->UnclaimSharedBlock(0);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_ERR_INVALID_ARGS);
}
}
// This is a test case testing goldfish Heap, control device, address space
// device, and host implementation of host-visible memory allocation.
//
// This test case using a device-local Heap and a pre-allocated address space
// block to simulate a host-visible sysmem Heap. It does the following things:
//
// 1) It allocates a memory block (vmo = |address_space_vmo| and gpa =
// |physical_addr|) from address space device.
//
// 2) It allocates an vmo (vmo = |vmo|) from the goldfish device-local Heap
// so that |vmo| is registered for color buffer creation.
//
// 3) It calls goldfish Control FIDL API to create a color buffer using |vmo|.
// and maps memory to |physical_addr|.
//
// 4) The color buffer creation and memory process should work correctly, and
// heap offset should be a non-negative value.
//
TEST(GoldfishHostMemoryTests, GoldfishHostVisibleColorBuffer) {
// Setup control device.
zx::result control_connection = ConnectToControl();
ASSERT_EQ(control_connection.status_value(), ZX_OK);
fidl::WireSyncClient<fuchsia_hardware_goldfish::ControlDevice> control(
std::move(control_connection.value()));
// ----------------------------------------------------------------------//
// Setup sysmem allocator and buffer collection.
auto allocator = CreateSysmemAllocator();
ASSERT_TRUE(allocator.is_valid());
auto token_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollectionToken>::Create();
ASSERT_EQ(allocator->AllocateSharedCollection(std::move(token_endpoints.server)).status(), ZX_OK);
auto collection_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollection>::Create();
ASSERT_EQ(allocator
->BindSharedCollection(std::move(token_endpoints.client),
std::move(collection_endpoints.server))
.status(),
ZX_OK);
fidl::WireSyncClient<fuchsia_sysmem::BufferCollection> collection(
std::move(collection_endpoints.client));
// ----------------------------------------------------------------------//
// Setup address space driver.
zx::result asd_connection = ConnectToAddress();
ASSERT_EQ(asd_connection.status_value(), ZX_OK);
fidl::WireSyncClient asd_parent(std::move(asd_connection.value()));
auto child_endpoints =
fidl::Endpoints<fuchsia_hardware_goldfish::AddressSpaceChildDriver>::Create();
{
auto result = asd_parent->OpenChildDriver(
fuchsia_hardware_goldfish::wire::AddressSpaceChildDriverType::kDefault,
std::move(child_endpoints.server));
ASSERT_TRUE(result.ok());
}
// Allocate device memory block using address space device.
constexpr uint64_t kHeapSize = 32768ULL;
fidl::WireSyncClient asd_child(std::move(child_endpoints.client));
uint64_t physical_addr = 0;
zx::vmo address_space_vmo;
{
auto result = asd_child->AllocateBlock(kHeapSize);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
physical_addr = result->paddr;
EXPECT_NE(physical_addr, 0U);
address_space_vmo = std::move(result->vmo);
EXPECT_EQ(address_space_vmo.is_valid(), true);
uint64_t actual_size = 0;
EXPECT_EQ(address_space_vmo.get_size(&actual_size), ZX_OK);
EXPECT_GE(actual_size, kHeapSize);
}
// ----------------------------------------------------------------------//
// Use device local heap which only *registers* the koid of vmo to control
// device.
fuchsia_sysmem::wire::BufferCollectionConstraints constraints;
constraints.usage.vulkan = fuchsia_sysmem::wire::kVulkanImageUsageTransferDst;
constraints.min_buffer_count_for_camping = 1;
constraints.has_buffer_memory_constraints = true;
constraints.buffer_memory_constraints = fuchsia_sysmem::wire::BufferMemoryConstraints{
.min_size_bytes = 4 * 1024,
.max_size_bytes = 4 * 1024,
.physically_contiguous_required = false,
.secure_required = false,
.ram_domain_supported = false,
.cpu_domain_supported = false,
.inaccessible_domain_supported = true,
.heap_permitted_count = 1,
.heap_permitted = {fuchsia_sysmem::wire::HeapType::kGoldfishDeviceLocal}};
SetDefaultCollectionName(collection);
EXPECT_TRUE(collection->SetConstraints(true, constraints).ok());
fuchsia_sysmem::wire::BufferCollectionInfo2 info;
{
auto result = collection->WaitForBuffersAllocated();
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->status, ZX_OK);
info = std::move(result->buffer_collection_info);
EXPECT_EQ(info.buffer_count, 1U);
EXPECT_TRUE(info.buffers[0].vmo.is_valid());
}
zx::vmo vmo = std::move(info.buffers[0].vmo);
EXPECT_TRUE(vmo.is_valid());
EXPECT_TRUE(collection->Close().ok());
// ----------------------------------------------------------------------//
// Creates color buffer and map host memory.
zx::vmo vmo_copy;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
{
// Verify that a CreateColorBuffer2() call with host-visible memory property,
// but without physical address will fail.
fidl::Arena allocator;
fuchsia_hardware_goldfish::wire::CreateColorBuffer2Params create_params(allocator);
// Without physical address
create_params.set_width(64)
.set_height(64)
.set_format(fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kBgra)
.set_memory_property(fuchsia_hardware_goldfish::wire::kMemoryPropertyHostVisible);
auto result = control->CreateColorBuffer2(std::move(vmo_copy), create_params);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_ERR_INVALID_ARGS);
EXPECT_LT(result->hw_address_page_offset, 0);
}
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
{
fidl::Arena allocator;
fuchsia_hardware_goldfish::wire::CreateColorBuffer2Params create_params(allocator);
create_params.set_width(64)
.set_height(64)
.set_format(fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kBgra)
.set_memory_property(0x02u)
.set_physical_address(allocator, physical_addr);
auto result = control->CreateColorBuffer2(std::move(vmo_copy), create_params);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
EXPECT_GE(result->hw_address_page_offset, 0);
}
// Verify if the color buffer works correctly.
zx::vmo vmo_copy2;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy2), ZX_OK);
{
auto result = control->GetBufferHandle(std::move(vmo_copy2));
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
EXPECT_NE(result->id, 0u);
EXPECT_EQ(result->type, fuchsia_hardware_goldfish::wire::BufferHandleType::kColorBuffer);
}
// Cleanup.
{
auto result = asd_child->DeallocateBlock(physical_addr);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
}
}
using GoldfishCreateColorBufferTest =
testing::TestWithParam<fuchsia_hardware_goldfish::wire::ColorBufferFormatType>;
TEST_P(GoldfishCreateColorBufferTest, CreateColorBufferWithFormat) {
zx::result control_connection = ConnectToControl();
ASSERT_EQ(control_connection.status_value(), ZX_OK);
fidl::WireSyncClient<fuchsia_hardware_goldfish::ControlDevice> control(
std::move(control_connection.value()));
auto allocator = CreateSysmemAllocator();
ASSERT_TRUE(allocator.is_valid());
auto token_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollectionToken>::Create();
ASSERT_EQ(allocator->AllocateSharedCollection(std::move(token_endpoints.server)).status(), ZX_OK);
auto collection_endpoints = fidl::Endpoints<fuchsia_sysmem::BufferCollection>::Create();
ASSERT_EQ(allocator
->BindSharedCollection(std::move(token_endpoints.client),
std::move(collection_endpoints.server))
.status(),
ZX_OK);
fidl::WireSyncClient collection(std::move(collection_endpoints.client));
fuchsia_sysmem::wire::BufferCollectionConstraints constraints;
constraints.usage.vulkan = fuchsia_sysmem::wire::kVulkanImageUsageTransferDst;
constraints.min_buffer_count_for_camping = 1;
constraints.has_buffer_memory_constraints = true;
constraints.buffer_memory_constraints = fuchsia_sysmem::wire::BufferMemoryConstraints{
.min_size_bytes = 4 * 1024,
.max_size_bytes = 4 * 1024,
.physically_contiguous_required = false,
.secure_required = false,
.ram_domain_supported = false,
.cpu_domain_supported = false,
.inaccessible_domain_supported = true,
.heap_permitted_count = 1,
.heap_permitted = {fuchsia_sysmem::wire::HeapType::kGoldfishDeviceLocal}};
SetDefaultCollectionName(collection);
EXPECT_TRUE(collection->SetConstraints(true, constraints).ok());
fuchsia_sysmem::wire::BufferCollectionInfo2 info;
{
auto result = collection->WaitForBuffersAllocated();
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->status, ZX_OK);
info = std::move(result->buffer_collection_info);
EXPECT_EQ(info.buffer_count, 1U);
EXPECT_TRUE(info.buffers[0].vmo.is_valid());
}
zx::vmo vmo = std::move(info.buffers[0].vmo);
EXPECT_TRUE(vmo.is_valid());
EXPECT_TRUE(collection->Close().ok());
zx::vmo vmo_copy;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
{
fidl::Arena allocator;
fuchsia_hardware_goldfish::wire::CreateColorBuffer2Params create_params(allocator);
create_params.set_width(64)
.set_height(64)
.set_format(GetParam())
.set_memory_property(fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal);
auto result = control->CreateColorBuffer2(std::move(vmo_copy), create_params);
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
}
zx::vmo vmo_copy2;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy2), ZX_OK);
{
auto result = control->GetBufferHandle(std::move(vmo_copy2));
ASSERT_TRUE(result.ok());
EXPECT_EQ(result->res, ZX_OK);
EXPECT_NE(result->id, 0u);
EXPECT_EQ(result->type, fuchsia_hardware_goldfish::wire::BufferHandleType::kColorBuffer);
}
}
TEST(GoldfishControlTests, CreateSyncKhr) {
zx::result control_connection = ConnectToControl();
ASSERT_EQ(control_connection.status_value(), ZX_OK);
fidl::WireSyncClient<fuchsia_hardware_goldfish::ControlDevice> control(
std::move(control_connection.value()));
zx::eventpair event_client, event_server;
zx_status_t status = zx::eventpair::create(0u, &event_client, &event_server);
{
auto result = control->CreateSyncFence(std::move(event_server));
ASSERT_TRUE(result.ok());
}
zx_signals_t pending;
status = event_client.wait_one(ZX_EVENTPAIR_SIGNALED, zx::deadline_after(zx::sec(10)), &pending);
EXPECT_EQ(status, ZX_OK);
}
INSTANTIATE_TEST_SUITE_P(
ColorBufferTests, GoldfishCreateColorBufferTest,
testing::Values(fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kRgba,
fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kBgra,
fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kRg,
fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kLuminance),
[](const testing::TestParamInfo<GoldfishCreateColorBufferTest::ParamType>& info)
-> std::string {
switch (info.param) {
case fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kRgba:
return "RGBA";
case fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kBgra:
return "BGRA";
case fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kRg:
return "RG";
case fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kLuminance:
return "LUMINANCE";
}
});