blob: b43a5f1a8373cd1ee3e7757cba071d8f23791a6b [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.
#include "virtio_queue_fake.h"
#include <string.h>
#include <fbl/alloc_checker.h>
zx_status_t VirtioQueueFake::Init(uint16_t queue_size) {
fbl::unique_ptr<uint8_t[]> desc;
fbl::unique_ptr<uint8_t[]> avail;
fbl::unique_ptr<uint8_t[]> used;
{
fbl::AllocChecker ac;
size_t desc_size = queue_->size * sizeof(queue_->desc[0]);
desc.reset(new (&ac) uint8_t[desc_size]);
if (!ac.check())
return ZX_ERR_NO_MEMORY;
memset(desc.get(), 0, desc_size);
}
{
fbl::AllocChecker ac;
size_t avail_size = sizeof(*queue_->avail) +
(queue_->size * sizeof(queue_->avail->ring[0])) +
sizeof(*queue_->used_event);
avail.reset(new (&ac) uint8_t[avail_size]);
if (!ac.check())
return ZX_ERR_NO_MEMORY;
memset(avail.get(), 0, avail_size);
}
{
fbl::AllocChecker ac;
size_t used_size = sizeof(*queue_->used) + (queue_->size * sizeof(queue_->used->ring[0])) +
sizeof(*queue_->avail_event);
used.reset(new (&ac) uint8_t[used_size]);
if (!ac.check())
return ZX_ERR_NO_MEMORY;
memset(used.get(), 0,used_size);
}
queue_size_ = queue_size;
desc_buf_ = fbl::move(desc);
avail_ring_buf_ = fbl::move(avail);
used_ring_buf_ = fbl::move(used);
queue_->size = queue_size;
virtio_queue_set_desc_addr(queue_, reinterpret_cast<uint64_t>(desc_buf_.get()));
virtio_queue_set_avail_addr(queue_, reinterpret_cast<uint64_t>(avail_ring_buf_.get()));
virtio_queue_set_used_addr(queue_, reinterpret_cast<uint64_t>(used_ring_buf_.get()));
return ZX_OK;
}
VirtioQueueFake::~VirtioQueueFake() {
queue_->addr.desc = 0;
queue_->desc = nullptr;
queue_->addr.avail = 0;
queue_->avail = nullptr;
queue_->addr.used = 0;
queue_->used = nullptr;
}
zx_status_t VirtioQueueFake::SetNext(uint16_t desc_index, uint16_t next_index) {
if (desc_index >= queue_size_)
return ZX_ERR_INVALID_ARGS;
if (next_index >= queue_size_)
return ZX_ERR_INVALID_ARGS;
volatile vring_desc& desc = queue_->desc[desc_index];
desc.flags |= VRING_DESC_F_NEXT;
desc.next = next_index;
return ZX_OK;
}
zx_status_t VirtioQueueFake::WriteDescriptor(void* buf, size_t len, uint16_t flags,
uint16_t* desc_out) {
uint16_t desc_index = next_free_desc_;
if (desc_index >= queue_size_)
return ZX_ERR_NO_MEMORY;
next_free_desc_++;
volatile vring_desc& desc = queue_->desc[desc_index];
desc.addr = reinterpret_cast<uint64_t>(buf);
desc.len = static_cast<uint32_t>(len);
desc.flags = flags;
if (desc_out != nullptr)
*desc_out = desc_index;
return ZX_OK;
}
void VirtioQueueFake::WriteToAvail(uint16_t desc) {
queue_->avail->ring[queue_->avail->idx++ % queue_size_] = desc;
}
zx_status_t DescBuilder::Build(uint16_t* desc) {
if (status_ == ZX_OK) {
queue_->WriteToAvail(head_desc_);
if (desc != nullptr)
*desc = head_desc_;
head_desc_ = 0;
prev_desc_ = 0;
len_ = 0;
}
return status_;
}
DescBuilder& DescBuilder::Append(void* buf, size_t buf_len, bool write) {
// If a previous Append operation failed just no-op.
if (status_ != ZX_OK)
return *this;
uint16_t flags = write ? VRING_DESC_F_WRITE : 0;
uint16_t desc;
status_ = queue_->WriteDescriptor(buf, buf_len, flags, &desc);
if (status_ == ZX_OK) {
if (len_++ == 0) {
head_desc_ = desc;
} else {
status_ = queue_->SetNext(prev_desc_, desc);
}
prev_desc_ = desc;
}
return *this;
}