mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-29 15:44:02 +00:00
vmm: ARMv8 virtio for network and console
Simple console and network implementation of the Virtio protocol. Fix #3620
This commit is contained in:
parent
c8b3b060aa
commit
f77531138a
@ -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 {
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
<start name="nic_drv">
|
||||
<binary name="nic_router" />
|
||||
<resource name="RAM" quantum="8M"/>
|
||||
<provides> <service name="Nic"/> </provides>
|
||||
<config>
|
||||
<default-policy domain="default"/>
|
||||
<domain name="default" interface="10.0.1.1/24">
|
||||
<dhcp-server ip_first="10.0.1.2" ip_last="10.0.1.2" />
|
||||
</domain>
|
||||
</config>
|
||||
</start>
|
||||
<start name="log_terminal">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<provides> <service name="Terminal"/> </provides>
|
||||
</start>
|
||||
<start name="terminal_crosslink">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides> <service name="Terminal"/> </provides>
|
||||
</start>
|
||||
<start name="vmm" caps="200" priority="-1">
|
||||
<resource name="RAM" quantum="256M"/>
|
||||
<route>
|
||||
<service name="Terminal" label="earlycon"> <child name="log_terminal"/> </service>
|
||||
<service name="Terminal"> <child name="terminal_crosslink"/> </service>
|
||||
<service name="Nic"> <child name="nic_drv"/> </service>
|
||||
<any-service><parent/><any-child/></any-service>
|
||||
</route>
|
||||
</start>
|
||||
<start name="vm">
|
||||
<binary name="test-terminal_expect_send"/>
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<config expect="/ #" send="ls" verbose="yes"/>
|
||||
<route>
|
||||
<service name="Terminal"> <child name="terminal_crosslink"/> </service>
|
||||
<any-service><parent/><any-child/></any-service>
|
||||
</route>
|
||||
</start>
|
||||
</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
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
82
repos/os/src/server/vmm/spec/arm_v8/virtio_console.h
Normal file
82
repos/os/src/server/vmm/spec/arm_v8/virtio_console.h
Normal file
@ -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 <terminal_session/connection.h>
|
||||
|
||||
#include <virtio_device.h>
|
||||
|
||||
namespace Vmm
|
||||
{
|
||||
class Virtio_console;
|
||||
}
|
||||
|
||||
class Vmm::Virtio_console : public Virtio_device
|
||||
{
|
||||
private:
|
||||
|
||||
Terminal::Connection _terminal;
|
||||
Cpu::Signal_handler<Virtio_console> _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_ */
|
57
repos/os/src/server/vmm/spec/arm_v8/virtio_device.cc
Normal file
57
repos/os/src/server/vmm/spec/arm_v8/virtio_device.cc
Normal file
@ -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 <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);
|
||||
}
|
501
repos/os/src/server/vmm/spec/arm_v8/virtio_device.h
Normal file
501
repos/os/src/server/vmm/spec/arm_v8/virtio_device.h
Normal file
@ -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 <util/mmio.h>
|
||||
#include <util/reconstructible.h>
|
||||
|
||||
#include <gic.h>
|
||||
#include <ram.h>
|
||||
#include <mmio.h>
|
||||
|
||||
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<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> { };
|
||||
};
|
||||
|
||||
|
||||
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 <typename FUNC>
|
||||
bool notify(FUNC func)
|
||||
{
|
||||
uint16_t used_idx = _used.read<Virtio_used::Idx>();
|
||||
uint16_t avail_idx = _avail.read<Virtio_avail::Idx>();
|
||||
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<Virtio_avail::Ring>(_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<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, id);
|
||||
written++; _idx++;
|
||||
|
||||
if (used_idx + written == avail_idx) break;
|
||||
}
|
||||
|
||||
_used.write<Virtio_used::Idx>(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<Virtio_queue> _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_ */
|
138
repos/os/src/server/vmm/spec/arm_v8/virtio_net.h
Normal file
138
repos/os/src/server/vmm/spec/arm_v8/virtio_net.h
Normal file
@ -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 <nic/packet_allocator.h>
|
||||
#include <nic_session/connection.h>
|
||||
|
||||
#include <virtio_device.h>
|
||||
|
||||
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<Virtio_net> _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_ */
|
@ -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);
|
||||
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include <cpu.h>
|
||||
#include <gic.h>
|
||||
#include <pl011.h>
|
||||
#include <virtio_console.h>
|
||||
#include <virtio_net.h>
|
||||
|
||||
#include <base/attached_ram_dataspace.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
@ -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<void>()};
|
||||
Genode::Heap _heap { _env.ram(), _env.rm() };
|
||||
@ -57,6 +59,8 @@ class Vmm::Vm
|
||||
Genode::Constructible<Ep> _eps[MAX_CPUS];
|
||||
Genode::Constructible<Cpu> _cpus[MAX_CPUS];
|
||||
Pl011 _uart;
|
||||
Virtio_console _virtio_console;
|
||||
Virtio_net _virtio_net;
|
||||
|
||||
void _load_kernel();
|
||||
void _load_dtb();
|
||||
|
Loading…
x
Reference in New Issue
Block a user