| // 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 "src/virtualization/bin/vmm/virtio_queue_fake.h" |
| |
| #include <lib/syslog/cpp/macros.h> |
| #include <string.h> |
| |
| VirtioQueueFake::VirtioQueueFake(VirtioQueue* queue, uint16_t queue_size) |
| : queue_(queue), |
| ring_(&queue_->ring_), |
| queue_size_(queue_size), |
| desc_buf_(sizeof(*VirtioRing::desc) * queue_size), |
| avail_buf_(sizeof(*VirtioRing::avail) + (sizeof(*vring_avail::ring) * queue_size) + |
| sizeof(*VirtioRing::used_event)), |
| used_buf_(sizeof(*VirtioRing::used) + (sizeof(*vring_used::ring) * queue_size) + |
| sizeof(*VirtioRing::avail_event)) { |
| std::fill(desc_buf_.begin(), desc_buf_.end(), 0); |
| std::fill(avail_buf_.begin(), avail_buf_.end(), 0); |
| std::fill(used_buf_.begin(), used_buf_.end(), 0); |
| |
| queue_->Configure(queue_size, reinterpret_cast<zx_gpaddr_t>(desc_buf_.data()), |
| reinterpret_cast<zx_gpaddr_t>(avail_buf_.data()), |
| reinterpret_cast<zx_gpaddr_t>(used_buf_.data())); |
| |
| // Disable interrupt generation. |
| ring_->used->flags = 1; |
| *const_cast<uint16_t*>(ring_->used_event) = 0xffff; |
| } |
| |
| 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; |
| } |
| |
| std::lock_guard<std::mutex> lock(queue_->mutex_); |
| auto& desc = const_cast<volatile vring_desc&>(ring_->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_++; |
| |
| std::lock_guard<std::mutex> lock(queue_->mutex_); |
| auto& desc = const_cast<volatile vring_desc&>(ring_->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) { |
| std::lock_guard<std::mutex> lock(queue_->mutex_); |
| auto avail = const_cast<volatile vring_avail*>(ring_->avail); |
| uint16_t& avail_idx = const_cast<uint16_t&>(ring_->avail->idx); |
| avail->ring[avail_idx++ % queue_size_] = desc; |
| } |
| |
| bool VirtioQueueFake::HasUsed() const { |
| std::lock_guard<std::mutex> lock(queue_->mutex_); |
| return ring_->used->idx != used_index_; |
| } |
| |
| struct vring_used_elem VirtioQueueFake::NextUsed() { |
| FX_DCHECK(HasUsed()); |
| std::lock_guard<std::mutex> lock(queue_->mutex_); |
| return const_cast<struct vring_used_elem&>(ring_->used->ring[used_index_++ % queue_size_]); |
| } |
| |
| 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; |
| // Signal so that queue event signals will be set. |
| status_ = queue_->queue()->Notify(); |
| } |
| 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; |
| } |