From f77531138a28a1b2ecc631b8a4c33b35bd31689b Mon Sep 17 00:00:00 2001 From: Sebastian Sumpf Date: Mon, 21 Oct 2019 14:18:12 +0200 Subject: [PATCH] vmm: ARMv8 virtio for network and console Simple console and network implementation of the Virtio protocol. Fix #3620 --- repos/os/run/vmm_arm.run | 35 +- repos/os/src/server/vmm/spec/arm_v8/pl011.cc | 2 +- repos/os/src/server/vmm/spec/arm_v8/target.mk | 1 + repos/os/src/server/vmm/spec/arm_v8/virt.dts | 20 +- .../server/vmm/spec/arm_v8/virtio_console.h | 82 +++ .../server/vmm/spec/arm_v8/virtio_device.cc | 57 ++ .../server/vmm/spec/arm_v8/virtio_device.h | 501 ++++++++++++++++++ .../src/server/vmm/spec/arm_v8/virtio_net.h | 138 +++++ repos/os/src/server/vmm/spec/arm_v8/vm.cc | 4 +- repos/os/src/server/vmm/spec/arm_v8/vm.h | 6 +- 10 files changed, 838 insertions(+), 8 deletions(-) create mode 100644 repos/os/src/server/vmm/spec/arm_v8/virtio_console.h create mode 100644 repos/os/src/server/vmm/spec/arm_v8/virtio_device.cc create mode 100644 repos/os/src/server/vmm/spec/arm_v8/virtio_device.h create mode 100644 repos/os/src/server/vmm/spec/arm_v8/virtio_net.h diff --git a/repos/os/run/vmm_arm.run b/repos/os/run/vmm_arm.run index 6e3e5850b..c78cea858 100644 --- a/repos/os/run/vmm_arm.run +++ b/repos/os/run/vmm_arm.run @@ -15,6 +15,8 @@ set build_components { core init timer server/terminal_crosslink test/terminal_expect_send + server/log_terminal + server/nic_router server/vmm } build $build_components @@ -40,17 +42,42 @@ install_config { + + + + + + + + + + + + + + + + + + + + + + + + + } @@ -94,17 +121,17 @@ if { [have_spec arm_64] } { if {![file exists bin/linux]} { puts "Download linux kernel ..." - exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-19.11/linux-arm64-image-5.2 + exec >& /dev/null wget -c -O bin/linux http://genode.org/files/release-20.02/linux-arm64 } if {![file exists bin/dtb]} { puts "Download device tree blob ..." - exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-19.11/dtb-arm64-virt + exec >& /dev/null wget -c -O bin/dtb http://genode.org/files/release-20.02/dtb-arm64-virt } if {![file exists bin/initrd]} { puts "Download initramfs ..." - exec >& /dev/null wget -c -O bin/initrd http://genode.org/files/release-19.11/initrd-arm64 + exec >& /dev/null wget -c -O bin/initrd http://genode.org/files/release-20.02/initrd-arm64 } # @@ -151,6 +178,8 @@ set boot_modules { timer terminal_crosslink test-terminal_expect_send + nic_router + log_terminal vmm linux dtb diff --git a/repos/os/src/server/vmm/spec/arm_v8/pl011.cc b/repos/os/src/server/vmm/spec/arm_v8/pl011.cc index a9cd8f834..68defd9f5 100644 --- a/repos/os/src/server/vmm/spec/arm_v8/pl011.cc +++ b/repos/os/src/server/vmm/spec/arm_v8/pl011.cc @@ -83,7 +83,7 @@ Pl011::Pl011(const char * const name, Mmio_bus & bus, Genode::Env & env) : Mmio_device(name, addr, size), - _terminal(env), + _terminal(env, "earlycon"), _handler(cpu, env.ep(), *this, &Pl011::_read), _irq(cpu.gic().irq(irq)) { 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 797b883a9..9af3f0a6e 100644 --- a/repos/os/src/server/vmm/spec/arm_v8/target.mk +++ b/repos/os/src/server/vmm/spec/arm_v8/target.mk @@ -8,6 +8,7 @@ SRC_CC += gicv2.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) diff --git a/repos/os/src/server/vmm/spec/arm_v8/virt.dts b/repos/os/src/server/vmm/spec/arm_v8/virt.dts index 92a0110c8..bda1efddd 100755 --- a/repos/os/src/server/vmm/spec/arm_v8/virt.dts +++ b/repos/os/src/server/vmm/spec/arm_v8/virt.dts @@ -45,9 +45,10 @@ }; chosen { - bootargs = "rdinit=/bin/sh"; + /* bootargs = "rdinit=/bin/sh console=hvc0 earlycon=pl011,0x9000000"; */ + bootargs = "init=/sbin/init ip=dhcp console=hvc0"; linux,initrd-start = <0x42000000>; - linux,initrd-end = <0x42113b86>; + linux,initrd-end = <0x420aa539>; stdout-path = "/pl011@9000000"; }; @@ -62,4 +63,19 @@ #interrupt-cells = <0x03>; #size-cells = <0x02>; }; + + + virtio_mmio@a000000 { + interrupts = <0x00 0x10 0x01>; + compatible = "virtio,mmio"; + dma-coherent; + reg = <0x00 0xa000000 0x00 0x200>; + }; + + virtio_mmio@a000200 { + interrupts = <0x00 0x11 0x01>; + compatible = "virtio,mmio"; + dma-coherent; + reg = <0x00 0xa000200 0x00 0x200>; + }; }; diff --git a/repos/os/src/server/vmm/spec/arm_v8/virtio_console.h b/repos/os/src/server/vmm/spec/arm_v8/virtio_console.h new file mode 100644 index 000000000..3c4cd01b1 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/virtio_console.h @@ -0,0 +1,82 @@ +/* + * \brief Virtio console implementation + * \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. + */ + +#ifndef _VIRTIO_CONSOLE_H_ +#define _VIRTIO_CONSOLE_H_ + +#include + +#include + +namespace Vmm +{ + class Virtio_console; +} + +class Vmm::Virtio_console : public Virtio_device +{ + private: + + Terminal::Connection _terminal; + Cpu::Signal_handler _handler; + + void _read() + { + auto read = [&] (addr_t data, size_t size) + { + size_t length = _terminal.read((void *)data, size); + return length; + }; + + if (!_terminal.avail() || !_queue[RX].constructed()) return; + + _queue[RX]->notify(read); + _assert_irq(); + } + + void _notify(unsigned idx) override + { + if (idx != TX) return; + + auto write = [&] (addr_t data, size_t size) + { + _terminal.write((void *)data, size); + return size; + }; + + if (_queue[TX]->notify(write)) + _assert_irq(); + } + + 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) + { + /* set device ID to console */ + _device_id(0x3); + + _terminal.read_avail_sigh(_handler); + } +}; + +#endif /* _VIRTIO_CONSOLE_H_ */ diff --git a/repos/os/src/server/vmm/spec/arm_v8/virtio_device.cc b/repos/os/src/server/vmm/spec/arm_v8/virtio_device.cc new file mode 100644 index 000000000..a780e4c1b --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/virtio_device.cc @@ -0,0 +1,57 @@ +/* + * \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/spec/arm_v8/virtio_device.h b/repos/os/src/server/vmm/spec/arm_v8/virtio_device.h new file mode 100644 index 000000000..67a7c5967 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/virtio_device.h @@ -0,0 +1,501 @@ +/* + * \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. + */ + +#ifndef _VIRTIO_DEVICE_H_ +#define _VIRTIO_DEVICE_H_ + +#include +#include + +#include +#include +#include + +namespace Vmm { + class Virtio_device; + class Virtio_queue; + struct Virtio_queue_data; + class Virtio_descriptor; + 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 }; + + addr_t descr() const { return ((addr_t)descr_high << 32) | descr_low; } + addr_t driver() const { return ((addr_t)driver_high << 32) | driver_low; } + addr_t device() const { return ((addr_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)); + } + + addr_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> { }; +}; + + +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 +{ + private: + + Virtio_queue_data &_data; + Ram &_ram; + + 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 }; + + public: + + Virtio_queue(Virtio_queue_data &data, Ram &ram) + : _data(data), _ram(ram) { } + + template + bool notify(FUNC func) + { + uint16_t used_idx = _used.read(); + uint16_t avail_idx = _avail.read(); + uint16_t queue_idx = _tx ? avail_idx : used_idx + 1; + + uint16_t written = 0; + while (_idx != queue_idx && written < _length) { + uint16_t id = _avail.read(_idx % _length); + + /* make sure id stays in ring */ + id %= _length; + + Virtio_descriptor descr = _descr.index(id); + addr_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, id); + written++; _idx++; + + if (used_idx + written == avail_idx) break; + } + + _used.write(used_idx + written); + + return written > 0; + } +}; + + +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; + + 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; + + + /*************** + ** Registers ** + ***************/ + + struct DeviceFeatures : Mmio_register + { + enum { + VIRTIO_F_VERSION_1 = 1, + }; + + Mmio_register &_selector; + + Register read(Address_range&, Cpu&) override + { + /* lower 32 bit */ + if (_selector.value() == 0) return 0; + + /* upper 32 bit */ + return VIRTIO_F_VERSION_1; + } + + DeviceFeatures(Mmio_register &selector) + : Mmio_register("DeviceFeatures", Mmio_register::RO, 0x10, 4), + _selector(selector) + { } + } _device_features { _reg_container.regs[4] }; + + struct DriverFeatures : Mmio_register + { + Mmio_register &_selector; + 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) + : 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 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 + { + Virtio_device &device; + + void write(Address_range&, Cpu&, Register reg) override + { + 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; + + void write(Address_range&, Cpu&, Register reg) override + { + 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; + + Register read(Address_range&, Cpu&) override + { + return device._queue_data().ready; + } + + void write(Address_range&, Cpu&, Register reg) override + { + 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 }; + + struct QueueNotify : Mmio_register + { + Virtio_device &device; + + void write(Address_range&, Cpu&, Register reg) override + { + if (!device._queue[reg].constructed()) return; + + device._notify(reg); + } + + QueueNotify(Virtio_device &device) + : Mmio_register("QueueNotify", Mmio_register::WO, 0x50, 4), + device(device) { } + } _queue_notify { *this }; + + struct QueueDescrLow : Mmio_register + { + Virtio_device &device; + + void write(Address_range&, Cpu&, Register reg) override + { + device._queue_data().descr_low = reg; + } + + 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 + { + 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 + { + 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 + { + 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 + { + 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 + { + 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 + { + device._deassert_irq(); + } + + InterruptAck(Virtio_device &device) + : Mmio_register("InterruptAck", Mmio_register::WO, 0x64, 4), + device(device) { } + } _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); +}; + +#endif /* _VIRTIO_DEVICE_H_ */ diff --git a/repos/os/src/server/vmm/spec/arm_v8/virtio_net.h b/repos/os/src/server/vmm/spec/arm_v8/virtio_net.h new file mode 100644 index 000000000..9ee1a7c98 --- /dev/null +++ b/repos/os/src/server/vmm/spec/arm_v8/virtio_net.h @@ -0,0 +1,138 @@ +/* + * \brief Virtio networking implementation + * \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. + */ +#ifndef _VIRTIO_NET_H_ +#define _VIRTIO_NET_H_ + +#include +#include + +#include + +namespace Vmm +{ + class Virtio_net; +} + +class Vmm::Virtio_net : public Virtio_device +{ + private: + + Genode::Env &_env; + + Genode::Heap _heap { _env.ram(), _env.rm() }; + Genode::Allocator_avl _tx_alloc { &_heap }; + + enum { BUF_SIZE = Nic::Packet_allocator::DEFAULT_PACKET_SIZE * 128, + NIC_HEADER_SIZE = 12 }; + + Nic::Connection _nic { _env, &_tx_alloc, BUF_SIZE, BUF_SIZE }; + + Cpu::Signal_handler _handler; + + void _free_packets() + { + while (_nic.tx()->ack_avail()) { + Nic::Packet_descriptor packet = _nic.tx()->get_acked_packet(); + _nic.tx()->release_packet(packet); + } + } + + void _rx() + { + /* RX */ + auto recv = [&] (addr_t data, size_t size) + { + Nic::Packet_descriptor const rx_packet = _nic.rx()->get_packet(); + + size_t sz = Genode::min(size, rx_packet.size() + NIC_HEADER_SIZE); + Genode::memcpy((void *)(data + NIC_HEADER_SIZE), + _nic.rx()->packet_content(rx_packet), + sz); + _nic.rx()->acknowledge_packet(rx_packet); + + return sz; + }; + + if (!_queue[RX].constructed()) return; + + bool progress = false; + while (_nic.rx()->packet_avail() && _nic.rx()->ready_to_ack()) { + if (!_queue[RX]->notify(recv)) break; + progress = true; + } + + if (progress) _assert_irq(); + } + + void _tx() + { + auto send = [&] (addr_t data, size_t size) + { + if (!_nic.tx()->ready_to_submit()) return 0lu; + + data += NIC_HEADER_SIZE; size -= NIC_HEADER_SIZE; + + Nic::Packet_descriptor tx_packet; + try { + tx_packet = _nic.tx()->alloc_packet(size); } + catch (Nic::Session::Tx::Source::Packet_alloc_failed) { + return 0lu; } + + Genode::memcpy(_nic.tx()->packet_content(tx_packet), + (void *)data, size); + _nic.tx()->submit_packet(tx_packet); + return size; + }; + + if (!_queue[TX].constructed()) return; + + if (_queue[TX]->notify(send)) _assert_irq(); + _free_packets(); + } + + void _handle() + { + _rx(); + _tx(); + } + + void _notify(unsigned /* idx */) override + { + _rx(); + _tx(); + } + + public: + + Virtio_net(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, 128), + _env(env), + _handler(cpu, _env.ep(), *this, &Virtio_net::_handle) + { + /* set device ID to network */ + _device_id(0x1); + + _nic.tx_channel()->sigh_ready_to_submit(_handler); + _nic.tx_channel()->sigh_ack_avail (_handler); + _nic.rx_channel()->sigh_ready_to_ack (_handler); + _nic.rx_channel()->sigh_packet_avail (_handler); + } +}; +#endif /* _VIRTIO_NET_H_ */ diff --git a/repos/os/src/server/vmm/spec/arm_v8/vm.cc b/repos/os/src/server/vmm/spec/arm_v8/vm.cc index abe2e8e35..26093ddfc 100644 --- a/repos/os/src/server/vmm/spec/arm_v8/vm.cc +++ b/repos/os/src/server/vmm/spec/arm_v8/vm.cc @@ -49,7 +49,9 @@ Vmm::Cpu & Vm::boot_cpu() Vm::Vm(Genode::Env & env) : _env(env), _gic("Gicv3", 0x8000000, 0x10000, _bus, env), - _uart("Pl011", 0x9000000, 0x1000, 33, boot_cpu(), _bus, env) + _uart("Pl011", 0x9000000, 0x1000, 33, boot_cpu(), _bus, env), + _virtio_console("HVC", 0xa000000, 0x200, 48, boot_cpu(), _bus, _ram, env), + _virtio_net("Net", 0xa000200, 0x200, 49, boot_cpu(), _bus, _ram, env) { _vm.attach(_vm_ram.cap(), RAM_ADDRESS); diff --git a/repos/os/src/server/vmm/spec/arm_v8/vm.h b/repos/os/src/server/vmm/spec/arm_v8/vm.h index b1b78cbaf..29c76523c 100644 --- a/repos/os/src/server/vmm/spec/arm_v8/vm.h +++ b/repos/os/src/server/vmm/spec/arm_v8/vm.h @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -48,7 +50,7 @@ class Vmm::Vm Genode::Attached_rom_dataspace _dtb_rom { _env, "dtb" }; Genode::Attached_rom_dataspace _initrd_rom { _env, "initrd" }; Genode::Attached_ram_dataspace _vm_ram { _env.ram(), _env.rm(), - RAM_SIZE, Genode::UNCACHED }; + RAM_SIZE, Genode::CACHED }; Ram _ram { RAM_ADDRESS, RAM_SIZE, (Genode::addr_t)_vm_ram.local_addr()}; Genode::Heap _heap { _env.ram(), _env.rm() }; @@ -57,6 +59,8 @@ class Vmm::Vm Genode::Constructible _eps[MAX_CPUS]; Genode::Constructible _cpus[MAX_CPUS]; Pl011 _uart; + Virtio_console _virtio_console; + Virtio_net _virtio_net; void _load_kernel(); void _load_dtb();