blob: fcd15bb157bf25589407078274ee3c236d1750c7 [file] [log] [blame]
// Copyright 2017 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.
#pragma once
#include <fbl/unique_ptr.h>
#include <machina/virtio.h>
#include <virtio/virtio.h>
#include <virtio/virtio_ring.h>
class VirtioQueueFake;
// Helper class for building buffer made up of chained descriptors.
//
// When building a descriptor chain, any errors are deferred until a call to
// DescBuilder::Build in order to make the interface more fluent.
class DescBuilder {
public:
DescBuilder& Append(void* addr, size_t size, bool writeable);
DescBuilder& Append(uintptr_t addr, size_t size, bool writeable) {
return Append(reinterpret_cast<void*>(addr), size, writeable);
}
// Adds a buffer to the chain that is flagged as device writeable.
DescBuilder& AppendWriteable(void* addr, size_t size) { return Append(addr, size, true); }
DescBuilder& AppendWriteable(uintptr_t addr, size_t size) { return Append(addr, size, true); }
// Adds a buffer to the chain that is flagged as device writeable.
DescBuilder& AppendReadable(void* addr, size_t size) { return Append(addr, size, false); }
DescBuilder& AppendReadable(uintptr_t addr, size_t size) { return Append(addr, size, false); }
// Make this descriptor chain visible to the device by writing the head
// index to the available ring and incrementing the available index.
//
// The index of the head descriptor is written to |desc| if it is non-null.
zx_status_t Build(uint16_t* desc);
zx_status_t Build() { return Build(nullptr); }
private:
friend class VirtioQueueFake;
DescBuilder(VirtioQueueFake* queue): queue_(queue) {}
VirtioQueueFake* queue_;
size_t len_ = 0;
uint16_t prev_desc_ = 0;
uint16_t head_desc_ = 0;
zx_status_t status_ = ZX_OK;
};
// Helper class for creating fake virtio queue requests.
//
// The device should be initialized with guest physmem at 0 so that the
// simulated guest physical address space aliases our address space.
class VirtioQueueFake {
public:
explicit VirtioQueueFake(virtio_queue_t* queue): queue_(queue) {}
~VirtioQueueFake();
// Allocate memory for a queue with the given size and wire up the queue
// to use those buffers.
zx_status_t Init(uint16_t size);
// Allocate and write a descriptor. |addr|, |len|, and |flags| correspond
// to the fields in vring_desc.
//
// The index of the allocated descriptor is written to |desc|.
//
// Descriptors are not reclaimed and it is a programming error to attempt
// to write to more than descriptors than the queue was initialized with.
// ZX_ERR_NO_MEMORY is returned if the pool of available desciptors has
// been exhausted.
zx_status_t WriteDescriptor(void* addr, size_t len, uint16_t flags, uint16_t* desc);
// Write to |desc| that it is continued via |next|.
//
// Returns ZX_ERR_INVALID_ARGS if |desc| or |next| are greater than the
// queue size.
zx_status_t SetNext(uint16_t desc, uint16_t next);
// Writes |desc| to the next entry in the available ring, making the
// descriptor chain visible to the device.
void WriteToAvail(uint16_t desc);
DescBuilder BuildDescriptor() { return DescBuilder(this); }
private:
uint16_t queue_size_;
virtio_queue_t* queue_;
fbl::unique_ptr<uint8_t[]> desc_buf_;
fbl::unique_ptr<uint8_t[]> avail_ring_buf_;
fbl::unique_ptr<uint8_t[]> used_ring_buf_;
// The next entry in the descriptor table that is available.
uint16_t next_free_desc_ = 0;
};