Comparar commits
1 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 24e19304f7 |
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 +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++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário