mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-14 13:18:19 +00:00
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:
committed by
Norman Feske
parent
83c2309710
commit
2879aa003b
@ -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; }
|
||||||
|
@ -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,
|
||||||
|
@ -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)/../..
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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,25 +62,24 @@ 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:
|
||||||
|
|
||||||
Virtio_console(const char * const name,
|
Virtio_console(const char * const name,
|
||||||
const uint64_t addr,
|
const uint64_t addr,
|
||||||
const uint64_t size,
|
const uint64_t size,
|
||||||
unsigned irq,
|
unsigned irq,
|
||||||
Cpu &cpu,
|
Cpu & cpu,
|
||||||
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),
|
:
|
||||||
_terminal(env, "console"),
|
Virtio_device<Virtio_split_queue, 2>(name, addr, size,
|
||||||
_handler(cpu, env.ep(), *this, &Virtio_console::_read)
|
irq, cpu, bus, ram, CONSOLE),
|
||||||
|
_terminal(env, "console"),
|
||||||
|
_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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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);
|
|
||||||
}
|
|
@ -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,522 +24,399 @@
|
|||||||
#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
|
|
||||||
{
|
|
||||||
uint32_t descr_low { 0 };
|
|
||||||
uint32_t descr_high { 0 };
|
|
||||||
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:
|
|
||||||
|
|
||||||
Virtio_descriptor(addr_t base)
|
|
||||||
: Mmio(base) { }
|
|
||||||
|
|
||||||
|
|
||||||
struct Address : Register<0x0, 64> { };
|
|
||||||
struct Length : Register<0x8, 32> { };
|
|
||||||
|
|
||||||
struct Flags : Register<0xc, 16>
|
|
||||||
{
|
|
||||||
struct Next : Bitfield<0, 1> { };
|
|
||||||
struct Write : Bitfield<1, 1> { };
|
|
||||||
struct Indirect : Bitfield<2, 1> { };
|
|
||||||
};
|
|
||||||
|
|
||||||
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>(); }
|
|
||||||
size_t length () const { return read<Length>(); }
|
|
||||||
uint16_t flags() const { return read<Flags>(); }
|
|
||||||
uint16_t next() const { return read<Next>(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class Vmm::Virtio_avail : public Genode::Mmio
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
Virtio_avail(addr_t base)
|
|
||||||
: Mmio(base) { };
|
|
||||||
|
|
||||||
struct Flags : Register<0x0, 16> { };
|
|
||||||
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:
|
|
||||||
|
|
||||||
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
|
* Split queue implementation
|
||||||
*/
|
*/
|
||||||
class Vmm::Virtio_queue
|
class Vmm::Virtio_split_queue
|
||||||
{
|
{
|
||||||
private:
|
public:
|
||||||
|
|
||||||
Virtio_queue_data &_data;
|
static constexpr unsigned MAX_SIZE_LOG2 = 9;
|
||||||
Ram &_ram;
|
static constexpr unsigned MAX_SIZE = 1 << MAX_SIZE_LOG2;
|
||||||
|
|
||||||
Virtio_descriptor _descr { _ram.local_address(_data.descr(),
|
protected:
|
||||||
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 };
|
template <unsigned LOG2>
|
||||||
uint32_t _length { _data.num };
|
class Index
|
||||||
bool _tx { _data.tx };
|
{
|
||||||
bool const _verbose { false };
|
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 Length : Register<0x8, 32> { };
|
||||||
|
|
||||||
|
struct Flags : Register<0xc, 16>
|
||||||
|
{
|
||||||
|
struct Next : Bitfield<0, 1> { };
|
||||||
|
struct Write : Bitfield<1, 1> { };
|
||||||
|
struct Indirect : Bitfield<2, 1> { };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Next : Register<0xe, 16> { };
|
||||||
|
|
||||||
|
uint64_t address() const { return read<Address>(); }
|
||||||
|
size_t length () const { return read<Length>(); }
|
||||||
|
uint16_t flags() const { return read<Flags>(); }
|
||||||
|
uint16_t next() const { return read<Next>(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Descriptor_array
|
||||||
|
{
|
||||||
|
size_t const elem_size { 16 };
|
||||||
|
unsigned const max;
|
||||||
|
addr_t const start;
|
||||||
|
|
||||||
|
Descriptor_array(Ram & ram, addr_t base, unsigned const max)
|
||||||
|
:
|
||||||
|
max(max),
|
||||||
|
start(ram.local_address(base, max * elem_size)) {}
|
||||||
|
|
||||||
|
Descriptor get(Descriptor_index idx)
|
||||||
|
{
|
||||||
|
if (idx.idx() >= max) error("Descriptor_index out of bounds");
|
||||||
|
return Descriptor(start + (elem_size * idx.idx()));
|
||||||
|
}
|
||||||
|
} _descriptors;
|
||||||
|
|
||||||
|
|
||||||
|
Ram & _ram;
|
||||||
|
Ring_index _cur_idx;
|
||||||
|
|
||||||
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,
|
||||||
|
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>
|
||||||
|
bool notify(FUNC func)
|
||||||
|
{
|
||||||
|
memory_barrier();
|
||||||
|
|
||||||
bool verbose() const { return _verbose; }
|
bool written = false;
|
||||||
|
|
||||||
template <typename FUNC>
|
for (Ring_index avail_idx = _avail.current();
|
||||||
bool notify(FUNC func)
|
_cur_idx != avail_idx; _cur_idx.inc()) {
|
||||||
{
|
|
||||||
asm volatile ("dmb ish" : : : "memory");
|
|
||||||
uint16_t used_idx = _used.read<Virtio_used::Idx>();
|
|
||||||
uint16_t avail_idx = _avail.read<Virtio_avail::Idx>();
|
|
||||||
|
|
||||||
uint16_t written = 0;
|
Descriptor_index id = _avail.get(_cur_idx);
|
||||||
|
Descriptor descriptor = _descriptors.get(id);
|
||||||
|
uint64_t address = descriptor.address();
|
||||||
|
size_t size = descriptor.length();
|
||||||
|
|
||||||
if (_verbose)
|
if (!address || !size) { break; }
|
||||||
Genode::log(_length > 64 ? "net $ " : "console $ ",
|
|
||||||
"[", _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) {
|
try {
|
||||||
|
addr_t data = _ram.local_address(address, size);
|
||||||
|
size_t consumed = func(data, size);
|
||||||
|
if (!consumed) { break; }
|
||||||
|
_used.add(_cur_idx, id, consumed);
|
||||||
|
written = true;
|
||||||
|
} catch (...) { break; }
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t id = _avail.read<Virtio_avail::Ring>(_idx % _length);
|
if (written) {
|
||||||
|
_used.write<Used_queue::Idx>(_cur_idx.idx());
|
||||||
|
memory_barrier();
|
||||||
|
}
|
||||||
|
|
||||||
/* make sure id stays in ring */
|
return written && _avail.inject_irq();
|
||||||
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 {
|
|
||||||
data = _ram.local_address(address, length);
|
|
||||||
length = func(data, length);
|
|
||||||
} 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) {
|
|
||||||
used_idx += written;
|
|
||||||
_used.write<Virtio_used::Idx>(used_idx);
|
|
||||||
asm volatile ("dmb ish" : : : "memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (written && _verbose)
|
|
||||||
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 };
|
Gic::Irq & _irq;
|
||||||
Virtio_queue_data _data[NUM];
|
Ram & _ram;
|
||||||
uint32_t _current { RX };
|
Genode::Mutex _mutex;
|
||||||
|
Genode::Constructible<QUEUE> _queue[NUM];
|
||||||
Genode::Constructible<Virtio_queue> _queue[NUM];
|
|
||||||
Gic::Irq &_irq;
|
|
||||||
Ram &_ram;
|
|
||||||
Genode::Mutex _mutex;
|
|
||||||
|
|
||||||
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 &_device;
|
Virtio_device & _dev;
|
||||||
Mmio_register &_selector;
|
|
||||||
|
|
||||||
Register read(Address_range&, Cpu&) override
|
public:
|
||||||
{
|
|
||||||
/* lower 32 bit */
|
|
||||||
if (_selector.value() == 0) return _device._device_specific_features();
|
|
||||||
|
|
||||||
/* upper 32 bit */
|
Reg(Virtio_device & dev,
|
||||||
return VIRTIO_F_VERSION_1;
|
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); }
|
||||||
|
|
||||||
DeviceFeatures(Virtio_device &device, Mmio_register &selector)
|
Virtio_device & device() { return _dev; }
|
||||||
: Mmio_register("DeviceFeatures", Mmio_register::RO, 0x10, 4),
|
};
|
||||||
_device(device), _selector(selector)
|
|
||||||
{ }
|
|
||||||
} _device_features { *this, _reg_container.regs[4] };
|
|
||||||
|
|
||||||
struct DriverFeatures : Mmio_register
|
class Set : public Reg
|
||||||
{
|
{
|
||||||
Mmio_register &_selector;
|
private:
|
||||||
uint32_t _lower { 0 };
|
|
||||||
uint32_t _upper { 0 };
|
|
||||||
|
|
||||||
void write(Address_range&, Cpu&, Register reg) override
|
Reg & _selector;
|
||||||
{
|
typename Reg::Register _regs[32] { 0 };
|
||||||
if (_selector.value() == 0) {
|
|
||||||
_lower = reg;
|
|
||||||
}
|
|
||||||
_upper = reg;
|
|
||||||
}
|
|
||||||
|
|
||||||
DriverFeatures(Mmio_register &selector)
|
public:
|
||||||
: Mmio_register("DriverFeatures", Mmio_register::WO, 0x20, 4),
|
|
||||||
_selector(selector)
|
|
||||||
{ }
|
|
||||||
} _driver_features { _reg_container.regs[5] };
|
|
||||||
|
|
||||||
struct Status : Mmio_register
|
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
|
||||||
{
|
{
|
||||||
Register read(Address_range&, Cpu&) override
|
return ((uint64_t)_descr_high.value()<<32) | _descr_low.value();
|
||||||
{
|
}
|
||||||
return value();
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(Address_range&, Cpu&, Register reg) override
|
uint64_t _driver_area() const
|
||||||
{
|
|
||||||
set(reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
Status()
|
|
||||||
: Mmio_register("Status", Mmio_register::RW, 0x70, 4, 0)
|
|
||||||
{ }
|
|
||||||
} _status;
|
|
||||||
|
|
||||||
struct QueueSel : Mmio_register
|
|
||||||
{
|
{
|
||||||
Virtio_device &device;
|
return ((uint64_t)_driver_high.value()<<32) | _driver_low.value();
|
||||||
|
}
|
||||||
|
|
||||||
void write(Address_range&, Cpu&, Register reg) override
|
uint64_t _device_area() const
|
||||||
{
|
|
||||||
Genode::Mutex::Guard guard(device.mutex());
|
|
||||||
if (reg >= device.NUM) return;
|
|
||||||
device._queue_select(reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
QueueSel(Virtio_device &device)
|
|
||||||
: Mmio_register("QueueSel", Mmio_register::WO, 0x30, 4),
|
|
||||||
device(device) { }
|
|
||||||
} _queue_sel { *this };
|
|
||||||
|
|
||||||
struct QueueNum : Mmio_register
|
|
||||||
{
|
{
|
||||||
Virtio_device &device;
|
return ((uint64_t)_device_high.value()<<32) | _device_low.value();
|
||||||
|
}
|
||||||
|
|
||||||
void write(Address_range&, Cpu&, Register reg) override
|
void _assert_irq()
|
||||||
{
|
|
||||||
Genode::Mutex::Guard guard(device.mutex());
|
|
||||||
device._queue_data().num = Genode::min(reg,
|
|
||||||
device._reg_container.regs[6].value());
|
|
||||||
}
|
|
||||||
|
|
||||||
QueueNum(Virtio_device &device)
|
|
||||||
: Mmio_register("QueueNum", Mmio_register::WO, 0x38, 4),
|
|
||||||
device(device) { }
|
|
||||||
} _queue_num { *this };
|
|
||||||
|
|
||||||
struct QueueReady : Mmio_register
|
|
||||||
{
|
{
|
||||||
Virtio_device &device;
|
_irq_status.set(0x1);
|
||||||
|
_irq.assert();
|
||||||
|
}
|
||||||
|
|
||||||
Register read(Address_range&, Cpu&) override
|
void _deassert_irq()
|
||||||
{
|
{
|
||||||
Genode::Mutex::Guard guard(device.mutex());
|
_irq_status.set(0);
|
||||||
return device._queue_data().ready;
|
_irq.deassert();
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(Address_range&, Cpu&, Register reg) override
|
void _construct_queue()
|
||||||
{
|
{
|
||||||
Genode::Mutex::Guard guard(device.mutex());
|
Genode::Mutex::Guard guard(mutex());
|
||||||
bool construct = reg == 1 ? true : false;
|
|
||||||
device._queue_data().ready = reg;
|
|
||||||
device._queue_state(construct);
|
|
||||||
}
|
|
||||||
|
|
||||||
QueueReady(Virtio_device &device)
|
unsigned num = _queue_sel.value();
|
||||||
: Mmio_register("QueueReady", Mmio_register::RW, 0x44, 4),
|
|
||||||
device(device) { }
|
if (_queue[num].constructed()) { return; }
|
||||||
|
|
||||||
|
_queue[num].construct(_descriptor_area(), _device_area(),
|
||||||
|
_driver_area(), _queue_num.value(), _ram);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Queue_ready : Reg
|
||||||
|
{
|
||||||
|
void write(Address_range&, Cpu&, Register reg) override {
|
||||||
|
if (reg == 1) { Reg::device()._construct_queue(); } }
|
||||||
|
|
||||||
|
Queue_ready(Virtio_device & device)
|
||||||
|
: Reg(device, "QueueReady", Reg::RW, 0x44) {}
|
||||||
} _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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Reg::device()._queue[reg].constructed()) {
|
||||||
|
error("Queue is not constructed and cannot be notified!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reg::device()._notify(reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueueNotify(Virtio_device &device)
|
Queue_notify(Virtio_device & device)
|
||||||
: Mmio_register("QueueNotify", Mmio_register::WO, 0x50, 4),
|
: Reg(device, "QueueNotify", Mmio_register::WO, 0x50) { }
|
||||||
device(device) { }
|
|
||||||
} _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; }
|
||||||
};
|
};
|
||||||
|
@ -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,28 +117,24 @@ 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;
|
||||||
|
|
||||||
Register read(Address_range& range, Cpu&) override
|
Register read(Address_range & range, Cpu&) override
|
||||||
{
|
{
|
||||||
if (range.start > 5) return 0;
|
if (range.start > 5) return 0;
|
||||||
|
|
||||||
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),
|
:
|
||||||
_env(env),
|
Virtio_device<Virtio_split_queue, 2>(name, addr, size,
|
||||||
_handler(cpu, _env.ep(), *this, &Virtio_net::_handle)
|
irq, cpu, bus, ram, NIC),
|
||||||
|
_env(env),
|
||||||
|
_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);
|
||||||
|
Reference in New Issue
Block a user