| // 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 <string.h> |
| |
| #include <fuchsia/guest/cpp/fidl.h> |
| #include <fuchsia/guest/device/cpp/fidl.h> |
| #include <virtio/wl.h> |
| |
| #include <lib/fxl/arraysize.h> |
| #include <lib/zx/socket.h> |
| |
| #include "garnet/bin/guest/vmm/device/test_with_device.h" |
| #include "garnet/bin/guest/vmm/device/virtio_queue_fake.h" |
| |
| namespace { |
| |
| #define VIRTWL_VQ_IN 0 |
| #define VIRTWL_VQ_OUT 1 |
| #define VIRTWL_VQ_MAGMA_IN 2 |
| #define VIRTWL_VQ_MAGMA_OUT 3 |
| #define VIRTWL_NEXT_VFD_ID_BASE 0x40000000 |
| |
| static constexpr char kVirtioWlUrl[] = |
| "fuchsia-pkg://fuchsia.com/virtio_wl#meta/virtio_wl.cmx"; |
| static constexpr uint16_t kNumQueues = 2; |
| static constexpr uint16_t kQueueSize = 32; |
| static constexpr uint32_t kVirtioWlVmarSize = 1 << 16; |
| static constexpr uint32_t kAllocateFlags = |
| ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE; |
| |
| class TestWaylandDispatcher : public fuchsia::guest::WaylandDispatcher { |
| public: |
| TestWaylandDispatcher(fit::function<void(zx::channel)> callback) |
| : callback_(std::move(callback)) {} |
| |
| fidl::InterfaceHandle<fuchsia::guest::WaylandDispatcher> Bind() { |
| return binding_.NewBinding(); |
| } |
| |
| private: |
| void OnNewConnection(zx::channel channel) { callback_(std::move(channel)); } |
| |
| fit::function<void(zx::channel)> callback_; |
| fidl::Binding<fuchsia::guest::WaylandDispatcher> binding_{this}; |
| }; |
| |
| class VirtioWlTest : public TestWithDevice { |
| public: |
| VirtioWlTest() |
| : wl_dispatcher_([this](zx::channel channel) { |
| channels_.emplace_back(std::move(channel)); |
| }), |
| in_queue_(phys_mem_, PAGE_SIZE * kNumQueues, kQueueSize), |
| out_queue_(phys_mem_, in_queue_.end(), kQueueSize) {} |
| |
| void SetUp() override { |
| uintptr_t vmar_addr; |
| zx::vmar vmar; |
| ASSERT_EQ(zx::vmar::root_self()->allocate( |
| 0u, kVirtioWlVmarSize, kAllocateFlags, &vmar, &vmar_addr), |
| ZX_OK); |
| fuchsia::guest::device::StartInfo start_info; |
| zx_status_t status = |
| LaunchDevice(kVirtioWlUrl, out_queue_.end(), &start_info); |
| ASSERT_EQ(ZX_OK, status); |
| |
| // Start device execution. |
| services.ConnectToService(wl_.NewRequest()); |
| wl_->Start(std::move(start_info), std::move(vmar), wl_dispatcher_.Bind()); |
| ASSERT_EQ(ZX_OK, status); |
| |
| // Configure device queues. |
| VirtioQueueFake* queues[kNumQueues] = {&in_queue_, &out_queue_}; |
| for (size_t i = 0; i < kNumQueues; i++) { |
| auto q = queues[i]; |
| q->Configure(PAGE_SIZE * i, PAGE_SIZE); |
| status = |
| wl_->ConfigureQueue(i, q->size(), q->desc(), q->avail(), q->used()); |
| ASSERT_EQ(ZX_OK, status); |
| } |
| } |
| |
| zx_status_t CreateNew(uint32_t vfd_id, uint8_t byte) { |
| virtio_wl_ctrl_vfd_new_t request = {}; |
| request.hdr.type = VIRTIO_WL_CMD_VFD_NEW; |
| request.vfd_id = vfd_id; |
| request.size = PAGE_SIZE; |
| virtio_wl_ctrl_vfd_new_t* response; |
| uint16_t descriptor_id; |
| zx_status_t status = |
| DescriptorChainBuilder(out_queue_) |
| .AppendReadableDescriptor(&request, sizeof(request)) |
| .AppendWritableDescriptor(&response, sizeof(*response)) |
| .Build(&descriptor_id); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = wl_->NotifyQueue(VIRTWL_VQ_OUT); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = WaitOnInterrupt(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| auto used_elem = out_queue_.NextUsed(); |
| if (!used_elem || used_elem->id != descriptor_id || |
| used_elem->len != sizeof(*response) || |
| response->hdr.type != VIRTIO_WL_RESP_VFD_NEW || !response->pfn || |
| response->size != PAGE_SIZE) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| memset(reinterpret_cast<void*>(response->pfn * PAGE_SIZE), byte, PAGE_SIZE); |
| return ZX_OK; |
| } |
| |
| zx_status_t CreateConnection(uint32_t vfd_id) { |
| virtio_wl_ctrl_vfd_new_t request = {}; |
| request.hdr.type = VIRTIO_WL_CMD_VFD_NEW_CTX; |
| request.vfd_id = vfd_id; |
| virtio_wl_ctrl_vfd_new_t* response; |
| uint16_t descriptor_id; |
| zx_status_t status = |
| DescriptorChainBuilder(out_queue_) |
| .AppendReadableDescriptor(&request, sizeof(request)) |
| .AppendWritableDescriptor(&response, sizeof(*response)) |
| .Build(&descriptor_id); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = wl_->NotifyQueue(VIRTWL_VQ_OUT); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = WaitOnInterrupt(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| auto used_elem = out_queue_.NextUsed(); |
| return (used_elem && used_elem->id == descriptor_id && |
| used_elem->len == sizeof(*response) && |
| response->hdr.type == VIRTIO_WL_RESP_VFD_NEW) |
| ? ZX_OK |
| : ZX_ERR_INTERNAL; |
| } |
| |
| zx_status_t CreatePipe(uint32_t vfd_id) { |
| virtio_wl_ctrl_vfd_new_t request = {}; |
| request.hdr.type = VIRTIO_WL_CMD_VFD_NEW_PIPE; |
| request.vfd_id = vfd_id; |
| request.flags = VIRTIO_WL_VFD_READ; |
| virtio_wl_ctrl_vfd_new_t* response; |
| uint16_t descriptor_id; |
| zx_status_t status = |
| DescriptorChainBuilder(out_queue_) |
| .AppendReadableDescriptor(&request, sizeof(request)) |
| .AppendWritableDescriptor(&response, sizeof(*response)) |
| .Build(&descriptor_id); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = wl_->NotifyQueue(VIRTWL_VQ_OUT); |
| if (status != ZX_OK) { |
| return status; |
| } |
| status = WaitOnInterrupt(); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| auto used_elem = out_queue_.NextUsed(); |
| return (used_elem && used_elem->id == descriptor_id && |
| used_elem->len == sizeof(*response) && |
| response->hdr.type == VIRTIO_WL_RESP_VFD_NEW) |
| ? ZX_OK |
| : ZX_ERR_INTERNAL; |
| } |
| |
| protected: |
| TestWaylandDispatcher wl_dispatcher_; |
| fuchsia::guest::device::VirtioWaylandSyncPtr wl_; |
| VirtioQueueFake in_queue_; |
| VirtioQueueFake out_queue_; |
| std::vector<zx::channel> channels_; |
| }; |
| |
| TEST_F(VirtioWlTest, HandleNew) { |
| virtio_wl_ctrl_vfd_new_t request = {}; |
| request.hdr.type = VIRTIO_WL_CMD_VFD_NEW; |
| request.vfd_id = 1u; |
| request.size = 4000u; |
| virtio_wl_ctrl_vfd_new_t* response; |
| uint16_t descriptor_id; |
| ASSERT_EQ(DescriptorChainBuilder(out_queue_) |
| .AppendReadableDescriptor(&request, sizeof(request)) |
| .AppendWritableDescriptor(&response, sizeof(*response)) |
| .Build(&descriptor_id), |
| ZX_OK); |
| |
| ASSERT_EQ(wl_->NotifyQueue(VIRTWL_VQ_OUT), ZX_OK); |
| ASSERT_EQ(WaitOnInterrupt(), ZX_OK); |
| |
| auto used_elem = out_queue_.NextUsed(); |
| EXPECT_TRUE(used_elem); |
| EXPECT_EQ(used_elem->id, descriptor_id); |
| EXPECT_EQ(used_elem->len, sizeof(*response)); |
| EXPECT_EQ(response->hdr.type, VIRTIO_WL_RESP_VFD_NEW); |
| EXPECT_EQ(response->hdr.flags, 0u); |
| EXPECT_EQ(response->vfd_id, 1u); |
| EXPECT_EQ(response->flags, |
| static_cast<uint32_t>(VIRTIO_WL_VFD_READ | VIRTIO_WL_VFD_WRITE)); |
| EXPECT_GT(response->pfn, 0u); |
| EXPECT_EQ(response->size, static_cast<uint32_t>(PAGE_SIZE)); |
| memset(reinterpret_cast<void*>(response->pfn * PAGE_SIZE), 0xff, 4000u); |
| } |
| |
| TEST_F(VirtioWlTest, HandleClose) { |
| ASSERT_EQ(CreateNew(1u, 0xff), ZX_OK); |
| |
| virtio_wl_ctrl_vfd_t request = {}; |
| request.hdr.type = VIRTIO_WL_CMD_VFD_CLOSE; |
| request.vfd_id = 1u; |
| virtio_wl_ctrl_hdr_t* response; |
| uint16_t descriptor_id; |
| ASSERT_EQ(DescriptorChainBuilder(out_queue_) |
| .AppendReadableDescriptor(&request, sizeof(request)) |
| .AppendWritableDescriptor(&response, sizeof(*response)) |
| .Build(&descriptor_id), |
| ZX_OK); |
| |
| ASSERT_EQ(wl_->NotifyQueue(VIRTWL_VQ_OUT), ZX_OK); |
| ASSERT_EQ(WaitOnInterrupt(), ZX_OK); |
| auto used_elem = out_queue_.NextUsed(); |
| EXPECT_TRUE(used_elem); |
| EXPECT_EQ(used_elem->id, descriptor_id); |
| EXPECT_EQ(used_elem->len, sizeof(*response)); |
| EXPECT_EQ(response->type, VIRTIO_WL_RESP_OK); |
| } |
| |
| TEST_F(VirtioWlTest, HandleNewCtx) { |
| virtio_wl_ctrl_vfd_new_t request = {}; |
| request.hdr.type = VIRTIO_WL_CMD_VFD_NEW_CTX; |
| request.vfd_id = 1u; |
| virtio_wl_ctrl_vfd_new_t* response; |
| uint16_t descriptor_id; |
| ASSERT_EQ(DescriptorChainBuilder(out_queue_) |
| .AppendReadableDescriptor(&request, sizeof(request)) |
| .AppendWritableDescriptor(&response, sizeof(*response)) |
| .Build(&descriptor_id), |
| ZX_OK); |
| |
| ASSERT_EQ(wl_->NotifyQueue(VIRTWL_VQ_OUT), ZX_OK); |
| ASSERT_EQ(WaitOnInterrupt(), ZX_OK); |
| auto used_elem = out_queue_.NextUsed(); |
| EXPECT_TRUE(used_elem); |
| EXPECT_EQ(used_elem->id, descriptor_id); |
| EXPECT_EQ(used_elem->len, sizeof(*response)); |
| EXPECT_EQ(response->hdr.type, VIRTIO_WL_RESP_VFD_NEW); |
| EXPECT_EQ(response->hdr.flags, 0u); |
| EXPECT_EQ(response->vfd_id, 1u); |
| EXPECT_EQ(response->flags, |
| static_cast<uint32_t>(VIRTIO_WL_VFD_READ | VIRTIO_WL_VFD_WRITE)); |
| |
| RunLoopUntilIdle(); |
| EXPECT_EQ(channels_.size(), 1u); |
| channels_.clear(); |
| } |
| |
| TEST_F(VirtioWlTest, HandleNewPipe) { |
| virtio_wl_ctrl_vfd_new_t request = {}; |
| request.hdr.type = VIRTIO_WL_CMD_VFD_NEW_PIPE; |
| request.vfd_id = 1u; |
| request.flags = VIRTIO_WL_VFD_READ; |
| virtio_wl_ctrl_vfd_new_t* response; |
| uint16_t descriptor_id; |
| ASSERT_EQ(DescriptorChainBuilder(out_queue_) |
| .AppendReadableDescriptor(&request, sizeof(request)) |
| .AppendWritableDescriptor(&response, sizeof(*response)) |
| .Build(&descriptor_id), |
| ZX_OK); |
| |
| ASSERT_EQ(wl_->NotifyQueue(VIRTWL_VQ_OUT), ZX_OK); |
| ASSERT_EQ(WaitOnInterrupt(), ZX_OK); |
| auto used_elem = out_queue_.NextUsed(); |
| EXPECT_TRUE(used_elem); |
| EXPECT_EQ(used_elem->id, descriptor_id); |
| EXPECT_EQ(used_elem->len, sizeof(*response)); |
| EXPECT_EQ(response->hdr.type, VIRTIO_WL_RESP_VFD_NEW); |
| EXPECT_EQ(response->hdr.flags, 0u); |
| EXPECT_EQ(response->vfd_id, 1u); |
| EXPECT_EQ(response->flags, static_cast<uint32_t>(VIRTIO_WL_VFD_READ)); |
| } |
| |
| TEST_F(VirtioWlTest, HandleDmabuf) { |
| virtio_wl_ctrl_vfd_new_t request = {}; |
| request.hdr.type = VIRTIO_WL_CMD_VFD_NEW_DMABUF; |
| request.vfd_id = 1u; |
| request.dmabuf.format = 0x34325241; // DRM_FORMAT_ARGB8888 |
| request.dmabuf.width = 64; |
| request.dmabuf.height = 64; |
| virtio_wl_ctrl_vfd_new_t* response; |
| uint16_t descriptor_id; |
| ASSERT_EQ(DescriptorChainBuilder(out_queue_) |
| .AppendReadableDescriptor(&request, sizeof(request)) |
| .AppendWritableDescriptor(&response, sizeof(*response)) |
| .Build(&descriptor_id), |
| ZX_OK); |
| |
| ASSERT_EQ(wl_->NotifyQueue(VIRTWL_VQ_OUT), ZX_OK); |
| ASSERT_EQ(WaitOnInterrupt(), ZX_OK); |
| |
| auto used_elem = out_queue_.NextUsed(); |
| EXPECT_TRUE(used_elem); |
| EXPECT_EQ(used_elem->id, descriptor_id); |
| EXPECT_EQ(used_elem->len, sizeof(*response)); |
| EXPECT_EQ(response->hdr.type, VIRTIO_WL_RESP_VFD_NEW_DMABUF); |
| EXPECT_EQ(response->hdr.flags, 0u); |
| EXPECT_EQ(response->vfd_id, 1u); |
| EXPECT_EQ(response->flags, |
| static_cast<uint32_t>(VIRTIO_WL_VFD_READ | VIRTIO_WL_VFD_WRITE)); |
| EXPECT_GT(response->pfn, 0u); |
| EXPECT_GT(response->size, 0u); |
| } |
| |
| TEST_F(VirtioWlTest, HandleSend) { |
| ASSERT_EQ(CreateNew(1u, 0xaa), ZX_OK); |
| ASSERT_EQ(CreatePipe(2u), ZX_OK); |
| ASSERT_EQ(CreateConnection(3u), ZX_OK); |
| |
| RunLoopUntilIdle(); |
| ASSERT_EQ(channels_.size(), 1u); |
| |
| uint8_t request[sizeof(virtio_wl_ctrl_vfd_send_t) + sizeof(uint32_t) * 3]; |
| virtio_wl_ctrl_vfd_send_t* header = |
| reinterpret_cast<virtio_wl_ctrl_vfd_send_t*>(request); |
| header->hdr.type = VIRTIO_WL_CMD_VFD_SEND; |
| header->vfd_id = 3u; |
| header->vfd_count = 2u; |
| uint32_t* vfds = reinterpret_cast<uint32_t*>(header + 1); |
| vfds[0] = 1u; |
| vfds[1] = 2u; |
| vfds[2] = 1234u; // payload |
| virtio_wl_ctrl_hdr_t* response; |
| uint16_t descriptor_id; |
| ASSERT_EQ(DescriptorChainBuilder(out_queue_) |
| .AppendReadableDescriptor(&request, sizeof(request)) |
| .AppendWritableDescriptor(&response, sizeof(*response)) |
| .Build(&descriptor_id), |
| ZX_OK); |
| |
| ASSERT_EQ(wl_->NotifyQueue(VIRTWL_VQ_OUT), ZX_OK); |
| ASSERT_EQ(WaitOnInterrupt(), ZX_OK); |
| auto used_elem = out_queue_.NextUsed(); |
| EXPECT_TRUE(used_elem); |
| EXPECT_EQ(used_elem->id, descriptor_id); |
| EXPECT_EQ(used_elem->len, sizeof(*response)); |
| EXPECT_EQ(response->type, VIRTIO_WL_RESP_OK); |
| |
| uint32_t data; |
| zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES]; |
| uint32_t actual_bytes, actual_handles; |
| ASSERT_EQ(zx_channel_read(channels_[0].get(), 0, &data, handles, sizeof(data), |
| arraysize(handles), &actual_bytes, &actual_handles), |
| ZX_OK); |
| EXPECT_EQ(actual_handles, 2u); |
| EXPECT_EQ(actual_bytes, sizeof(data)); |
| EXPECT_EQ(data, 1234u); |
| |
| zx::vmo vmo(handles[0]); |
| zx::socket socket(handles[1]); |
| |
| // Verify data transfer using shared memory. |
| uintptr_t addr; |
| ASSERT_EQ( |
| zx::vmar::root_self()->map(0, vmo, 0, PAGE_SIZE, |
| ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, &addr), |
| ZX_OK); |
| EXPECT_EQ(*reinterpret_cast<uint8_t*>(addr), 0xaa); |
| ASSERT_EQ(zx::vmar::root_self()->unmap(addr, PAGE_SIZE), ZX_OK); |
| |
| // Verify data transfer over pipe. |
| size_t actual_size; |
| ASSERT_EQ(socket.write(0, &data, sizeof(data), &actual_size), ZX_OK); |
| EXPECT_EQ(actual_size, sizeof(data)); |
| RunLoopUntilIdle(); |
| |
| size_t buffer_size = sizeof(virtio_wl_ctrl_vfd_recv_t) + sizeof(data); |
| uint8_t* buffer; |
| ASSERT_EQ(DescriptorChainBuilder(in_queue_) |
| .AppendWritableDescriptor(&buffer, buffer_size) |
| .Build(&descriptor_id), |
| ZX_OK); |
| virtio_wl_ctrl_vfd_recv_t* recv_header = |
| reinterpret_cast<virtio_wl_ctrl_vfd_recv_t*>(buffer); |
| |
| ASSERT_EQ(wl_->NotifyQueue(VIRTWL_VQ_IN), ZX_OK); |
| ASSERT_EQ(WaitOnInterrupt(), ZX_OK); |
| used_elem = in_queue_.NextUsed(); |
| EXPECT_TRUE(used_elem); |
| EXPECT_EQ(used_elem->id, descriptor_id); |
| EXPECT_EQ(used_elem->len, buffer_size); |
| EXPECT_EQ(recv_header->hdr.type, VIRTIO_WL_CMD_VFD_RECV); |
| EXPECT_EQ(recv_header->hdr.flags, 0u); |
| EXPECT_EQ(recv_header->vfd_id, 2u); |
| EXPECT_EQ(recv_header->vfd_count, 0u); |
| EXPECT_EQ(*reinterpret_cast<uint32_t*>(recv_header + 1), 1234u); |
| |
| channels_.clear(); |
| } |
| |
| TEST_F(VirtioWlTest, Recv) { |
| ASSERT_EQ(CreateConnection(1u), ZX_OK); |
| RunLoopUntilIdle(); |
| ASSERT_EQ(channels_.size(), 1u); |
| |
| zx::vmo vmo; |
| ASSERT_EQ(zx::vmo::create(PAGE_SIZE, 0, &vmo), ZX_OK); |
| uintptr_t addr; |
| ASSERT_EQ( |
| zx::vmar::root_self()->map(0, vmo, 0, PAGE_SIZE, |
| ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, &addr), |
| ZX_OK); |
| memset(reinterpret_cast<void*>(addr), 0xaa, PAGE_SIZE); |
| ASSERT_EQ(zx::vmar::root_self()->unmap(addr, PAGE_SIZE), ZX_OK); |
| |
| zx::socket socket, remote_socket; |
| ASSERT_EQ(zx::socket::create(0, &socket, &remote_socket), ZX_OK); |
| |
| uint32_t data = 1234u; |
| zx_handle_t handles[] = {vmo.release(), remote_socket.release()}; |
| ASSERT_EQ(zx_channel_write(channels_[0].get(), 0, &data, sizeof(data), |
| handles, fbl::count_of(handles)), |
| ZX_OK); |
| RunLoopUntilIdle(); |
| |
| size_t buffer_size = |
| sizeof(virtio_wl_ctrl_vfd_new_t) * fbl::count_of(handles) + |
| sizeof(virtio_wl_ctrl_vfd_recv_t) + |
| sizeof(uint32_t) * fbl::count_of(handles) + sizeof(data); |
| uint8_t* buffer; |
| uint16_t descriptor_id; |
| ASSERT_EQ(DescriptorChainBuilder(in_queue_) |
| .AppendWritableDescriptor(&buffer, buffer_size) |
| .Build(&descriptor_id), |
| ZX_OK); |
| virtio_wl_ctrl_vfd_new_t* new_vfd_cmd = |
| reinterpret_cast<virtio_wl_ctrl_vfd_new_t*>(buffer); |
| virtio_wl_ctrl_vfd_recv_t* header = |
| reinterpret_cast<virtio_wl_ctrl_vfd_recv_t*>(new_vfd_cmd + |
| fbl::count_of(handles)); |
| uint32_t* vfds = reinterpret_cast<uint32_t*>(header + 1); |
| |
| ASSERT_EQ(wl_->NotifyQueue(VIRTWL_VQ_IN), ZX_OK); |
| ASSERT_EQ(WaitOnInterrupt(), ZX_OK); |
| auto used_elem = in_queue_.NextUsed(); |
| EXPECT_TRUE(used_elem); |
| EXPECT_EQ(used_elem->id, descriptor_id); |
| EXPECT_EQ(used_elem->len, buffer_size); |
| |
| EXPECT_EQ(new_vfd_cmd[0].hdr.type, VIRTIO_WL_CMD_VFD_NEW); |
| EXPECT_EQ(new_vfd_cmd[0].hdr.flags, 0u); |
| EXPECT_EQ(new_vfd_cmd[0].vfd_id, |
| static_cast<uint32_t>(VIRTWL_NEXT_VFD_ID_BASE)); |
| EXPECT_EQ(new_vfd_cmd[0].flags, |
| static_cast<uint32_t>(VIRTIO_WL_VFD_READ | VIRTIO_WL_VFD_WRITE)); |
| EXPECT_GT(new_vfd_cmd[0].pfn, 0u); |
| EXPECT_EQ(new_vfd_cmd[0].size, static_cast<uint32_t>(PAGE_SIZE)); |
| EXPECT_EQ(*reinterpret_cast<uint8_t*>(new_vfd_cmd[0].pfn * PAGE_SIZE), 0xaa); |
| |
| EXPECT_EQ(new_vfd_cmd[1].hdr.type, VIRTIO_WL_CMD_VFD_NEW_PIPE); |
| EXPECT_EQ(new_vfd_cmd[1].hdr.flags, 0u); |
| EXPECT_EQ(new_vfd_cmd[1].vfd_id, |
| static_cast<uint32_t>(VIRTWL_NEXT_VFD_ID_BASE + 1)); |
| EXPECT_EQ(new_vfd_cmd[1].flags, |
| static_cast<uint32_t>(VIRTIO_WL_VFD_READ | VIRTIO_WL_VFD_WRITE)); |
| |
| EXPECT_EQ(header->hdr.type, VIRTIO_WL_CMD_VFD_RECV); |
| EXPECT_EQ(header->hdr.flags, 0u); |
| EXPECT_EQ(header->vfd_id, 1u); |
| EXPECT_EQ(header->vfd_count, 2u); |
| EXPECT_EQ(vfds[0], static_cast<uint32_t>(VIRTWL_NEXT_VFD_ID_BASE)); |
| EXPECT_EQ(vfds[1], static_cast<uint32_t>(VIRTWL_NEXT_VFD_ID_BASE + 1)); |
| EXPECT_EQ(*reinterpret_cast<uint32_t*>(vfds + fbl::count_of(handles)), 1234u); |
| |
| // Check that closing shared memory works as expected. |
| virtio_wl_ctrl_vfd_t request = {}; |
| request.hdr.type = VIRTIO_WL_CMD_VFD_CLOSE; |
| request.vfd_id = VIRTWL_NEXT_VFD_ID_BASE; |
| virtio_wl_ctrl_hdr_t* response; |
| ASSERT_EQ(DescriptorChainBuilder(out_queue_) |
| .AppendReadableDescriptor(&request, sizeof(request)) |
| .AppendWritableDescriptor(&response, sizeof(*response)) |
| .Build(&descriptor_id), |
| ZX_OK); |
| |
| ASSERT_EQ(wl_->NotifyQueue(VIRTWL_VQ_OUT), ZX_OK); |
| ASSERT_EQ(WaitOnInterrupt(), ZX_OK); |
| used_elem = out_queue_.NextUsed(); |
| EXPECT_TRUE(used_elem); |
| EXPECT_EQ(used_elem->id, descriptor_id); |
| EXPECT_EQ(used_elem->len, sizeof(*response)); |
| EXPECT_EQ(response->type, VIRTIO_WL_RESP_OK); |
| |
| // Check that writing to pipe works as expected. |
| uint8_t send_request[sizeof(virtio_wl_ctrl_vfd_send_t) + sizeof(uint32_t)]; |
| virtio_wl_ctrl_vfd_send_t* send_header = |
| reinterpret_cast<virtio_wl_ctrl_vfd_send_t*>(send_request); |
| send_header->hdr.type = VIRTIO_WL_CMD_VFD_SEND; |
| send_header->vfd_id = VIRTWL_NEXT_VFD_ID_BASE + 1; |
| send_header->vfd_count = 0; |
| *reinterpret_cast<uint32_t*>(send_header + 1) = 1234u; // payload |
| virtio_wl_ctrl_hdr_t* send_response; |
| ASSERT_EQ( |
| DescriptorChainBuilder(out_queue_) |
| .AppendReadableDescriptor(&send_request, sizeof(send_request)) |
| .AppendWritableDescriptor(&send_response, sizeof(*send_response)) |
| .Build(&descriptor_id), |
| ZX_OK); |
| |
| ASSERT_EQ(wl_->NotifyQueue(VIRTWL_VQ_OUT), ZX_OK); |
| ASSERT_EQ(WaitOnInterrupt(), ZX_OK); |
| used_elem = out_queue_.NextUsed(); |
| EXPECT_TRUE(used_elem); |
| EXPECT_EQ(used_elem->id, descriptor_id); |
| EXPECT_EQ(used_elem->len, sizeof(*send_response)); |
| EXPECT_EQ(send_response->type, VIRTIO_WL_RESP_OK); |
| |
| uint32_t pipe_data; |
| size_t actual_bytes; |
| ASSERT_EQ(socket.read(0, &pipe_data, sizeof(pipe_data), &actual_bytes), |
| ZX_OK); |
| EXPECT_EQ(actual_bytes, sizeof(pipe_data)); |
| EXPECT_EQ(pipe_data, 1234u); |
| |
| channels_.clear(); |
| } |
| |
| TEST_F(VirtioWlTest, Hup) { |
| ASSERT_EQ(CreateConnection(1u), ZX_OK); |
| RunLoopUntilIdle(); |
| ASSERT_EQ(channels_.size(), 1u); |
| |
| // Close remote side of channel. |
| channels_.clear(); |
| RunLoopUntilIdle(); |
| |
| virtio_wl_ctrl_vfd_t* header; |
| uint16_t descriptor_id; |
| ASSERT_EQ(DescriptorChainBuilder(in_queue_) |
| .AppendWritableDescriptor(&header, sizeof(*header)) |
| .Build(&descriptor_id), |
| ZX_OK); |
| |
| ASSERT_EQ(wl_->NotifyQueue(VIRTWL_VQ_IN), ZX_OK); |
| ASSERT_EQ(WaitOnInterrupt(), ZX_OK); |
| auto used_elem = in_queue_.NextUsed(); |
| EXPECT_TRUE(used_elem); |
| EXPECT_EQ(used_elem->id, descriptor_id); |
| EXPECT_EQ(used_elem->len, sizeof(*header)); |
| EXPECT_EQ(header->hdr.type, VIRTIO_WL_CMD_VFD_HUP); |
| EXPECT_EQ(header->hdr.flags, 0u); |
| EXPECT_EQ(header->vfd_id, 1u); |
| } |
| |
| } // namespace |