vmm: cleanup virtio model, make it extensible

* Introduce different index types for ring counters and descriptor arrays
  within the Virtio split queue implementation
* Be more accurate in reporting the queue number supported, and raise it
  to 512
* Introduce abstractions for mmio register access, where several values
  are stored at the same place, and selector registers exist.
* Turn Virtio_device into a template to define the Virio queue type,
  and its numbers per device model (needed for e.g., future block model)

Issue #4025
This commit is contained in:
Stefan Kalkowski
2021-02-18 17:56:56 +01:00
committed by Norman Feske
parent 83c2309710
commit 2879aa003b
8 changed files with 361 additions and 550 deletions

View File

@ -51,7 +51,7 @@ void Mmio_register::write(Address_range & access, Cpu & cpu, Register value)
} }
Mmio_register::Register Mmio_register::value() { return _value; } Mmio_register::Register Mmio_register::value() const { return _value; }
void Mmio_register::set(Register value) { _value = value; } void Mmio_register::set(Register value) { _value = value; }

View File

@ -35,8 +35,8 @@ class Vmm::Mmio_register : public Vmm::Address_range
virtual Register read(Address_range & access, Cpu&); virtual Register read(Address_range & access, Cpu&);
virtual void write(Address_range & access, Cpu&, Register value); virtual void write(Address_range & access, Cpu&, Register value);
void set(Register value); virtual void set(Register value);
Register value(); virtual Register value() const;
Mmio_register(Name name, Mmio_register(Name name,
Type type, Type type,

View File

@ -11,7 +11,6 @@ SRC_CC += main.cc
SRC_CC += mmio.cc SRC_CC += mmio.cc
SRC_CC += pl011.cc SRC_CC += pl011.cc
SRC_CC += vm.cc SRC_CC += vm.cc
SRC_CC += virtio_device.cc
INC_DIR += $(PRG_DIR)/../.. $(PRG_DIR) INC_DIR += $(PRG_DIR)/../.. $(PRG_DIR)
vpath %.cc $(PRG_DIR)/../.. vpath %.cc $(PRG_DIR)/../..

View File

@ -10,7 +10,6 @@ SRC_CC += gic.cc
SRC_CC += main.cc SRC_CC += main.cc
SRC_CC += mmio.cc SRC_CC += mmio.cc
SRC_CC += pl011.cc SRC_CC += pl011.cc
SRC_CC += virtio_device.cc
SRC_CC += vm.cc SRC_CC += vm.cc
INC_DIR += $(PRG_DIR)/../.. $(PRG_DIR) INC_DIR += $(PRG_DIR)/../.. $(PRG_DIR)

View File

@ -18,15 +18,15 @@
#include <virtio_device.h> #include <virtio_device.h>
namespace Vmm namespace Vmm { class Virtio_console; }
{
class Virtio_console;
}
class Vmm::Virtio_console : public Virtio_device
class Vmm::Virtio_console : public Virtio_device<Virtio_split_queue, 2>
{ {
private: private:
enum Queue_id { RX, TX };
Terminal::Connection _terminal; Terminal::Connection _terminal;
Cpu::Signal_handler<Virtio_console> _handler; Cpu::Signal_handler<Virtio_console> _handler;
@ -62,7 +62,7 @@ class Vmm::Virtio_console : public Virtio_device
_assert_irq(); _assert_irq();
} }
Register _device_specific_features() override { return 0; } enum Device_id { CONSOLE = 0x3 };
public: public:
@ -74,13 +74,12 @@ class Vmm::Virtio_console : public Virtio_device
Mmio_bus & bus, Mmio_bus & bus,
Ram & ram, Ram & ram,
Genode::Env & env) Genode::Env & env)
: Virtio_device(name, addr, size, irq, cpu, bus, ram), :
Virtio_device<Virtio_split_queue, 2>(name, addr, size,
irq, cpu, bus, ram, CONSOLE),
_terminal(env, "console"), _terminal(env, "console"),
_handler(cpu, env.ep(), *this, &Virtio_console::_read) _handler(cpu, env.ep(), *this, &Virtio_console::_read)
{ {
/* set device ID to console */
_device_id(0x3);
_terminal.read_avail_sigh(_handler); _terminal.read_avail_sigh(_handler);
} }
}; };

View File

@ -1,57 +0,0 @@
/*
* \brief Generic and simple virtio device
* \author Sebastian Sumpf
* \date 2019-10-10
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <cpu.h>
#include <virtio_device.h>
#include <base/log.h>
using Vmm::Virtio_device;
using Register = Vmm::Mmio_register::Register;
Virtio_device::Virtio_device(const char * const name,
const Genode::uint64_t addr,
const Genode::uint64_t size,
unsigned irq,
Cpu &cpu,
Mmio_bus &bus,
Ram &ram,
unsigned queue_size)
: Mmio_device(name, addr, size),
_irq(cpu.gic().irq(irq)),
_ram(ram)
{
for (unsigned i = 0; i < (sizeof(Dummy::regs) / sizeof(Mmio_register)); i++)
add(_reg_container.regs[i]);
add(_device_features);
add(_driver_features);
add(_queue_sel);
add(_queue_ready);
add(_queue_num);
add(_queue_notify);
add(_queue_descr_low);
add(_queue_descr_high);
add(_queue_driver_low);
add(_queue_driver_high);
add(_queue_device_low);
add(_queue_device_high);
add(_interrupt_status);
add(_interrupt_ack);
add(_status);
/* set queue size */
_reg_container.regs[6].set(queue_size);
bus.add(*this);
}

View File

@ -1,6 +1,7 @@
/* /*
* \brief Generic and simple virtio device * \brief Generic and simple virtio device
* \author Sebastian Sumpf * \author Sebastian Sumpf
* \author Stefan Kalkowski
* \date 2019-10-10 * \date 2019-10-10
*/ */
@ -14,6 +15,7 @@
#ifndef _VIRTIO_DEVICE_H_ #ifndef _VIRTIO_DEVICE_H_
#define _VIRTIO_DEVICE_H_ #define _VIRTIO_DEVICE_H_
#include <cpu/memory_barrier.h>
#include <util/mmio.h> #include <util/mmio.h>
#include <util/reconstructible.h> #include <util/reconstructible.h>
@ -22,50 +24,106 @@
#include <mmio.h> #include <mmio.h>
namespace Vmm { namespace Vmm {
class Virtio_device; class Virtio_split_queue;
class Virtio_queue; template <typename QUEUE, unsigned NUM> class Virtio_device;
struct Virtio_queue_data;
class Virtio_descriptor; using namespace Genode;
class Virtio_avail;
class Virtio_used;
} }
using uint16_t = Genode::uint16_t;
using uint32_t = Genode::uint32_t;
using uint64_t = Genode::uint64_t;
using addr_t = Genode::addr_t;
using size_t = Genode::size_t;
struct Vmm::Virtio_queue_data /**
{ * Split queue implementation
uint32_t descr_low { 0 }; */
uint32_t descr_high { 0 }; class Vmm::Virtio_split_queue
uint32_t driver_low { 0 };
uint32_t driver_high { 0 };
uint32_t device_low { 0 };
uint32_t device_high { 0 };
uint32_t num { 0 };
uint32_t ready { 0 };
bool tx { false };
uint64_t descr() const {
return ((uint64_t)descr_high << 32) | descr_low; }
uint64_t driver() const {
return ((uint64_t)driver_high << 32) | driver_low; }
uint64_t device() const {
return ((uint64_t)device_high << 32) | device_low; }
enum { MAX_QUEUE_SIZE = 1 << 15 };
};
class Vmm::Virtio_descriptor : Genode::Mmio
{ {
public: public:
Virtio_descriptor(addr_t base) static constexpr unsigned MAX_SIZE_LOG2 = 9;
: Mmio(base) { } static constexpr unsigned MAX_SIZE = 1 << MAX_SIZE_LOG2;
protected:
template <unsigned LOG2>
class Index
{
private:
unsigned _idx : LOG2;
public:
Index(unsigned idx = 0) : _idx(idx % (1 << LOG2)) {}
void inc() { _idx++; }
unsigned idx() const { return _idx; }
bool operator != (Index const & o) const {
return _idx != o._idx; }
};
using Ring_index = Index<16>;
using Descriptor_index = Index<MAX_SIZE_LOG2>;
struct Queue_base : Mmio
{
unsigned const max;
Queue_base(addr_t base, unsigned max)
: Mmio(base), max(max) {}
struct Flags : Register<0x0, 16> { };
struct Idx : Register<0x2, 16> { };
Ring_index current() { return read<Idx>(); }
};
struct Avail_queue : Queue_base
{
using Queue_base::Queue_base;
struct Ring : Register_array<0x4, 16, MAX_SIZE, 16> { };
bool inject_irq() { return (read<Flags>() & 1) == 0; }
Descriptor_index get(Ring_index id)
{
unsigned v = read<Ring>(id.idx() % max);
if (v >= max) {
throw Exception("Descriptor_index out of bounds"); }
return Descriptor_index(v);
}
} _avail;
struct Used_queue : Queue_base
{
using Queue_base::Queue_base;
struct Ring : Register_array<0x4, 64, MAX_SIZE, 64>
{
struct Id : Bitfield<0, 32> { };
struct Length : Bitfield<32,32> { };
};
void add(Ring_index ri, Descriptor_index di, size_t size)
{
if (di.idx() >= max) {
throw Exception("Descriptor_index out of bounds"); }
write<Flags>(0);
Ring::access_t elem = 0;
Ring::Id::set(elem, di.idx());
Ring::Length::set(elem, size);
write<Ring>(elem, ri.idx() % max);
}
} _used;
struct Descriptor : Mmio
{
using Mmio::Mmio;
struct Address : Register<0x0, 64> { }; struct Address : Register<0x0, 64> { };
struct Length : Register<0x8, 32> { }; struct Length : Register<0x8, 32> { };
@ -79,13 +137,6 @@ class Vmm::Virtio_descriptor : Genode::Mmio
struct Next : Register<0xe, 16> { }; struct Next : Register<0xe, 16> { };
static constexpr size_t size() { return 16; }
Virtio_descriptor index(unsigned idx)
{
return Virtio_descriptor(base() + (size() * idx));
}
uint64_t address() const { return read<Address>(); } uint64_t address() const { return read<Address>(); }
size_t length () const { return read<Length>(); } size_t length () const { return read<Length>(); }
uint16_t flags() const { return read<Flags>(); } uint16_t flags() const { return read<Flags>(); }
@ -93,451 +144,279 @@ class Vmm::Virtio_descriptor : Genode::Mmio
}; };
class Vmm::Virtio_avail : public Genode::Mmio struct Descriptor_array
{ {
public: size_t const elem_size { 16 };
unsigned const max;
addr_t const start;
Virtio_avail(addr_t base) Descriptor_array(Ram & ram, addr_t base, unsigned const max)
: Mmio(base) { }; :
max(max),
start(ram.local_address(base, max * elem_size)) {}
struct Flags : Register<0x0, 16> { }; Descriptor get(Descriptor_index idx)
struct Idx : Register<0x2, 16> { };
struct Ring : Register_array<0x4, 16, Virtio_queue_data::MAX_QUEUE_SIZE, 16> { };
bool inject_irq() { return (read<Flags>() & 1) == 0; }
};
class Vmm::Virtio_used : public Genode::Mmio
{ {
public: if (idx.idx() >= max) error("Descriptor_index out of bounds");
return Descriptor(start + (elem_size * idx.idx()));
}
} _descriptors;
Virtio_used(addr_t base)
: Mmio(base) { };
struct Flags : Register<0x0, 16> { };
struct Idx : Register<0x2, 16> { };
struct Elem : Register_array<0x4, 64, Virtio_queue_data::MAX_QUEUE_SIZE, 64>
{
struct Id : Bitfield<0, 32> { };
struct Length : Bitfield<32,32> { };
};
};
/**
* Split queue implementation
*/
class Vmm::Virtio_queue
{
private:
Virtio_queue_data &_data;
Ram & _ram; Ram & _ram;
Ring_index _cur_idx;
Virtio_descriptor _descr { _ram.local_address(_data.descr(),
Virtio_descriptor::size() * _data.num ) };
Virtio_avail _avail { _ram.local_address(_data.driver(), 6 + 2 * _data.num) };
Virtio_used _used { _ram.local_address(_data.device(), 6 + 8 * _data.num) };
uint16_t _idx { 0 };
uint32_t _length { _data.num };
bool _tx { _data.tx };
bool const _verbose { false };
public: public:
Virtio_queue(Virtio_queue_data &data, Ram &ram) Virtio_split_queue(addr_t const descriptor_area,
: _data(data), _ram(ram) { } addr_t const device_area,
addr_t const driver_area,
unsigned const queue_num,
bool verbose() const { return _verbose; } Ram & ram)
:
_avail(ram.local_address(driver_area, 6+2*queue_num), queue_num),
_used(ram.local_address(device_area, 6+8*queue_num), queue_num),
_descriptors(ram, descriptor_area, queue_num),
_ram(ram) { }
template <typename FUNC> template <typename FUNC>
bool notify(FUNC func) bool notify(FUNC func)
{ {
asm volatile ("dmb ish" : : : "memory"); memory_barrier();
uint16_t used_idx = _used.read<Virtio_used::Idx>();
uint16_t avail_idx = _avail.read<Virtio_avail::Idx>();
uint16_t written = 0; bool written = false;
if (_verbose) for (Ring_index avail_idx = _avail.current();
Genode::log(_length > 64 ? "net $ " : "console $ ", _cur_idx != avail_idx; _cur_idx.inc()) {
"[", _tx ? "tx] " : "rx] ",
"idx: ", _idx, " avail_idx: ", avail_idx, " used_idx: ", used_idx,
" queue length: ", _length, " avail flags: ", _avail.read<Virtio_avail::Flags>());
while (_idx != avail_idx && written < _length) { Descriptor_index id = _avail.get(_cur_idx);
Descriptor descriptor = _descriptors.get(id);
uint64_t address = descriptor.address();
size_t size = descriptor.length();
uint16_t id = _avail.read<Virtio_avail::Ring>(_idx % _length); if (!address || !size) { break; }
/* make sure id stays in ring */
id %= _length;
Virtio_descriptor descr = _descr.index(id);
uint64_t address = descr.address();
size_t length = descr.length();
if (!address || !length) break;
addr_t data = 0;
try { try {
data = _ram.local_address(address, length); addr_t data = _ram.local_address(address, size);
length = func(data, length); size_t consumed = func(data, size);
if (!consumed) { break; }
_used.add(_cur_idx, id, consumed);
written = true;
} catch (...) { break; } } catch (...) { break; }
if (length == 0) break;
_used.write<Virtio_used::Flags>(0);
Virtio_used::Elem::access_t elem = 0;
Virtio_used::Elem::Id::set(elem, id);
Virtio_used::Elem::Length::set(elem, length);
_used.write<Virtio_used::Elem>(elem, _idx % _length);
written++; _idx++;
if (used_idx + written == avail_idx) break;
} }
if (written) { if (written) {
used_idx += written; _used.write<Used_queue::Idx>(_cur_idx.idx());
_used.write<Virtio_used::Idx>(used_idx); memory_barrier();
asm volatile ("dmb ish" : : : "memory");
} }
if (written && _verbose) return written && _avail.inject_irq();
Genode::log(_length > 64 ? "net $ " : "console $ ",
"[", _tx ? "tx] " : "rx] ", "updated used_idx: ", used_idx,
" written: ", written);
return written > 0 && _avail.inject_irq();
} }
}; };
template <typename QUEUE, unsigned NUM>
class Vmm::Virtio_device : public Vmm::Mmio_device class Vmm::Virtio_device : public Vmm::Mmio_device
{ {
protected: protected:
enum { RX = 0, TX = 1, NUM = 2 };
Virtio_queue_data _data[NUM];
uint32_t _current { RX };
Genode::Constructible<Virtio_queue> _queue[NUM];
Gic::Irq & _irq; Gic::Irq & _irq;
Ram & _ram; Ram & _ram;
Genode::Mutex _mutex; Genode::Mutex _mutex;
Genode::Constructible<QUEUE> _queue[NUM];
struct Dummy {
Mmio_register regs[7];
} _reg_container { .regs = {
{ "MagicValue", Mmio_register::RO, 0x0, 4, 0x74726976 },
{ "Version", Mmio_register::RO, 0x4, 4, 0x2 },
{ "DeviceID", Mmio_register::RO, 0x8, 4, 0x0 },
{ "VendorID", Mmio_register::RO, 0xc, 4, 0x554d4551 /* QEMU */ },
{ "DeviceFeatureSel", Mmio_register::RW, 0x14, 4, 0 },
{ "DriverFeatureSel", Mmio_register::RW, 0x24, 4, 0 },
{ "QueueNumMax", Mmio_register::RO, 0x34, 4, 8 }
}};
void _device_id(unsigned id)
{
_reg_container.regs[2].set(id);
}
void _queue_select(uint32_t sel) { _current = sel; }
Virtio_queue_data &_queue_data() { return _data[_current]; }
void _queue_state(bool const construct)
{
if (construct && !_queue[_current].constructed() && _queue_data().num > 0) {
_queue_data().tx = (_current == TX);
_queue[_current].construct(_queue_data(), _ram);
}
if (!construct && _queue[_current].constructed())
_queue[_current].destruct();
}
void _assert_irq()
{
_interrupt_status.set(0x1);
_irq.assert();
}
void _deassert_irq()
{
_interrupt_status.set(0);
_irq.deassert();
}
virtual void _notify(unsigned idx) = 0; virtual void _notify(unsigned idx) = 0;
virtual Register _device_specific_features() = 0;
/*************** /***************
** Registers ** ** Registers **
***************/ ***************/
struct DeviceFeatures : Mmio_register class Reg : public Mmio_register
{ {
enum { private:
VIRTIO_F_VERSION_1 = 1,
Virtio_device & _dev;
public:
Reg(Virtio_device & dev,
Mmio_register::Name name,
Mmio_register::Type type,
Genode::uint64_t start,
uint32_t value = 0)
: Mmio_register(name, type, start, 4, value), _dev(dev) {
_dev.add(*this); }
Virtio_device & device() { return _dev; }
}; };
Virtio_device &_device; class Set : public Reg
Mmio_register &_selector;
Register read(Address_range&, Cpu&) override
{ {
/* lower 32 bit */ private:
if (_selector.value() == 0) return _device._device_specific_features();
/* upper 32 bit */ Reg & _selector;
return VIRTIO_F_VERSION_1; typename Reg::Register _regs[32] { 0 };
public:
Set(Virtio_device & device,
Reg & selector,
Mmio_register::Name name,
Mmio_register::Type type,
Genode::uint64_t start)
: Reg(device, name, type, start),
_selector(selector) {}
Register read(Address_range &, Cpu &) override {
return _regs[_selector.value()]; }
void write(Address_range & a, Cpu &, Register reg) override {
_regs[_selector.value()] = reg; }
void set(Register value) override {
_regs[_selector.value()] = value; }
Register value() const override {
return _regs[_selector.value()]; }
};
Reg _magic { *this, "MagicValue", Reg::RO, 0x0, 0x74726976 };
Reg _version { *this, "Version", Reg::RO, 0x4, 0x2 };
Reg _dev_id { *this, "DeviceID", Reg::RO, 0x8 };
Reg _vendor_id { *this, "VendorID", Reg::RO, 0xc, 0x554d4551/*QEMU*/};
Reg _dev_sel { *this, "DeviceFeatureSel", Reg::WO, 0x14 };
Reg _drv_sel { *this, "DriverFeatureSel", Reg::WO, 0x24 };
Reg _queue_sel { *this, "QueueSel", Reg::WO, 0x30 };
Set _dev_feature { *this, _dev_sel, "DeviceFeatures", Reg::RO, 0x10 };
Set _drv_features { *this, _drv_sel, "DriverFeatures", Reg::WO, 0x20 };
Reg _queue_num_max { *this, "QueueNumMax", Reg::RO, 0x34, QUEUE::MAX_SIZE };
Set _queue_num { *this, _queue_sel, "QueueNum", Reg::WO, 0x38 };
Reg _irq_status { *this, "InterruptStatus", Reg::RO, 0x60 };
Reg _status { *this, "Status", Reg::RW, 0x70 };
Set _descr_low { *this, _queue_sel, "QueueDescrLow", Reg::WO, 0x80 };
Set _descr_high { *this, _queue_sel, "QueueDescrHigh", Reg::WO, 0x84 };
Set _driver_low { *this, _queue_sel, "QueueDriverLow", Reg::WO, 0x90 };
Set _driver_high { *this, _queue_sel, "QueueDriverHigh", Reg::WO, 0x94 };
Set _device_low { *this, _queue_sel, "QueueDeviceLow", Reg::WO, 0xa0 };
Set _device_high { *this, _queue_sel, "QueueDeviceHigh", Reg::WO, 0xa4 };
uint64_t _descriptor_area() const
{
return ((uint64_t)_descr_high.value()<<32) | _descr_low.value();
} }
DeviceFeatures(Virtio_device &device, Mmio_register &selector) uint64_t _driver_area() const
: Mmio_register("DeviceFeatures", Mmio_register::RO, 0x10, 4),
_device(device), _selector(selector)
{ }
} _device_features { *this, _reg_container.regs[4] };
struct DriverFeatures : Mmio_register
{ {
Mmio_register &_selector; return ((uint64_t)_driver_high.value()<<32) | _driver_low.value();
uint32_t _lower { 0 };
uint32_t _upper { 0 };
void write(Address_range&, Cpu&, Register reg) override
{
if (_selector.value() == 0) {
_lower = reg;
}
_upper = reg;
} }
DriverFeatures(Mmio_register &selector) uint64_t _device_area() const
: Mmio_register("DriverFeatures", Mmio_register::WO, 0x20, 4),
_selector(selector)
{ }
} _driver_features { _reg_container.regs[5] };
struct Status : Mmio_register
{ {
Register read(Address_range&, Cpu&) override return ((uint64_t)_device_high.value()<<32) | _device_low.value();
{
return value();
} }
void write(Address_range&, Cpu&, Register reg) override void _assert_irq()
{ {
set(reg); _irq_status.set(0x1);
_irq.assert();
} }
Status() void _deassert_irq()
: Mmio_register("Status", Mmio_register::RW, 0x70, 4, 0)
{ }
} _status;
struct QueueSel : Mmio_register
{ {
Virtio_device &device; _irq_status.set(0);
_irq.deassert();
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
if (reg >= device.NUM) return;
device._queue_select(reg);
} }
QueueSel(Virtio_device &device) void _construct_queue()
: Mmio_register("QueueSel", Mmio_register::WO, 0x30, 4),
device(device) { }
} _queue_sel { *this };
struct QueueNum : Mmio_register
{ {
Virtio_device &device; Genode::Mutex::Guard guard(mutex());
void write(Address_range&, Cpu&, Register reg) override unsigned num = _queue_sel.value();
{
Genode::Mutex::Guard guard(device.mutex()); if (_queue[num].constructed()) { return; }
device._queue_data().num = Genode::min(reg,
device._reg_container.regs[6].value()); _queue[num].construct(_descriptor_area(), _device_area(),
_driver_area(), _queue_num.value(), _ram);
} }
QueueNum(Virtio_device &device) struct Queue_ready : Reg
: Mmio_register("QueueNum", Mmio_register::WO, 0x38, 4),
device(device) { }
} _queue_num { *this };
struct QueueReady : Mmio_register
{ {
Virtio_device &device; void write(Address_range&, Cpu&, Register reg) override {
if (reg == 1) { Reg::device()._construct_queue(); } }
Register read(Address_range&, Cpu&) override Queue_ready(Virtio_device & device)
{ : Reg(device, "QueueReady", Reg::RW, 0x44) {}
Genode::Mutex::Guard guard(device.mutex());
return device._queue_data().ready;
}
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
bool construct = reg == 1 ? true : false;
device._queue_data().ready = reg;
device._queue_state(construct);
}
QueueReady(Virtio_device &device)
: Mmio_register("QueueReady", Mmio_register::RW, 0x44, 4),
device(device) { }
} _queue_ready { *this }; } _queue_ready { *this };
struct QueueNotify : Mmio_register
{
Virtio_device &device;
struct Queue_notify : Reg
{
void write(Address_range&, Cpu&, Register reg) override void write(Address_range&, Cpu&, Register reg) override
{ {
Genode::Mutex::Guard guard(device.mutex()); Genode::Mutex::Guard guard(Reg::device().mutex());
if (!device._queue[reg].constructed()) return;
device._notify(reg); if (reg >= NUM) {
error("Number of queues not supported by device!");
return;
} }
QueueNotify(Virtio_device &device) if (!Reg::device()._queue[reg].constructed()) {
: Mmio_register("QueueNotify", Mmio_register::WO, 0x50, 4), error("Queue is not constructed and cannot be notified!");
device(device) { } return;
}
Reg::device()._notify(reg);
}
Queue_notify(Virtio_device & device)
: Reg(device, "QueueNotify", Mmio_register::WO, 0x50) { }
} _queue_notify { *this }; } _queue_notify { *this };
struct QueueDescrLow : Mmio_register
{
Virtio_device &device;
struct Interrupt_ack : Reg
{
void write(Address_range&, Cpu&, Register reg) override void write(Address_range&, Cpu&, Register reg) override
{ {
Genode::Mutex::Guard guard(device.mutex()); Genode::Mutex::Guard guard(Reg::device().mutex());
device._queue_data().descr_low = reg; Reg::device()._deassert_irq();
} }
QueueDescrLow(Virtio_device &device) Interrupt_ack(Virtio_device &device)
: Mmio_register("QueuDescrLow", Mmio_register::WO, 0x80, 4), : Reg(device, "InterruptAck", Mmio_register::WO, 0x64) {}
device(device) { }
} _queue_descr_low { *this };
struct QueueDescrHigh : Mmio_register
{
Virtio_device &device;
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
device._queue_data().descr_high = reg;
}
QueueDescrHigh(Virtio_device &device)
: Mmio_register("QueuDescrHigh", Mmio_register::WO, 0x84, 4),
device(device) { }
} _queue_descr_high { *this };
struct QueueDriverLow : Mmio_register
{
Virtio_device &device;
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
device._queue_data().driver_low = reg;
}
QueueDriverLow(Virtio_device &device)
: Mmio_register("QueuDriverLow", Mmio_register::WO, 0x90, 4),
device(device) { }
} _queue_driver_low { *this };
struct QueueDriverHigh : Mmio_register
{
Virtio_device &device;
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
device._queue_data().driver_high = reg;
}
QueueDriverHigh(Virtio_device &device)
: Mmio_register("QueuDriverHigh", Mmio_register::WO, 0x94, 4),
device(device) { }
} _queue_driver_high { *this };
struct QueueDeviceLow : Mmio_register
{
Virtio_device &device;
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
device._queue_data().device_low = reg;
}
QueueDeviceLow(Virtio_device &device)
: Mmio_register("QueuDeviceLow", Mmio_register::WO, 0xa0, 4),
device(device) { }
} _queue_device_low { *this };
struct QueueDeviceHigh : Mmio_register
{
Virtio_device &device;
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
device._queue_data().device_high = reg;
}
QueueDeviceHigh(Virtio_device &device)
: Mmio_register("QueuDeviceHigh", Mmio_register::WO, 0xa4, 4),
device(device) { }
} _queue_device_high { *this };
struct InterruptStatus : Mmio_register
{
Register read(Address_range&, Cpu&) override
{
return value();
}
InterruptStatus()
: Mmio_register("InterruptStatus", Mmio_register::RO, 0x60, 4)
{ }
} _interrupt_status;
struct InterruptAck : Mmio_register
{
Virtio_device &device;
void write(Address_range&, Cpu&, Register reg) override
{
Genode::Mutex::Guard guard(device.mutex());
device._deassert_irq();
}
InterruptAck(Virtio_device &device)
: Mmio_register("InterruptAck", Mmio_register::WO, 0x64, 4),
device(device) { }
} _interrupt_ack { *this }; } _interrupt_ack { *this };
public: public:
Virtio_device(const char * const name, Virtio_device(const char * const name,
const uint64_t addr, const Genode::uint64_t addr,
const uint64_t size, const Genode::uint64_t size,
unsigned irq, unsigned irq,
Cpu &cpu, Cpu &cpu,
Mmio_bus &bus, Mmio_bus &bus,
Ram &ram, Ram &ram,
unsigned queue_size = 8); uint32_t dev_id)
:
Mmio_device(name, addr, size),
_irq(cpu.gic().irq(irq)),
_ram(ram)
{
/* set device id from specific, derived device */
_dev_id.set(dev_id);
/* prepare device features */
enum { VIRTIO_F_VERSION_1 = 1 };
_dev_sel.set(1); /* set to 32...63 feature bits */
_dev_feature.set(VIRTIO_F_VERSION_1);
_dev_sel.set(0); /* set to 0...31 feature bits */
bus.add(*this);
}
Genode::Mutex & mutex() { return _mutex; } Genode::Mutex & mutex() { return _mutex; }
}; };

View File

@ -18,15 +18,15 @@
#include <virtio_device.h> #include <virtio_device.h>
namespace Vmm namespace Vmm { class Virtio_net; }
{
class Virtio_net;
}
class Vmm::Virtio_net : public Virtio_device
class Vmm::Virtio_net : public Virtio_device<Virtio_split_queue, 2>
{ {
private: private:
enum Queue_id { RX, TX };
Genode::Env &_env; Genode::Env &_env;
Genode::Heap _heap { _env.ram(), _env.rm() }; Genode::Heap _heap { _env.ram(), _env.rm() };
@ -60,10 +60,6 @@ class Vmm::Virtio_net : public Virtio_device
size_t sz = Genode::min(size, rx_packet.size() + NIC_HEADER_SIZE); size_t sz = Genode::min(size, rx_packet.size() + NIC_HEADER_SIZE);
if (_queue[RX]->verbose() && sz < rx_packet.size() + NIC_HEADER_SIZE)
Genode::warning("[rx] trim packet from ",
rx_packet.size() + NIC_HEADER_SIZE, " -> ", sz, " bytes");
Genode::memcpy((void *)(data + NIC_HEADER_SIZE), Genode::memcpy((void *)(data + NIC_HEADER_SIZE),
_nic.rx()->packet_content(rx_packet), _nic.rx()->packet_content(rx_packet),
sz - NIC_HEADER_SIZE); sz - NIC_HEADER_SIZE);
@ -121,13 +117,9 @@ class Vmm::Virtio_net : public Virtio_device
_rx(); _rx();
} }
Register _device_specific_features() override enum Device_id { NIC = 0x1 };
{
enum { VIRTIO_NET_F_MAC = 1u << 5 };
return VIRTIO_NET_F_MAC;
}
struct ConfigArea : Mmio_register struct Config_area : Reg
{ {
Nic::Mac_address & mac; Nic::Mac_address & mac;
@ -138,11 +130,11 @@ class Vmm::Virtio_net : public Virtio_device
return mac.addr[range.start]; return mac.addr[range.start];
} }
ConfigArea(Nic::Mac_address &mac) Config_area(Virtio_net & device, Nic::Mac_address & mac)
: Mmio_register("ConfigArea", Mmio_register::RO, 0x100, 8), : Reg(device, "ConfigArea", Mmio_register::RO, 0x100, 8),
mac(mac) mac(mac)
{ } { }
} _config_area { _mac }; } _config_area { *this, _mac };
public: public:
@ -154,14 +146,14 @@ class Vmm::Virtio_net : public Virtio_device
Mmio_bus &bus, Mmio_bus &bus,
Ram &ram, Ram &ram,
Genode::Env &env) Genode::Env &env)
: Virtio_device(name, addr, size, irq, cpu, bus, ram, 1024), :
Virtio_device<Virtio_split_queue, 2>(name, addr, size,
irq, cpu, bus, ram, NIC),
_env(env), _env(env),
_handler(cpu, _env.ep(), *this, &Virtio_net::_handle) _handler(cpu, _env.ep(), *this, &Virtio_net::_handle)
{ {
/* set device ID to network */ enum { VIRTIO_NET_F_MAC = 1u << 5 };
_device_id(0x1); _dev_feature.set(VIRTIO_NET_F_MAC);
add(_config_area);
_nic.tx_channel()->sigh_ready_to_submit(_handler); _nic.tx_channel()->sigh_ready_to_submit(_handler);
_nic.tx_channel()->sigh_ack_avail (_handler); _nic.tx_channel()->sigh_ack_avail (_handler);