blob: c3f33dac68e2ee6426c871e9053352e80b4ff7fc [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 <fcntl.h>
#include <fuchsia/hardware/goldfish/c/fidl.h>
#include <fuchsia/sysmem/c/fidl.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fdio.h>
#include <lib/fidl-async-2/fidl_struct.h>
#include <lib/zx/channel.h>
#include <lib/zx/vmo.h>
#include <unistd.h>
#include <zircon/syscalls.h>
#include <unittest/unittest.h>
static bool GoldfishPipeTest() {
BEGIN_TEST;
int fd = open("/dev/class/goldfish-pipe/000", O_RDWR);
EXPECT_GE(fd, 0);
zx::channel channel;
EXPECT_EQ(fdio_get_service_handle(fd, channel.reset_and_get_address()), ZX_OK);
zx::channel pipe_client;
zx::channel pipe_server;
EXPECT_EQ(zx::channel::create(0, &pipe_client, &pipe_server), ZX_OK);
EXPECT_EQ(fuchsia_hardware_goldfish_PipeDeviceOpenPipe(channel.get(), pipe_server.release()),
ZX_OK);
int32_t res;
const size_t kSize = 3 * 4096;
EXPECT_EQ(fuchsia_hardware_goldfish_PipeSetBufferSize(pipe_client.get(), kSize, &res), ZX_OK);
EXPECT_EQ(res, ZX_OK);
zx::vmo vmo;
EXPECT_EQ(
fuchsia_hardware_goldfish_PipeGetBuffer(pipe_client.get(), &res, vmo.reset_and_get_address()),
ZX_OK);
EXPECT_EQ(res, ZX_OK);
// Connect to pingpong service.
constexpr char kPipeName[] = "pipe:pingpong";
size_t bytes = strlen(kPipeName) + 1;
EXPECT_EQ(vmo.write(kPipeName, 0, bytes), ZX_OK);
uint64_t actual;
EXPECT_EQ(fuchsia_hardware_goldfish_PipeWrite(pipe_client.get(), bytes, 0, &res, &actual), ZX_OK);
EXPECT_EQ(res, ZX_OK);
EXPECT_EQ(actual, bytes);
// Write 1 byte.
const uint8_t kSentinel = 0xaa;
EXPECT_EQ(vmo.write(&kSentinel, 0, 1), ZX_OK);
EXPECT_EQ(fuchsia_hardware_goldfish_PipeWrite(pipe_client.get(), 1, 0, &res, &actual), ZX_OK);
EXPECT_EQ(res, ZX_OK);
EXPECT_EQ(actual, 1);
// Read 1 byte result.
EXPECT_EQ(fuchsia_hardware_goldfish_PipeRead(pipe_client.get(), 1, 0, &res, &actual), ZX_OK);
EXPECT_EQ(res, ZX_OK);
EXPECT_EQ(actual, 1);
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);
EXPECT_EQ(fuchsia_hardware_goldfish_PipeWrite(pipe_client.get(), kSize, 0, &res, &actual), ZX_OK);
EXPECT_EQ(res, ZX_OK);
EXPECT_EQ(actual, kSize);
// Read 3 * 4096 bytes.
EXPECT_EQ(fuchsia_hardware_goldfish_PipeRead(pipe_client.get(), kSize, 0, &res, &actual), ZX_OK);
EXPECT_EQ(res, ZX_OK);
EXPECT_EQ(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);
EXPECT_EQ(fuchsia_hardware_goldfish_PipeCall(pipe_client.get(), kSmallSize, 0, kSmallSize,
kRecvOffset, &res, &actual),
ZX_OK);
EXPECT_EQ(res, ZX_OK);
EXPECT_EQ(actual, 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);
END_TEST;
}
BEGIN_TEST_CASE(GoldfishPipeTests)
RUN_TEST(GoldfishPipeTest)
END_TEST_CASE(GoldfishPipeTests)
extern const fidl_type_t fuchsia_sysmem_BufferCollectionConstraintsTable;
using BufferCollectionConstraints = FidlStruct<fuchsia_sysmem_BufferCollectionConstraints,
&fuchsia_sysmem_BufferCollectionConstraintsTable>;
extern const fidl_type_t fuchsia_sysmem_BufferCollectionInfo_2Table;
using BufferCollectionInfo =
FidlStruct<fuchsia_sysmem_BufferCollectionInfo_2, &fuchsia_sysmem_BufferCollectionInfo_2Table>;
static bool GoldfishControlTest() {
BEGIN_TEST;
int fd = open("/dev/class/goldfish-control/000", O_RDWR);
EXPECT_GE(fd, 0);
zx::channel channel;
EXPECT_EQ(fdio_get_service_handle(fd, channel.reset_and_get_address()), ZX_OK);
zx::channel allocator_client;
zx::channel allocator_server;
EXPECT_EQ(zx::channel::create(0, &allocator_client, &allocator_server), ZX_OK);
EXPECT_EQ(fdio_service_connect("/svc/fuchsia.sysmem.Allocator", allocator_server.release()),
ZX_OK);
zx::channel token_client;
zx::channel token_server;
EXPECT_EQ(zx::channel::create(0, &token_client, &token_server), ZX_OK);
EXPECT_EQ(fuchsia_sysmem_AllocatorAllocateSharedCollection(allocator_client.get(),
token_server.release()),
ZX_OK);
zx::channel collection_client;
zx::channel collection_server;
EXPECT_EQ(zx::channel::create(0, &collection_client, &collection_server), ZX_OK);
EXPECT_EQ(fuchsia_sysmem_AllocatorBindSharedCollection(
allocator_client.get(), token_client.release(), collection_server.release()),
ZX_OK);
BufferCollectionConstraints constraints(BufferCollectionConstraints::Default);
constraints->usage.vulkan = fuchsia_sysmem_vulkanUsageTransferDst;
constraints->min_buffer_count_for_camping = 1;
constraints->has_buffer_memory_constraints = true;
constraints->buffer_memory_constraints = fuchsia_sysmem_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_HeapType_GOLDFISH_DEVICE_LOCAL}};
EXPECT_EQ(fuchsia_sysmem_BufferCollectionSetConstraints(collection_client.get(), true,
constraints.release()),
ZX_OK);
zx_status_t status2 = ZX_OK;
BufferCollectionInfo info(BufferCollectionInfo::Default);
EXPECT_EQ(fuchsia_sysmem_BufferCollectionWaitForBuffersAllocated(collection_client.get(),
&status2, info.get()),
ZX_OK);
EXPECT_EQ(status2, ZX_OK);
EXPECT_EQ(info->buffer_count, 1);
EXPECT_NE(info->buffers[0].vmo, ZX_HANDLE_INVALID);
zx::vmo vmo(info->buffers[0].vmo);
info->buffers[0].vmo = ZX_HANDLE_INVALID;
EXPECT_TRUE(vmo.is_valid());
EXPECT_EQ(fuchsia_sysmem_BufferCollectionClose(collection_client.get()), ZX_OK);
zx::vmo vmo_copy;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
status2 = ZX_OK;
EXPECT_EQ(fuchsia_hardware_goldfish_ControlDeviceCreateColorBuffer(
channel.get(), vmo_copy.release(), 64, 64,
fuchsia_hardware_goldfish_ColorBufferFormatType_BGRA, &status2),
ZX_OK);
EXPECT_EQ(status2, ZX_OK);
zx::vmo vmo_copy2;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy2), ZX_OK);
status2 = ZX_OK;
uint32_t id = 0;
EXPECT_EQ(fuchsia_hardware_goldfish_ControlDeviceGetColorBuffer(
channel.get(), vmo_copy2.release(), &status2, &id),
ZX_OK);
EXPECT_EQ(status2, ZX_OK);
EXPECT_NE(id, 0);
zx::vmo vmo_copy3;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy3), ZX_OK);
status2 = ZX_OK;
EXPECT_EQ(fuchsia_hardware_goldfish_ControlDeviceCreateColorBuffer(
channel.get(), vmo_copy3.release(), 64, 64,
fuchsia_hardware_goldfish_ColorBufferFormatType_BGRA, &status2),
ZX_OK);
EXPECT_EQ(status2, ZX_ERR_ALREADY_EXISTS);
END_TEST;
}
// In this test case we call CreateColorBuffer() and GetColorBuffer()
// on VMOs not registered with goldfish sysmem heap.
//
// The IPC transmission should succeed but FIDL interface should
// return ZX_ERR_INVALID_ARGS.
static bool GoldfishControlTest_InvalidVmo() {
BEGIN_TEST;
int fd = open("/dev/class/goldfish-control/000", O_RDWR);
EXPECT_GE(fd, 0);
zx::channel channel;
EXPECT_EQ(fdio_get_service_handle(fd, channel.reset_and_get_address()), ZX_OK);
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);
zx_status_t status2 = ZX_OK;
EXPECT_EQ(fuchsia_hardware_goldfish_ControlDeviceCreateColorBuffer(
channel.get(), vmo_copy.release(), 16, 16,
fuchsia_hardware_goldfish_ColorBufferFormatType_BGRA, &status2),
ZX_OK);
EXPECT_EQ(status2, ZX_ERR_INVALID_ARGS);
// Call GetColorBuffer() 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);
status2 = ZX_OK;
uint32_t id = 0;
EXPECT_EQ(fuchsia_hardware_goldfish_ControlDeviceGetColorBuffer(
channel.get(), vmo_copy2.release(), &status2, &id),
ZX_OK);
EXPECT_EQ(status2, ZX_ERR_INVALID_ARGS);
END_TEST;
}
// In this test case we call GetColorBuffer() 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.
static bool GoldfishControlTest_GetNotCreatedColorBuffer() {
BEGIN_TEST;
int fd = open("/dev/class/goldfish-control/000", O_RDWR);
EXPECT_GE(fd, 0);
zx::channel channel;
EXPECT_EQ(fdio_get_service_handle(fd, channel.reset_and_get_address()), ZX_OK);
zx::channel allocator_client;
zx::channel allocator_server;
EXPECT_EQ(zx::channel::create(0, &allocator_client, &allocator_server), ZX_OK);
EXPECT_EQ(fdio_service_connect("/svc/fuchsia.sysmem.Allocator", allocator_server.release()),
ZX_OK);
zx::channel token_client;
zx::channel token_server;
EXPECT_EQ(zx::channel::create(0, &token_client, &token_server), ZX_OK);
EXPECT_EQ(fuchsia_sysmem_AllocatorAllocateSharedCollection(allocator_client.get(),
token_server.release()),
ZX_OK);
zx::channel collection_client;
zx::channel collection_server;
EXPECT_EQ(zx::channel::create(0, &collection_client, &collection_server), ZX_OK);
EXPECT_EQ(fuchsia_sysmem_AllocatorBindSharedCollection(
allocator_client.get(), token_client.release(), collection_server.release()),
ZX_OK);
BufferCollectionConstraints constraints(BufferCollectionConstraints::Default);
constraints->usage.vulkan = fuchsia_sysmem_vulkanUsageTransferDst;
constraints->min_buffer_count_for_camping = 1;
constraints->has_buffer_memory_constraints = true;
constraints->buffer_memory_constraints = fuchsia_sysmem_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_HeapType_GOLDFISH_DEVICE_LOCAL}};
EXPECT_EQ(fuchsia_sysmem_BufferCollectionSetConstraints(collection_client.get(), true,
constraints.release()),
ZX_OK);
zx_status_t status2 = ZX_OK;
BufferCollectionInfo info(BufferCollectionInfo::Default);
EXPECT_EQ(fuchsia_sysmem_BufferCollectionWaitForBuffersAllocated(collection_client.get(),
&status2, info.get()),
ZX_OK);
EXPECT_EQ(status2, ZX_OK);
EXPECT_EQ(info->buffer_count, 1);
EXPECT_NE(info->buffers[0].vmo, ZX_HANDLE_INVALID);
zx::vmo vmo(info->buffers[0].vmo);
info->buffers[0].vmo = ZX_HANDLE_INVALID;
EXPECT_TRUE(vmo.is_valid());
EXPECT_EQ(fuchsia_sysmem_BufferCollectionClose(collection_client.get()), ZX_OK);
zx::vmo vmo_copy;
EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK);
status2 = ZX_OK;
uint32_t id = 0;
EXPECT_EQ(fuchsia_hardware_goldfish_ControlDeviceGetColorBuffer(channel.get(), vmo_copy.release(),
&status2, &id),
ZX_OK);
EXPECT_EQ(status2, ZX_ERR_NOT_FOUND);
END_TEST;
}
BEGIN_TEST_CASE(GoldfishControlTests)
RUN_TEST(GoldfishControlTest)
RUN_TEST(GoldfishControlTest_InvalidVmo)
RUN_TEST(GoldfishControlTest_GetNotCreatedColorBuffer)
END_TEST_CASE(GoldfishControlTests)
static bool GoldfishAddressSpaceTest() {
BEGIN_TEST;
int fd = open("/dev/class/goldfish-address-space/000", O_RDWR);
EXPECT_GE(fd, 0);
zx::channel parent_channel;
EXPECT_EQ(fdio_get_service_handle(fd, parent_channel.reset_and_get_address()), ZX_OK);
zx::channel child_channel;
zx::channel child_channel2;
EXPECT_EQ(zx::channel::create(0, &child_channel, &child_channel2), ZX_OK);
EXPECT_EQ(fuchsia_hardware_goldfish_AddressSpaceDeviceOpenChildDriver(
parent_channel.get(), fuchsia_hardware_goldfish_AddressSpaceChildDriverType_DEFAULT,
child_channel.get()),
ZX_OK);
constexpr uint64_t kHeapSize = 16ULL * 1048576ULL;
zx_status_t res;
uint64_t actual_size = 0;
uint64_t paddr = 0;
zx::vmo vmo;
EXPECT_EQ(fuchsia_hardware_goldfish_AddressSpaceChildDriverAllocateBlock(
child_channel2.get(), kHeapSize, &res, &paddr, vmo.reset_and_get_address()),
ZX_OK);
EXPECT_EQ(res, ZX_OK);
EXPECT_NE(paddr, 0);
EXPECT_EQ(vmo.is_valid(), true);
EXPECT_EQ(vmo.get_size(&actual_size), ZX_OK);
EXPECT_GE(actual_size, kHeapSize);
uint64_t paddr2 = 0;
zx::vmo vmo2;
EXPECT_EQ(fuchsia_hardware_goldfish_AddressSpaceChildDriverAllocateBlock(
child_channel2.get(), kHeapSize, &res, &paddr2, vmo2.reset_and_get_address()),
ZX_OK);
EXPECT_EQ(res, ZX_OK);
EXPECT_NE(paddr2, 0);
EXPECT_NE(paddr2, paddr);
EXPECT_EQ(vmo2.is_valid(), true);
EXPECT_EQ(vmo.get_size(&actual_size), ZX_OK);
EXPECT_GE(actual_size, kHeapSize);
EXPECT_EQ(fuchsia_hardware_goldfish_AddressSpaceChildDriverDeallocateBlock(child_channel2.get(),
paddr, &res),
ZX_OK);
EXPECT_EQ(res, ZX_OK);
EXPECT_EQ(fuchsia_hardware_goldfish_AddressSpaceChildDriverDeallocateBlock(child_channel2.get(),
paddr2, &res),
ZX_OK);
EXPECT_EQ(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]);
fuchsia_hardware_goldfish_AddressSpaceChildDriverPingMessage msg;
msg.metadata = 0;
fuchsia_hardware_goldfish_AddressSpaceChildDriverPingMessage msg_out;
EXPECT_EQ(fuchsia_hardware_goldfish_AddressSpaceChildDriverPing(child_channel2.get(), &msg, &res,
&msg_out),
ZX_OK);
zx_handle_t shared_vmo_handle = ZX_HANDLE_INVALID;
EXPECT_EQ(fuchsia_hardware_goldfish_AddressSpaceChildDriverClaimSharedBlock(
child_channel2.get(), shared_offset, shared_size, &res, &shared_vmo_handle),
ZX_OK);
EXPECT_EQ(res, ZX_OK);
// Test that overlapping blocks cannot be claimed in the same connection.
for (size_t i = 0; i < overlaps_to_test; ++i) {
EXPECT_EQ(
fuchsia_hardware_goldfish_AddressSpaceChildDriverClaimSharedBlock(
child_channel2.get(), overlap_offsets[i], overlap_sizes[i], &res, &shared_vmo_handle),
ZX_OK);
EXPECT_EQ(res, ZX_ERR_INVALID_ARGS);
}
EXPECT_EQ(fuchsia_hardware_goldfish_AddressSpaceChildDriverUnclaimSharedBlock(
child_channel2.get(), shared_offset, &res),
ZX_OK);
EXPECT_EQ(res, ZX_OK);
// Test that removed or unknown offsets cannot be unclaimed.
EXPECT_EQ(fuchsia_hardware_goldfish_AddressSpaceChildDriverUnclaimSharedBlock(
child_channel2.get(), shared_offset, &res),
ZX_OK);
EXPECT_EQ(res, ZX_ERR_INVALID_ARGS);
EXPECT_EQ(fuchsia_hardware_goldfish_AddressSpaceChildDriverUnclaimSharedBlock(
child_channel2.get(), 0, &res),
ZX_OK);
EXPECT_EQ(res, ZX_ERR_INVALID_ARGS);
END_TEST;
}
BEGIN_TEST_CASE(GoldfishAddressSpaceTests)
RUN_TEST(GoldfishAddressSpaceTest)
END_TEST_CASE(GoldfishAddressSpaceTests)
int main(int argc, char** argv) {
if (access("/dev/sys/platform/acpi/goldfish", F_OK) != -1) {
return unittest_run_all_tests(argc, argv) ? 0 : -1;
}
return 0;
}