1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "console.h" 6 7#include <ddk/debug.h> 8#include <fbl/algorithm.h> 9#include <fbl/auto_lock.h> 10#include <string.h> 11#include <virtio/virtio.h> 12#include <lib/zx/vmar.h> 13 14#define LOCAL_TRACE 0 15 16namespace virtio { 17 18namespace { 19 20zx_status_t QueueTransfer(Ring* ring, uintptr_t phys, uint32_t len, bool write) { 21 uint16_t index; 22 vring_desc* desc = ring->AllocDescChain(1, &index); 23 if (!desc) { 24 // This should not happen 25 zxlogf(ERROR, "Failed to find free descriptor for the virtio ring\n"); 26 return ZX_ERR_NO_MEMORY; 27 } 28 29 desc->addr = phys; 30 desc->len = len; 31 // writeable for the driver is readonly for the device and vice versa 32 desc->flags = write ? 0 : VRING_DESC_F_WRITE; 33 ring->SubmitChain(index); 34 35 return ZX_OK; 36} 37 38} // namespace 39 40TransferBuffer::TransferBuffer() { 41 memset(&buf_, 0, sizeof(buf_)); 42} 43 44TransferBuffer::~TransferBuffer() { 45 io_buffer_release(&buf_); 46} 47 48zx_status_t TransferBuffer::Init(const zx::bti& bti, size_t count, uint32_t chunk_size) { 49 if (!count) 50 return ZX_OK; 51 52 count_ = count; 53 chunk_size_ = chunk_size; 54 size_ = count * chunk_size; 55 56 TransferDescriptor* descriptor = new TransferDescriptor[count_]; 57 if (!descriptor) { 58 zxlogf(ERROR, "Failed to allocate transfer descriptors (%d)\n", ZX_ERR_NO_MEMORY); 59 return ZX_ERR_NO_MEMORY; 60 } 61 62 descriptor_.reset(descriptor, count_); 63 64 zx_status_t status = io_buffer_init(&buf_, bti.get(), size_, IO_BUFFER_RW | IO_BUFFER_CONTIG); 65 if (status != ZX_OK) { 66 zxlogf(ERROR, "Failed to allocate transfer buffers (%d)\n", status); 67 return status; 68 } 69 70 void* virt = io_buffer_virt(&buf_); 71 zx_paddr_t phys = io_buffer_phys(&buf_); 72 for (size_t i = 0; i < count_; ++i) { 73 TransferDescriptor& desc = descriptor_[i]; 74 75 desc.virt = reinterpret_cast<uint8_t*>(virt) + i * chunk_size; 76 desc.phys = phys + i * chunk_size; 77 desc.total_len = chunk_size; 78 desc.used_len = 0; 79 desc.processed_len = 0; 80 } 81 82 return ZX_OK; 83} 84 85TransferDescriptor* TransferBuffer::GetDescriptor(size_t index) { 86 if (index > count_) 87 return nullptr; 88 return &descriptor_[index]; 89} 90 91TransferDescriptor* TransferBuffer::PhysicalToDescriptor(uintptr_t phys) { 92 zx_paddr_t base = io_buffer_phys(&buf_); 93 if (phys < base || phys >= base + size_) 94 return nullptr; 95 return &descriptor_[(phys - base) / chunk_size_]; 96} 97 98void TransferQueue::Add(TransferDescriptor* desc) { 99 queue_.push_front(desc); 100} 101 102TransferDescriptor* TransferQueue::Peek() { 103 if (queue_.is_empty()) 104 return nullptr; 105 return &queue_.back(); 106} 107 108TransferDescriptor* TransferQueue::Dequeue() { 109 if (queue_.is_empty()) 110 return nullptr; 111 return queue_.pop_back(); 112} 113 114bool TransferQueue::IsEmpty() const { 115 return queue_.is_empty(); 116} 117 118ConsoleDevice::ConsoleDevice(zx_device_t* bus_device, zx::bti bti, fbl::unique_ptr<Backend> backend) 119 : Device(bus_device, fbl::move(bti), fbl::move(backend)) {} 120 121ConsoleDevice::~ConsoleDevice() {} 122 123// We don't need to hold request_lock_ during initialization 124zx_status_t ConsoleDevice::Init() TA_NO_THREAD_SAFETY_ANALYSIS { 125 LTRACE_ENTRY; 126 // It's a common part for all virtio devices: reset the device, notify 127 // about the driver and negotiate supported features 128 DeviceReset(); 129 DriverStatusAck(); 130 if (!DeviceFeatureSupported(VIRTIO_F_VERSION_1)) { 131 zxlogf(ERROR, "%s: Legacy virtio interface is not supported by this driver\n", tag()); 132 return ZX_ERR_NOT_SUPPORTED; 133 } 134 DriverFeatureAck(VIRTIO_F_VERSION_1); 135 136 zx_status_t status = DeviceStatusFeaturesOk(); 137 if (status) { 138 zxlogf(ERROR, "%s: Feature negotiation failed (%d)\n", tag(), status); 139 return status; 140 } 141 142 status = port0_receive_queue_.Init(0, kDescriptors); 143 if (status) { 144 zxlogf(ERROR, "%s: Failed to initialize receive queue (%d)\n", tag(), status); 145 return status; 146 } 147 148 status = port0_receive_buffer_.Init(bti_, kDescriptors, kChunkSize); 149 if (status) { 150 zxlogf(ERROR, "%s: Failed to allocate buffers for receive queue (%d)\n", tag(), status); 151 return status; 152 } 153 154 // Initially the whole receive buffer is available for device to write, so 155 // put all descriptors in the virtio ring available list 156 for (size_t i = 0; i < kDescriptors; ++i) { 157 TransferDescriptor* desc = port0_receive_buffer_.GetDescriptor(i); 158 QueueTransfer(&port0_receive_queue_, desc->phys, desc->total_len, /*write*/ 0); 159 } 160 // Notify the device 161 port0_receive_queue_.Kick(); 162 163 status = port0_transmit_queue_.Init(1, kDescriptors); 164 if (status) { 165 zxlogf(ERROR, "%s: Failed to initialize transmit queue (%d)\n", tag(), status); 166 return status; 167 } 168 169 status = port0_transmit_buffer_.Init(bti_, kDescriptors, kChunkSize); 170 if (status) { 171 zxlogf(ERROR, "%s: Failed to allocate buffers for transmit queue (%d)\n", tag(), status); 172 return status; 173 } 174 175 // Initially the whole transmit buffer available for writing, so put all the 176 // descriptors in the queue 177 for (size_t i = 0; i < kDescriptors; ++i) { 178 TransferDescriptor* desc = port0_transmit_buffer_.GetDescriptor(i); 179 port0_transmit_descriptors_.Add(desc); 180 } 181 182 device_ops_.read = virtio_console_read; 183 device_ops_.write = virtio_console_write; 184 185 device_add_args_t args = {}; 186 args.version = DEVICE_ADD_ARGS_VERSION; 187 args.name = "virtio-console"; 188 args.ctx = this; 189 args.ops = &device_ops_; 190 191 // We probably want to have an alias for console devices 192 args.proto_id = ZX_PROTOCOL_CONSOLE; 193 194 status = device_add(bus_device_, &args, &device_); 195 if (status) { 196 zxlogf(ERROR, "%s: Failed to register device (%d)\n", tag(), status); 197 device_ = nullptr; 198 return status; 199 } 200 201 StartIrqThread(); 202 DriverStatusOk(); 203 204 LTRACE_EXIT; 205 return ZX_OK; 206} 207 208void ConsoleDevice::IrqRingUpdate() { 209 LTRACE_ENTRY; 210 211 fbl::AutoLock a(&request_lock_); 212 213 // These callbacks are called synchronously, so we don't need to acquire request_lock_ 214 port0_receive_queue_.IrqRingUpdate([this](vring_used_elem* elem) TA_NO_THREAD_SAFETY_ANALYSIS { 215 uint16_t index = static_cast<uint16_t>(elem->id); 216 vring_desc* desc = port0_receive_queue_.DescFromIndex(index); 217 uint32_t remain = elem->len; 218 219 for (;;) { 220 bool has_next = desc->flags & VRING_DESC_F_NEXT; 221 uint16_t next = desc->next; 222 223 TransferDescriptor* trans = port0_receive_buffer_.PhysicalToDescriptor(desc->addr); 224 225 trans->processed_len = 0; 226 trans->used_len = fbl::min(trans->total_len, remain); 227 remain -= trans->used_len; 228 port0_receive_descriptors_.Add(trans); 229 230 port0_receive_queue_.FreeDesc(index); 231 if (!has_next) 232 break; 233 234 index = next; 235 desc = port0_receive_queue_.DescFromIndex(index); 236 } 237 device_state_set(device_, DEV_STATE_READABLE); 238 }); 239 240 port0_transmit_queue_.IrqRingUpdate([this](vring_used_elem* elem) TA_NO_THREAD_SAFETY_ANALYSIS { 241 uint16_t index = static_cast<uint16_t>(elem->id); 242 vring_desc* desc = port0_transmit_queue_.DescFromIndex(index); 243 244 for (;;) { 245 bool has_next = desc->flags & VRING_DESC_F_NEXT; 246 uint16_t next = desc->next; 247 248 TransferDescriptor* trans = port0_transmit_buffer_.PhysicalToDescriptor(desc->addr); 249 250 port0_transmit_descriptors_.Add(trans); 251 252 port0_transmit_queue_.FreeDesc(index); 253 if (!has_next) 254 break; 255 256 index = next; 257 desc = port0_transmit_queue_.DescFromIndex(index); 258 } 259 device_state_set(device_, DEV_STATE_WRITABLE); 260 }); 261 LTRACE_EXIT; 262} 263 264zx_status_t ConsoleDevice::virtio_console_read(void* ctx, void* buf, size_t count, zx_off_t off, size_t* actual) { 265 ConsoleDevice* console = reinterpret_cast<ConsoleDevice*>(ctx); 266 267 return console->Read(buf, count, off, actual); 268} 269 270zx_status_t ConsoleDevice::Read(void* buf, size_t count, zx_off_t off, size_t* actual) { 271 LTRACE_ENTRY; 272 *actual = 0; 273 274 if (count > UINT32_MAX) 275 count = UINT32_MAX; 276 277 fbl::AutoLock a(&request_lock_); 278 279 TransferDescriptor* desc = port0_receive_descriptors_.Peek(); 280 if (!desc) { 281 device_state_clr(device_, DEV_STATE_READABLE); 282 return ZX_ERR_SHOULD_WAIT; 283 } 284 285 uint32_t len = fbl::min(static_cast<uint32_t>(count), desc->used_len - desc->processed_len); 286 memcpy(buf, desc->virt + desc->processed_len, len); 287 desc->processed_len += len; 288 *actual += len; 289 290 // Did we read the whole buffer? If so return it back to the device 291 if (desc->processed_len == desc->used_len) { 292 port0_receive_descriptors_.Dequeue(); 293 QueueTransfer(&port0_receive_queue_, desc->phys, desc->total_len, /*write*/ 0); 294 port0_receive_queue_.Kick(); 295 } 296 297 LTRACE_EXIT; 298 return ZX_OK; 299} 300 301zx_status_t ConsoleDevice::virtio_console_write(void* ctx, const void* buf, size_t count, zx_off_t off, size_t* actual) { 302 ConsoleDevice* console = reinterpret_cast<ConsoleDevice*>(ctx); 303 304 return console->Write(buf, count, off, actual); 305} 306 307zx_status_t ConsoleDevice::Write(const void* buf, size_t count, zx_off_t off, size_t* actual) { 308 LTRACE_ENTRY; 309 *actual = 0; 310 311 if (count > UINT32_MAX) 312 count = UINT32_MAX; 313 314 fbl::AutoLock a(&request_lock_); 315 316 TransferDescriptor* desc = port0_transmit_descriptors_.Dequeue(); 317 if (!desc) { 318 device_state_clr(device_, DEV_STATE_WRITABLE); 319 return ZX_ERR_SHOULD_WAIT; 320 } 321 322 uint32_t len = fbl::min(static_cast<uint32_t>(count), desc->total_len); 323 memcpy(desc->virt, buf, len); 324 desc->used_len = len; 325 *actual += len; 326 327 QueueTransfer(&port0_transmit_queue_, desc->phys, desc->used_len, /*write*/ 1); 328 port0_transmit_queue_.Kick(); 329 330 LTRACE_EXIT; 331 return ZX_OK; 332} 333 334} // namespace virtio 335