Comparar commits

...

1 Commits

Autor SHA1 Mensagem Data
Travis Geiselbrecht 24e19304f7 WIP virtio console
Change-Id: Ic59b1d8b508b6693bf22f5d63dd52c322c1e4f1a
2017-06-15 23:33:28 -07:00
10 arquivos alterados com 798 adições e 13 exclusões
+8 -4
Ver Arquivo
@@ -276,10 +276,14 @@ fi
# start a few extra harmless virtio devices that can be ignored
if (( $VIRTIO )); then
ARGS+=" -device virtio-serial-pci"
ARGS+=" -device virtio-rng-pci"
ARGS+=" -device virtio-mouse-pci"
ARGS+=" -device virtio-keyboard-pci"
ARGS+=" -device virtio-serial-pci,id=ser0,disable-legacy=on"
ARGS+=" -device virtconsole,bus=ser0.0,chardev=ser0,name=serial"
ARGS+=" -chardev socket,path=/tmp/ser0,server,nowait,id=ser0"
ARGS+=" -device virtconsole,bus=ser0.0,chardev=ser1,name=serial2"
ARGS+=" -chardev socket,path=/tmp/ser1,server,nowait,id=ser1"
#ARGS+=" -device virtio-rng-pci"
#ARGS+=" -device virtio-mouse-pci"
#ARGS+=" -device virtio-keyboard-pci"
fi
case $ARCH in
+495
Ver Arquivo
@@ -0,0 +1,495 @@
// Copyright 2016 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 "console.h"
#include <ddk/protocol/block.h>
#include <inttypes.h>
#include <magenta/compiler.h>
#include <mxtl/algorithm.h>
#include <mxtl/auto_lock.h>
#include <pretty/hexdump.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include "trace.h"
#include "utils.h"
#define LOCAL_TRACE 0
// clang-format off
#define VIRTIO_CONSOLE_F_SIZE (1<<0)
#define VIRTIO_CONSOLE_F_MULTIPORT (1<<1)
#define VIRTIO_CONSOLE_F_EMERG_WRITE (1<<2)
#define VIRTIO_CONSOLE_DEVICE_READY 0
#define VIRTIO_CONSOLE_DEVICE_ADD 1
#define VIRTIO_CONSOLE_DEVICE_REMOVE 2
#define VIRTIO_CONSOLE_PORT_READY 3
#define VIRTIO_CONSOLE_CONSOLE_PORT 4
#define VIRTIO_CONSOLE_RESIZE 5
#define VIRTIO_CONSOLE_PORT_OPEN 6
#define VIRTIO_CONSOLE_PORT_NAME 7
struct virtio_console_control {
uint32_t id;
uint16_t event;
uint16_t value;
};
// clang-format on
namespace virtio {
static mx_status_t QueueTransfer(Ring *ring, mx_paddr_t pa, uint32_t len, bool write) {
uint16_t i;
auto desc = ring->AllocDescChain(1, &i);
if (!desc) {
return ERR_NO_MEMORY;
}
desc->addr = pa;
desc->len = len;
desc->flags = write ? 0 : VRING_DESC_F_WRITE;
#if LOCAL_TRACE > 0
virtio_dump_desc(desc);
#endif
/* submit the transfer */
ring->SubmitChain(i);
/* kick it off */
ring->Kick();
return NO_ERROR;
}
static mx_status_t QueueRxTransfer(Ring *ring, TransferBuffer *tb) {
tb->used_len = 0;
tb->processed_len = 0;
assert(tb->total_len <= UINT32_MAX);
return QueueTransfer(ring, tb->pa, (uint32_t)tb->total_len, false);
}
static mx_status_t QueueTxTransfer(Ring *ring, TransferBuffer *tb) {
tb->processed_len = 0;
assert(tb->used_len <= UINT32_MAX);
assert(tb->used_len <= tb->total_len);
return QueueTransfer(ring, tb->pa, (uint32_t)tb->used_len, true);
}
// DDK level ops
mx_status_t ConsoleDevice::virtio_console_read(void* ctx, void* buf, size_t count, mx_off_t off, size_t* actual) {
LTRACEF("ctx %p count %zu off %lu\n", ctx, count, off);
Port *p = (Port*)ctx;
ConsoleDevice *c = p->console_device;
return c->Read(p, buf, count, off, actual);
}
mx_status_t ConsoleDevice::virtio_console_write(void* ctx, const void* buf, size_t count, mx_off_t off, size_t* actual) {
LTRACEF("ctx %p count %zu off %lu\n", ctx, count, off);
Port *p = (Port*)ctx;
ConsoleDevice *c = p->console_device;
return c->Write(p, buf, count, off, actual);
}
mx_status_t ConsoleDevice::Read(Port *p, void* buf, size_t count, mx_off_t off, size_t* actual) {
LTRACEF("port %p count %zu off %lu\n", p, count, off);
*actual = 0;
mxtl::AutoLock a(&request_lock_);
// see if we have any queued up data
TransferBuffer *tb = p->rx_queue.PeekHead();
if (!tb) {
device_state_clr(p->device, DEV_STATE_READABLE);
return ERR_SHOULD_WAIT;
}
size_t len = mxtl::min(count, tb->used_len - tb->processed_len);
memcpy(buf, tb->ptr + tb->processed_len, len);
tb->processed_len += len;
*actual += len;
// if this completes the transfer, requeue it
if (tb->processed_len == tb->used_len) {
auto tb2 = p->rx_queue.Dequeue();
assert(tb == tb2);
QueueRxTransfer(p->rx_ring.get(), tb);
}
LTRACEF("retuning with actual count %zu\n", *actual);
return NO_ERROR;
}
mx_status_t ConsoleDevice::Write(Port *p, const void* buf, size_t count, mx_off_t off, size_t* actual) {
LTRACEF("port %p count %zu off %lu\n", p, count, off);
*actual = 0;
mxtl::AutoLock a(&request_lock_);
// pop a transfer buffer off the tx queue, fill it with data and queue it
auto tb = p->tx_queue.Dequeue();
if (!tb) {
// we're out of buffers, the other side must not be listening
device_state_clr(p->device, DEV_STATE_WRITABLE);
return ERR_SHOULD_WAIT;
}
// build a packet to transfer the data
size_t len = mxtl::min(count, tb->total_len);
memcpy(tb->ptr, buf, len);
*actual += len;
tb->used_len = len;
// queue it
QueueTxTransfer(p->tx_ring.get(), tb);
return NO_ERROR;
}
// console device
ConsoleDevice::ConsoleDevice(mx_device_t* bus_device)
: Device(bus_device) {
// so that Bind() knows how much io space to allocate
bar0_size_ = 0x40;
}
ConsoleDevice::~ConsoleDevice() {
}
int ConsoleDevice::virtio_console_start_entry(void* arg) {
ConsoleDevice* c = static_cast<ConsoleDevice*>(arg);
c->virtio_console_start();
return 0;
}
mx_status_t ConsoleDevice::virtio_console_start() {
mxtl::AutoLock a(&request_lock_);
// queue up all transfers on the control port
for (size_t i = 0; i < control_ring_size; i++) {
TransferBuffer *tb = control_rx_buffers_.GetBuffer(i);
QueueTransfer(&control_rx_vring_, tb->pa, port_buffer_size, false);
}
// tell the device we're ready to talk
virtio_console_control control = {};
control.event = VIRTIO_CONSOLE_DEVICE_READY;
control.value = 1;
TransferBuffer *tb = control_tx_buffers_.GetBuffer(next_control_tx_buffer_);
memcpy(tb->ptr, &control, sizeof(control));
QueueTransfer(&control_tx_vring_, tb->pa, sizeof(control), true);
return NO_ERROR;
}
mx_status_t ConsoleDevice::Init() {
LTRACE_ENTRY;
// reset the device
Reset();
// read our configuration
CopyDeviceConfig(&config_, sizeof(config_));
LTRACEF("cols %hu\n", config_.cols);
LTRACEF("rows %hu\n", config_.rows);
LTRACEF("max_ports %u\n", config_.max_ports);
// ack and set the driver status bit
StatusAcknowledgeDriver();
// XXX check features bits and ack/nak them
// allocate the control vrings
mx_status_t err = control_rx_vring_.Init(2, control_ring_size);
if (err < 0) {
VIRTIO_ERROR("failed to allocate rx control ring\n");
return err;
}
err = control_tx_vring_.Init(3, control_ring_size);
if (err < 0) {
VIRTIO_ERROR("failed to allocate tx control ring\n");
return err;
}
control_rx_buffers_.Init(control_ring_size, control_buffer_size);
control_tx_buffers_.Init(control_ring_size, control_buffer_size);
// start the interrupt thread
StartIrqThread();
// set DRIVER_OK
StatusDriverOK();
// add the root device under /dev/class/console/virtiocon
// point the ctx of our DDK device at ourself
device_add_args_t args = {};
args.version = DEVICE_ADD_ARGS_VERSION;
args.name = "virtiocon";
args.ctx = this;
args.ops = &device_ops_;
args.proto_id = MX_PROTOCOL_CONSOLE;
auto status = device_add(bus_device_, &args, &device_);
if (status < 0) {
VIRTIO_ERROR("failed device add %d\n", status);
device_ = nullptr;
return status;
}
// start a worker thread that runs through a sequence to finish initializing the console
thrd_create_with_name(&start_thread_, virtio_console_start_entry, this, "virtio-console-starter");
thrd_detach(start_thread_);
return NO_ERROR;
}
// return the descriptor chain to the ring, returning the physical address that was used
static mx_paddr_t complete_transfer(Ring *ring, vring_used_elem *elem) {
uint32_t i = (uint16_t)elem->id;
vring_desc* desc = ring->DescFromIndex((uint16_t)i);
mx_paddr_t pa = desc->addr;
for (;;) {
int next;
#if LOCAL_TRACE > 0
virtio_dump_desc(desc);
#endif
if (desc->flags & VRING_DESC_F_NEXT) {
next = desc->next;
} else {
/* end of chain */
next = -1;
}
ring->FreeDesc((uint16_t)i);
if (next < 0)
break;
i = next;
desc = ring->DescFromIndex((uint16_t)i);
}
return pa;
}
void ConsoleDevice::HandleControlMessage(TransferBuffer *tb) {
virtio_console_control *rx_message = (virtio_console_control *)tb->ptr;
assert(rx_message);
bool send_response = false;
virtio_console_control response = {};
switch (rx_message->event) {
case VIRTIO_CONSOLE_DEVICE_ADD: {
LTRACEF("CONSOLE_DEVICE_ADD: port %u\n", rx_message->id);
response.event = VIRTIO_CONSOLE_PORT_READY;
response.value = 1;
response.id = rx_message->id;
send_response = true;
if (port_[rx_message->id].active) {
TRACEF("CONSOLE_DEVICE_ADD: asked to add port %u which is already active\n", rx_message->id);
break;
}
uint16_t ring_index = (rx_message->id == 0) ? 0 : (uint16_t)((rx_message->id + 1u) * 2u);
LTRACEF("port %u ring index is %hu\n", rx_message->id, ring_index);
port_[rx_message->id].Init(this, ring_index);
char name[128];
snprintf(name, sizeof(name), "virtiocon-%u", rx_message->id);
device_add_args_t args = {};
args.version = DEVICE_ADD_ARGS_VERSION;
args.name = name;
args.ctx = &port_[rx_message->id]; // pass a pointer to the port, not the overall device
args.ops = &port_[rx_message->id].device_ops;
args.proto_id = MX_PROTOCOL_CONSOLE;
mx_status_t status = device_add(device_, &args, &port_[rx_message->id].device);
if (status < 0) {
VIRTIO_ERROR("failed device add %d\n", status);
}
// queue up all the packets on the rx side of the port
for (size_t i = 0; i < port_ring_size; i++) {
TransferBuffer *tb = port_[rx_message->id].rx_buffer.GetBuffer(i);
QueueRxTransfer(port_[rx_message->id].rx_ring.get(), tb);
}
break;
}
case VIRTIO_CONSOLE_CONSOLE_PORT:
LTRACEF("CONSOLE_CONSOLE_PORT: port %u\n", rx_message->id);
response.event = VIRTIO_CONSOLE_PORT_OPEN;
response.value = 1;
response.id = rx_message->id;
send_response = true;
break;
default:
TRACEF("unhandled console control message %u\n", rx_message->event);
hexdump(rx_message, tb->used_len);
}
if (send_response) {
TransferBuffer *tb = control_tx_buffers_.GetBuffer(next_control_tx_buffer_++);
memcpy(tb->ptr, &response, sizeof(response));
QueueTransfer(&control_tx_vring_, tb->pa, sizeof(virtio_console_control), true);
}
}
void ConsoleDevice::IrqRingUpdate() {
LTRACE_ENTRY;
mxtl::AutoLock a(&request_lock_);
// handle console tx ring transfers
auto handle_console_tx_ring = [this](vring_used_elem* used_elem) {
LTRACEF("console tx used_elem %p\n", used_elem);
complete_transfer(&control_tx_vring_, used_elem);
};
control_tx_vring_.IrqRingUpdate(handle_console_tx_ring);
// handle port tx ring transfers
for (auto &port: port_) {
if (port.active) {
auto handle_port_tx_ring = [this, &port](vring_used_elem* used_elem) {
LTRACEF("port tx used_elem %p\n", used_elem);
mx_paddr_t pa = complete_transfer(port.tx_ring.get(), used_elem);
// get the transfer buffer for this and return it to the tx queue
TransferBuffer *tb = port.tx_buffer.PhysicalToTransfer(pa);
assert(tb);
LTRACEF("returning tx transfer %p on port %p\n", tb, &port);
port.tx_queue.Add(tb);
// we have at least one packet ready to be filled in so we're WRITABLE now
device_state_set(port.device, DEV_STATE_WRITABLE);
};
port.tx_ring->IrqRingUpdate(handle_port_tx_ring);
}
}
// handle console rx ring transfers
auto handle_console_rx_ring = [this](vring_used_elem* used_elem) {
LTRACEF("console_rx used_elem %p\n", used_elem);
mx_paddr_t pa = complete_transfer(&control_rx_vring_, used_elem);
LTRACEF("control rx len %u\n", used_elem->len);
TransferBuffer *tb = control_rx_buffers_.PhysicalToTransfer(pa);
assert(tb);
tb->used_len = used_elem->len;
tb->processed_len = 0;
HandleControlMessage(tb);
// queue the packet again
QueueTransfer(&control_rx_vring_, pa, port_buffer_size, false);
};
control_rx_vring_.IrqRingUpdate(handle_console_rx_ring);
// handle port rx ring transfers
for (auto &port: port_) {
if (port.active) {
auto handle_port_rx_ring = [this, &port](vring_used_elem* used_elem) {
LTRACEF("port rx used_elem %p %u\n", used_elem, used_elem->id);
mx_paddr_t pa = complete_transfer(port.rx_ring.get(), used_elem);
LTRACEF("port rx pa %#lx len %u\n", pa, used_elem->len);
// take the incoming port data and stuff in the rx transfer queue
TransferBuffer *tb = port.rx_buffer.PhysicalToTransfer(pa);
// queue the rx descriptor again
assert(tb);
// queue the received data
bool queue_was_empty = port.rx_queue.IsEmpty();
tb->used_len = used_elem->len;
tb->processed_len = 0;
LTRACEF("queuing transfer %p on port %p\n", tb, &port);
port.rx_queue.Add(tb);
// if we're putting the first thing in the queue, mark the device readable
if (queue_was_empty) {
device_state_set(port.device, DEV_STATE_READABLE);
}
};
port.rx_ring->IrqRingUpdate(handle_port_rx_ring);
}
}
LTRACE_EXIT;
}
void ConsoleDevice::IrqConfigChange() {
LTRACE_ENTRY;
}
ConsoleDevice::Port::Port() {}
ConsoleDevice::Port::~Port() {}
mx_status_t ConsoleDevice::Port::Init(ConsoleDevice *dev, uint16_t ring_index) {
if (active)
return NO_ERROR;
rx_ring.reset(new Ring(dev));
tx_ring.reset(new Ring(dev));
mx_status_t err = rx_ring->Init(ring_index, port_ring_size);
if (err < 0) {
VIRTIO_ERROR("failed to allocate port rx ring\n");
return err;
}
err = tx_ring->Init((uint16_t)(ring_index + 1), port_ring_size);
if (err < 0) {
VIRTIO_ERROR("failed to allocate port tx ring\n");
return err;
}
rx_buffer.Init(port_ring_size, port_buffer_size);
tx_buffer.Init(port_ring_size, port_buffer_size);
// rx queue starts off empty
// tx queue starts off with all the transfer buffers queued in it
for (size_t i = 0; i < port_ring_size; i++) {
tx_queue.Add(tx_buffer.GetBuffer(i));
}
device_ops = {};
device_ops.version = DEVICE_OPS_VERSION;
device_ops.read = virtio_console_read;
device_ops.write = virtio_console_write;
console_device = dev;
active = true;
return NO_ERROR;
}
} // namespace virtio
+101
Ver Arquivo
@@ -0,0 +1,101 @@
// Copyright 2016 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 "device.h"
#include "ring.h"
#include <magenta/compiler.h>
#include <mxtl/unique_ptr.h>
#include <stdlib.h>
#include "transfer_buffer_list.h"
namespace virtio {
class Ring;
class ConsoleDevice : public Device {
public:
ConsoleDevice(mx_device_t* device);
virtual ~ConsoleDevice();
virtual mx_status_t Init();
virtual void IrqRingUpdate();
virtual void IrqConfigChange();
private:
static mx_status_t virtio_console_read(void* ctx, void* buf, size_t count, mx_off_t off, size_t* actual);
static mx_status_t virtio_console_write(void* ctx, const void* buf, size_t count, mx_off_t off, size_t* actual);
struct Port;
mx_status_t Read(Port *p, void* buf, size_t count, mx_off_t off, size_t* actual);
mx_status_t Write(Port *p, const void* buf, size_t count, mx_off_t off, size_t* actual);
void HandleControlMessage(TransferBuffer *tb);
mx_status_t virtio_console_start();
static int virtio_console_start_entry(void* arg);
thrd_t start_thread_ = {};
// request condition
mxtl::Mutex request_lock_;
cnd_t request_cond_ = {};
// control tx/rx rings
Ring control_rx_vring_ = {this};
Ring control_tx_vring_ = {this};
// per port tracking data
struct Port {
Port();
~Port();
DISALLOW_COPY_ASSIGN_AND_MOVE(Port);
mx_status_t Init(ConsoleDevice *dev, uint16_t ring_index);
// members
mxtl::unique_ptr<Ring> rx_ring;
mxtl::unique_ptr<Ring> tx_ring;
TransferBufferList rx_buffer;
TransferBufferList tx_buffer;
TransferBufferQueue rx_queue;
TransferBufferQueue tx_queue;
mx_device_t *device = nullptr;
mx_protocol_device_t device_ops = {};
bool active = false;
ConsoleDevice *console_device = nullptr;
};
// there can be up to 32 ports per device
Port port_[32];
// saved block device configuration out of the pci config BAR
struct virtio_console_config {
uint16_t cols;
uint16_t rows;
uint32_t max_ports;
uint32_t emerg_wr;
} config_ __PACKED = {};
static const size_t control_buffer_size = 128;
static const size_t control_ring_size = 32;
static const size_t port_buffer_size = 512;
static const size_t port_ring_size = 128;
// transfer buffers for control rx and tx
TransferBufferList control_rx_buffers_;
TransferBufferList control_tx_buffers_;
uint32_t next_control_tx_buffer_ = 0;
// pending iotxns
list_node iotxn_list = LIST_INITIAL_VALUE(iotxn_list);
};
} // namespace virtio
+4 -2
Ver Arquivo
@@ -145,8 +145,9 @@ mx_status_t Device::Bind(pci_protocol_t* pci,
case VIRTIO_PCI_CAP_NOTIFY_CFG: {
MapBar(cap->bar);
mmio_regs_.notify_base = (volatile uint16_t*)((uintptr_t)bar_[cap->bar].mmio_base + cap->offset);
mmio_regs_.notify_mul = 0x1000;
LTRACEF("notify_base %p\n", mmio_regs_.notify_base);
uint32_t *multiplier = (uint32_t *)(cap + 1);
mmio_regs_.notify_mul = *multiplier;
LTRACEF("notify_base %p, multiplier %u\n", mmio_regs_.notify_base, mmio_regs_.notify_mul);
break;
}
case VIRTIO_PCI_CAP_ISR_CFG: {
@@ -229,6 +230,7 @@ void Device::IrqWorker() {
auto status = mx_interrupt_wait(irq_handle_.get());
if (status < 0) {
printf("virtio: error %d waiting for interrupt\n", status);
assert(0);
continue;
}
+8 -6
Ver Arquivo
@@ -8,6 +8,8 @@
#include "trace.h"
#include <assert.h>
namespace virtio {
class Device;
@@ -48,21 +50,21 @@ private:
// perform the main loop of finding free descriptor chains and passing it to a passed in function
template <typename T>
inline void Ring::IrqRingUpdate(T free_chain) {
//TRACEF("used flags 0x%hhx idx 0x%hhx last_used %u\n",
//TRACEF("used flags %#hx idx %#hx last_used %#hx\n",
// ring_.used->flags, ring_.used->idx, ring_.last_used);
// find a new free chain of descriptors
unsigned int cur_idx = ring_.used->idx;
for (unsigned int i = ring_.last_used; i != (cur_idx & ring_.num_mask); i = (i + 1) & ring_.num_mask) {
//TRACEF("looking at idx %u\n", i);
uint16_t cur_idx = ring_.used->idx;
for (uint16_t i = ring_.last_used; i != cur_idx; i++) {
//TRACEF("looking at idx %#x (%#x), cur_idx %#x\n", i, i & ring_.num_mask, cur_idx);
struct vring_used_elem* used_elem = &ring_.used->ring[i];
struct vring_used_elem* used_elem = &ring_.used->ring[i & ring_.num_mask];
//TRACEF("used chain id %u, len %u\n", used_elem->id, used_elem->len);
// free the chain
free_chain(used_elem);
ring_.last_used = ((ring_.last_used + 1) & ring_.num_mask) & 0xffff;
ring_.last_used++;
}
}
+2
Ver Arquivo
@@ -10,10 +10,12 @@ MODULE_TYPE := driver
MODULE_SRCS := \
$(LOCAL_DIR)/block.cpp \
$(LOCAL_DIR)/console.cpp \
$(LOCAL_DIR)/device.cpp \
$(LOCAL_DIR)/gpu.cpp \
$(LOCAL_DIR)/ring.cpp \
$(LOCAL_DIR)/utils.cpp \
$(LOCAL_DIR)/transfer_buffer_list.cpp \
$(LOCAL_DIR)/virtio_c.c \
$(LOCAL_DIR)/virtio_driver.cpp \
@@ -0,0 +1,81 @@
// 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 "transfer_buffer_list.h"
#include <magenta/errors.h>
#include <mx/vmar.h>
#include "utils.h"
#include "trace.h"
#define LOCAL_TRACE 0
namespace virtio {
TransferBufferList::TransferBufferList() {
}
mx_status_t TransferBufferList::Init(size_t count, size_t buffer_size) {
assert(count_ == 0);
count_ = count;
buffer_size_ = buffer_size;
size_ = count_ * buffer_size_;
// allocate a buffer large enough to be able to be carved up into count_
// buffers of buffer_size_
mx_status_t err = map_contiguous_memory(size_, (uintptr_t*)&buffer_, &buffer_pa_);
if (err < 0) {
VIRTIO_ERROR("cannot alloc buffers %d\n", err);
return err;
}
// build a per buffer descriptor object
auto tb = new TransferBuffer[count_];
buffers_.reset(tb, count_);
for (size_t i = 0; i < count_; i++) {
buffers_[i].ptr = buffer_ + i * buffer_size;
buffers_[i].pa = buffer_pa_ + i * buffer_size;
buffers_[i].total_len = buffer_size;
buffers_[i].used_len = 0;
buffers_[i].processed_len = 0;
}
return NO_ERROR;
}
TransferBufferList::~TransferBufferList() {
if (buffer_) {
mx::vmar::root_self().unmap((uintptr_t)buffer_, size_);
}
delete[] buffers_.get();
buffers_.release();
}
TransferBuffer* TransferBufferList::GetBuffer(size_t index) {
if (index > count_) {
assert(0);
return nullptr;
}
return &buffers_[index];
}
TransferBuffer* TransferBufferList::PhysicalToTransfer(mx_paddr_t pa) {
if (pa < buffer_pa_ || pa >= buffer_pa_ + size_) {
assert(0);
return nullptr;
}
size_t index = (pa - buffer_pa_) / buffer_size_;
assert(index < count_);
LTRACEF("pa %#lx buffer_pa %#lx index %zu\n", pa, buffer_pa_, index);
return &buffers_[index];
}
} // namespace virtio
+90
Ver Arquivo
@@ -0,0 +1,90 @@
// 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 <magenta/compiler.h>
#include <magenta/types.h>
#include <mxtl/array.h>
#include <mxtl/macros.h>
#include <mxtl/intrusive_double_list.h>
#include <sys/types.h>
#include "trace.h"
namespace virtio {
struct TransferBuffer : mxtl::DoublyLinkedListable<TransferBuffer *> {
unsigned int index;
uint8_t* ptr;
mx_paddr_t pa;
size_t total_len;
// modified as transfers are queued and moved around
size_t used_len;
size_t processed_len;
};
class TransferBufferList {
public:
TransferBufferList();
~TransferBufferList();
mx_status_t Init(size_t count, size_t buffer_size);
TransferBuffer* GetBuffer(size_t index);
// look up the corresponding transfer based on the physical address
TransferBuffer* PhysicalToTransfer(mx_paddr_t pa);
private:
DISALLOW_COPY_ASSIGN_AND_MOVE(TransferBufferList);
size_t count_ = 0;
size_t buffer_size_ = 0;
size_t size_ = 0;
uint8_t* buffer_ = nullptr;
mx_paddr_t buffer_pa_ = 0;
mxtl::Array<TransferBuffer> buffers_;
};
class TransferBufferQueue {
public:
TransferBufferQueue() {}
~TransferBufferQueue() {}
void Add(TransferBuffer *tb) {
queue_.push_front(tb);
count_++;
}
TransferBuffer* PeekHead() {
if (queue_.is_empty())
return nullptr;
return &queue_.back();
}
TransferBuffer* Dequeue() {
if (queue_.is_empty())
return nullptr;
count_--;
return queue_.pop_back();
}
bool IsEmpty() {
return queue_.is_empty();
}
private:
DISALLOW_COPY_ASSIGN_AND_MOVE(TransferBufferQueue);
mxtl::DoublyLinkedList<TransferBuffer*> queue_;
size_t count_ = 0;
};
} // namespace virtio
+3 -1
Ver Arquivo
@@ -20,12 +20,14 @@ static mx_driver_ops_t virtio_driver_ops = {
.bind = virtio_bind,
};
MAGENTA_DRIVER_BEGIN(virtio, virtio_driver_ops, "magenta", "0.1", 6)
MAGENTA_DRIVER_BEGIN(virtio, virtio_driver_ops, "magenta", "0.1", 8)
BI_ABORT_IF(NE, BIND_PROTOCOL, MX_PROTOCOL_PCI),
BI_ABORT_IF(NE, BIND_PCI_VID, 0x1af4),
BI_MATCH_IF(EQ, BIND_PCI_DID, 0x1001), // Block device (transitional)
BI_MATCH_IF(EQ, BIND_PCI_DID, 0x1042), // Block device
BI_MATCH_IF(EQ, BIND_PCI_DID, 0x1050), // GPU device
BI_MATCH_IF(EQ, BIND_PCI_DID, 0x1003), // Console device
BI_MATCH_IF(EQ, BIND_PCI_DID, 0x1043), // Console device
//BI_MATCH_IF(EQ, BIND_PCI_DID, 0x1000), // Network device (transitional)
BI_ABORT(),
MAGENTA_DRIVER_END(virtio)
+6
Ver Arquivo
@@ -18,6 +18,7 @@
#include <magenta/types.h>
#include "block.h"
#include "console.h"
#include "device.h"
#include "gpu.h"
#include "trace.h"
@@ -61,6 +62,11 @@ extern "C" mx_status_t virtio_bind(void* ctx, mx_device_t* device, void** cookie
LTRACEF("found gpu device\n");
vd.reset(new virtio::GpuDevice(device));
break;
case 0x1003:
case 0x1043:
TRACEF("found console device\n");
vd.reset(new virtio::ConsoleDevice(device));
break;
default:
printf("unhandled device id, how did this happen?\n");
return -1;