From 2879aa003b82eef08d96a6159c53ff8dc2cd645c Mon Sep 17 00:00:00 2001 From: Stefan Kalkowski Date: Thu, 18 Feb 2021 17:56:56 +0100 Subject: [PATCH] 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 --- repos/os/src/server/vmm/mmio.cc | 2 +- repos/os/src/server/vmm/mmio.h | 4 +- repos/os/src/server/vmm/spec/arm_v7/target.mk | 1 - repos/os/src/server/vmm/spec/arm_v8/target.mk | 1 - repos/os/src/server/vmm/virtio_console.h | 37 +- repos/os/src/server/vmm/virtio_device.cc | 57 -- repos/os/src/server/vmm/virtio_device.h | 763 ++++++++---------- repos/os/src/server/vmm/virtio_net.h | 46 +- 8 files changed, 361 insertions(+), 550 deletions(-) delete mode 100644 repos/os/src/server/vmm/virtio_device.cc diff --git a/repos/os/src/server/vmm/mmio.cc b/repos/os/src/server/vmm/mmio.cc index 55f816b843..14f625f5d6 100644 --- a/repos/os/src/server/vmm/mmio.cc +++ b/repos/os/src/server/vmm/mmio.cc @@ -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; } diff --git a/repos/os/src/server/vmm/mmio.h b/repos/os/src/server/vmm/mmio.h index 7b743b1809..e26d4969bc 100644 --- a/repos/os/src/server/vmm/mmio.h +++ b/repos/os/src/server/vmm/mmio.h @@ -35,8 +35,8 @@ class Vmm::Mmio_register : public Vmm::Address_range virtual Register read(Address_range & access, Cpu&); virtual void write(Address_range & access, Cpu&, Register value); - void set(Register value); - Register value(); + virtual void set(Register value); + virtual Register value() const; Mmio_register(Name name, Type type, diff --git a/repos/os/src/server/vmm/spec/arm_v7/target.mk b/repos/os/src/server/vmm/spec/arm_v7/target.mk index 37e2434f05..175ad7c3bb 100644 --- a/repos/os/src/server/vmm/spec/arm_v7/target.mk +++ b/repos/os/src/server/vmm/spec/arm_v7/target.mk @@ -11,7 +11,6 @@ SRC_CC += main.cc SRC_CC += mmio.cc SRC_CC += pl011.cc SRC_CC += vm.cc -SRC_CC += virtio_device.cc INC_DIR += $(PRG_DIR)/../.. $(PRG_DIR) vpath %.cc $(PRG_DIR)/../.. diff --git a/repos/os/src/server/vmm/spec/arm_v8/target.mk b/repos/os/src/server/vmm/spec/arm_v8/target.mk index f57618be18..954b4c43f3 100644 --- a/repos/os/src/server/vmm/spec/arm_v8/target.mk +++ b/repos/os/src/server/vmm/spec/arm_v8/target.mk @@ -10,7 +10,6 @@ SRC_CC += gic.cc SRC_CC += main.cc SRC_CC += mmio.cc SRC_CC += pl011.cc -SRC_CC += virtio_device.cc SRC_CC += vm.cc INC_DIR += $(PRG_DIR)/../.. $(PRG_DIR) diff --git a/repos/os/src/server/vmm/virtio_console.h b/repos/os/src/server/vmm/virtio_console.h index 419f5f0063..2bead217ce 100644 --- a/repos/os/src/server/vmm/virtio_console.h +++ b/repos/os/src/server/vmm/virtio_console.h @@ -18,15 +18,15 @@ #include -namespace Vmm -{ - class Virtio_console; -} +namespace Vmm { class Virtio_console; } -class Vmm::Virtio_console : public Virtio_device + +class Vmm::Virtio_console : public Virtio_device { private: + enum Queue_id { RX, TX }; + Terminal::Connection _terminal; Cpu::Signal_handler _handler; @@ -62,25 +62,24 @@ class Vmm::Virtio_console : public Virtio_device _assert_irq(); } - Register _device_specific_features() override { return 0; } + enum Device_id { CONSOLE = 0x3 }; public: Virtio_console(const char * const name, - const uint64_t addr, - const uint64_t size, - unsigned irq, - Cpu &cpu, - Mmio_bus &bus, - Ram &ram, - Genode::Env &env) - : Virtio_device(name, addr, size, irq, cpu, bus, ram), - _terminal(env, "console"), - _handler(cpu, env.ep(), *this, &Virtio_console::_read) + const uint64_t addr, + const uint64_t size, + unsigned irq, + Cpu & cpu, + Mmio_bus & bus, + Ram & ram, + Genode::Env & env) + : + Virtio_device(name, addr, size, + 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); } }; diff --git a/repos/os/src/server/vmm/virtio_device.cc b/repos/os/src/server/vmm/virtio_device.cc deleted file mode 100644 index a780e4c1ba..0000000000 --- a/repos/os/src/server/vmm/virtio_device.cc +++ /dev/null @@ -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 -#include - -#include - -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); -} diff --git a/repos/os/src/server/vmm/virtio_device.h b/repos/os/src/server/vmm/virtio_device.h index 63fd3b0d0b..1185439dee 100644 --- a/repos/os/src/server/vmm/virtio_device.h +++ b/repos/os/src/server/vmm/virtio_device.h @@ -1,6 +1,7 @@ /* * \brief Generic and simple virtio device * \author Sebastian Sumpf + * \author Stefan Kalkowski * \date 2019-10-10 */ @@ -14,6 +15,7 @@ #ifndef _VIRTIO_DEVICE_H_ #define _VIRTIO_DEVICE_H_ +#include #include #include @@ -22,522 +24,399 @@ #include namespace Vmm { - class Virtio_device; - class Virtio_queue; - struct Virtio_queue_data; - class Virtio_descriptor; - class Virtio_avail; - class Virtio_used; + class Virtio_split_queue; + template class Virtio_device; + + using namespace Genode; } -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
(); } - size_t length () const { return read(); } - uint16_t flags() const { return read(); } - uint16_t next() const { return read(); } -}; - - -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() & 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 */ -class Vmm::Virtio_queue +class Vmm::Virtio_split_queue { - private: + public: - Virtio_queue_data &_data; - Ram &_ram; + static constexpr unsigned MAX_SIZE_LOG2 = 9; + static constexpr unsigned MAX_SIZE = 1 << MAX_SIZE_LOG2; - 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) }; + protected: - uint16_t _idx { 0 }; - uint32_t _length { _data.num }; - bool _tx { _data.tx }; - bool const _verbose { false }; + template + 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; + + + 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(); } + }; + + + struct Avail_queue : Queue_base + { + using Queue_base::Queue_base; + + struct Ring : Register_array<0x4, 16, MAX_SIZE, 16> { }; + + bool inject_irq() { return (read() & 1) == 0; } + + Descriptor_index get(Ring_index id) + { + unsigned v = read(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(0); + Ring::access_t elem = 0; + Ring::Id::set(elem, di.idx()); + Ring::Length::set(elem, size); + write(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
(); } + size_t length () const { return read(); } + uint16_t flags() const { return read(); } + uint16_t next() const { return read(); } + }; + + + 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: - Virtio_queue(Virtio_queue_data &data, Ram &ram) - : _data(data), _ram(ram) { } + Virtio_split_queue(addr_t const descriptor_area, + 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 + bool notify(FUNC func) + { + memory_barrier(); - bool verbose() const { return _verbose; } + bool written = false; - template - bool notify(FUNC func) - { - asm volatile ("dmb ish" : : : "memory"); - uint16_t used_idx = _used.read(); - uint16_t avail_idx = _avail.read(); + for (Ring_index avail_idx = _avail.current(); + _cur_idx != avail_idx; _cur_idx.inc()) { - 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) - 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()); + if (!address || !size) { break; } - 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(_idx % _length); + if (written) { + _used.write(_cur_idx.idx()); + memory_barrier(); + } - /* 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 { - data = _ram.local_address(address, length); - length = func(data, length); - } catch (...) { break; } - - if (length == 0) break; - - _used.write(0); - Virtio_used::Elem::access_t elem = 0; - Virtio_used::Elem::Id::set(elem, id); - Virtio_used::Elem::Length::set(elem, length); - _used.write(elem, _idx % _length); - written++; _idx++; - - if (used_idx + written == avail_idx) break; + return written && _avail.inject_irq(); } - - if (written) { - used_idx += written; - _used.write(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 class Vmm::Virtio_device : public Vmm::Mmio_device { protected: - enum { RX = 0, TX = 1, NUM = 2 }; - Virtio_queue_data _data[NUM]; - uint32_t _current { RX }; - - Genode::Constructible _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(); - } + Gic::Irq & _irq; + Ram & _ram; + Genode::Mutex _mutex; + Genode::Constructible _queue[NUM]; virtual void _notify(unsigned idx) = 0; - virtual Register _device_specific_features() = 0; + /*************** ** Registers ** ***************/ - struct DeviceFeatures : Mmio_register + class Reg : public Mmio_register { - enum { - VIRTIO_F_VERSION_1 = 1, - }; + private: - Virtio_device &_device; - Mmio_register &_selector; + Virtio_device & _dev; - Register read(Address_range&, Cpu&) override - { - /* lower 32 bit */ - if (_selector.value() == 0) return _device._device_specific_features(); + public: - /* upper 32 bit */ - return VIRTIO_F_VERSION_1; - } + 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); } - DeviceFeatures(Virtio_device &device, Mmio_register &selector) - : Mmio_register("DeviceFeatures", Mmio_register::RO, 0x10, 4), - _device(device), _selector(selector) - { } - } _device_features { *this, _reg_container.regs[4] }; + Virtio_device & device() { return _dev; } + }; - struct DriverFeatures : Mmio_register + class Set : public Reg { - Mmio_register &_selector; - uint32_t _lower { 0 }; - uint32_t _upper { 0 }; + private: - void write(Address_range&, Cpu&, Register reg) override - { - if (_selector.value() == 0) { - _lower = reg; - } - _upper = reg; - } + Reg & _selector; + typename Reg::Register _regs[32] { 0 }; - DriverFeatures(Mmio_register &selector) - : Mmio_register("DriverFeatures", Mmio_register::WO, 0x20, 4), - _selector(selector) - { } - } _driver_features { _reg_container.regs[5] }; + public: - 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 value(); - } + return ((uint64_t)_descr_high.value()<<32) | _descr_low.value(); + } - void write(Address_range&, Cpu&, Register reg) override - { - set(reg); - } - - Status() - : Mmio_register("Status", Mmio_register::RW, 0x70, 4, 0) - { } - } _status; - - struct QueueSel : Mmio_register + uint64_t _driver_area() const { - Virtio_device &device; + return ((uint64_t)_driver_high.value()<<32) | _driver_low.value(); + } - 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) - : Mmio_register("QueueSel", Mmio_register::WO, 0x30, 4), - device(device) { } - } _queue_sel { *this }; - - struct QueueNum : Mmio_register + uint64_t _device_area() const { - Virtio_device &device; + return ((uint64_t)_device_high.value()<<32) | _device_low.value(); + } - void write(Address_range&, Cpu&, Register reg) override - { - 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 + void _assert_irq() { - Virtio_device &device; + _irq_status.set(0x1); + _irq.assert(); + } - Register read(Address_range&, Cpu&) override - { - Genode::Mutex::Guard guard(device.mutex()); - return device._queue_data().ready; - } + void _deassert_irq() + { + _irq_status.set(0); + _irq.deassert(); + } - 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); - } + void _construct_queue() + { + Genode::Mutex::Guard guard(mutex()); - QueueReady(Virtio_device &device) - : Mmio_register("QueueReady", Mmio_register::RW, 0x44, 4), - device(device) { } + unsigned num = _queue_sel.value(); + + 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 }; - struct QueueNotify : Mmio_register - { - Virtio_device &device; + struct Queue_notify : Reg + { void write(Address_range&, Cpu&, Register reg) override { - Genode::Mutex::Guard guard(device.mutex()); - if (!device._queue[reg].constructed()) return; + Genode::Mutex::Guard guard(Reg::device().mutex()); - 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) - : Mmio_register("QueueNotify", Mmio_register::WO, 0x50, 4), - device(device) { } + Queue_notify(Virtio_device & device) + : Reg(device, "QueueNotify", Mmio_register::WO, 0x50) { } } _queue_notify { *this }; - struct QueueDescrLow : Mmio_register - { - Virtio_device &device; + struct Interrupt_ack : Reg + { void write(Address_range&, Cpu&, Register reg) override { - Genode::Mutex::Guard guard(device.mutex()); - device._queue_data().descr_low = reg; + Genode::Mutex::Guard guard(Reg::device().mutex()); + Reg::device()._deassert_irq(); } - QueueDescrLow(Virtio_device &device) - : Mmio_register("QueuDescrLow", Mmio_register::WO, 0x80, 4), - 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(Virtio_device &device) + : Reg(device, "InterruptAck", Mmio_register::WO, 0x64) {} } _interrupt_ack { *this }; public: - Virtio_device(const char * const name, - const uint64_t addr, - const uint64_t size, - unsigned irq, - Cpu &cpu, - Mmio_bus &bus, - Ram &ram, - unsigned queue_size = 8); + 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, + 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; } }; diff --git a/repos/os/src/server/vmm/virtio_net.h b/repos/os/src/server/vmm/virtio_net.h index c411093447..1d8395bd3e 100644 --- a/repos/os/src/server/vmm/virtio_net.h +++ b/repos/os/src/server/vmm/virtio_net.h @@ -18,15 +18,15 @@ #include -namespace Vmm -{ - class Virtio_net; -} +namespace Vmm { class Virtio_net; } -class Vmm::Virtio_net : public Virtio_device + +class Vmm::Virtio_net : public Virtio_device { private: + enum Queue_id { RX, TX }; + Genode::Env &_env; 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); - 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), _nic.rx()->packet_content(rx_packet), sz - NIC_HEADER_SIZE); @@ -121,28 +117,24 @@ class Vmm::Virtio_net : public Virtio_device _rx(); } - Register _device_specific_features() override - { - enum { VIRTIO_NET_F_MAC = 1u << 5 }; - return VIRTIO_NET_F_MAC; - } + enum Device_id { NIC = 0x1 }; - 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; return mac.addr[range.start]; } - ConfigArea(Nic::Mac_address &mac) - : Mmio_register("ConfigArea", Mmio_register::RO, 0x100, 8), + Config_area(Virtio_net & device, Nic::Mac_address & mac) + : Reg(device, "ConfigArea", Mmio_register::RO, 0x100, 8), mac(mac) { } - } _config_area { _mac }; + } _config_area { *this, _mac }; public: @@ -154,14 +146,14 @@ class Vmm::Virtio_net : public Virtio_device Mmio_bus &bus, Ram &ram, Genode::Env &env) - : Virtio_device(name, addr, size, irq, cpu, bus, ram, 1024), - _env(env), - _handler(cpu, _env.ep(), *this, &Virtio_net::_handle) + : + Virtio_device(name, addr, size, + irq, cpu, bus, ram, NIC), + _env(env), + _handler(cpu, _env.ep(), *this, &Virtio_net::_handle) { - /* set device ID to network */ - _device_id(0x1); - - add(_config_area); + enum { VIRTIO_NET_F_MAC = 1u << 5 }; + _dev_feature.set(VIRTIO_NET_F_MAC); _nic.tx_channel()->sigh_ready_to_submit(_handler); _nic.tx_channel()->sigh_ack_avail (_handler);