mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-21 10:01:57 +00:00
os: add VirtIO nic driver
The driver is faily simple and does not support fancy features like TCP checksum offloading or vlan filtering, but it is fully capable of running every Genode network based scenario I've tried. Its currently known to work on virt_qemu arm platforms and x86_64. Fix #3825
This commit is contained in:
parent
7fbb245710
commit
8d5005e03a
3
repos/os/recipes/pkg/drivers_nic-virt_qemu/README
Normal file
3
repos/os/recipes/pkg/drivers_nic-virt_qemu/README
Normal file
@ -0,0 +1,3 @@
|
||||
|
||||
Device drivers needed for scenarios
|
||||
using one network interface
|
4
repos/os/recipes/pkg/drivers_nic-virt_qemu/archives
Normal file
4
repos/os/recipes/pkg/drivers_nic-virt_qemu/archives
Normal file
@ -0,0 +1,4 @@
|
||||
_/raw/drivers_nic-virt_qemu
|
||||
_/src/virtdev_rom
|
||||
_/src/platform_drv
|
||||
_/src/virtio_nic_drv
|
1
repos/os/recipes/pkg/drivers_nic-virt_qemu/hash
Normal file
1
repos/os/recipes/pkg/drivers_nic-virt_qemu/hash
Normal file
@ -0,0 +1 @@
|
||||
2020-07-01 45d2ce0a7e352afa423739b9c1de5435511f30b0
|
4
repos/os/recipes/raw/drivers_nic-virt_qemu/content.mk
Normal file
4
repos/os/recipes/raw/drivers_nic-virt_qemu/content.mk
Normal file
@ -0,0 +1,4 @@
|
||||
content: drivers.config
|
||||
|
||||
drivers.config:
|
||||
cp $(REP_DIR)/recipes/raw/drivers_nic-virt_qemu/$@ $@
|
54
repos/os/recipes/raw/drivers_nic-virt_qemu/drivers.config
Normal file
54
repos/os/recipes/raw/drivers_nic-virt_qemu/drivers.config
Normal file
@ -0,0 +1,54 @@
|
||||
<config verbose="true">
|
||||
<parent-provides>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="ROM"/>
|
||||
<service name="PD"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="LOG"/>
|
||||
<service name="Timer"/>
|
||||
</parent-provides>
|
||||
|
||||
<default caps="100"/>
|
||||
|
||||
<service name="Nic">
|
||||
<default-policy> <child name="virtio_mmio_nic"/> </default-policy> </service>
|
||||
|
||||
<start name="virtdev_rom">
|
||||
<resource name="RAM" quantum="640K"/>
|
||||
<provides> <service name="ROM"/> </provides>
|
||||
<route> <any-service> <parent/> </any-service> </route>
|
||||
<config>
|
||||
<policy label_prefix="virtio_mmio_nic">
|
||||
<device name="nic0"/>
|
||||
</policy>
|
||||
</config>
|
||||
</start>
|
||||
|
||||
<start name="platform_drv">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<provides> <service name="Platform"/> </provides>
|
||||
<route>
|
||||
<service name="ROM" label="config">
|
||||
<child name="virtdev_rom"/>
|
||||
</service>
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="virtio_mmio_nic">
|
||||
<resource name="RAM" quantum="640K"/>
|
||||
<provides> <service name="Nic"/> </provides>
|
||||
<route>
|
||||
<service name="Platform">
|
||||
<child name="platform_drv"/>
|
||||
</service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
</route>
|
||||
</start>
|
||||
|
||||
</config>
|
1
repos/os/recipes/raw/drivers_nic-virt_qemu/hash
Normal file
1
repos/os/recipes/raw/drivers_nic-virt_qemu/hash
Normal file
@ -0,0 +1 @@
|
||||
2020-07-01 a13f65f854fef31aaca5c1183f4e80d47f7797ef
|
2
repos/os/recipes/src/virtio_nic_drv/content.mk
Normal file
2
repos/os/recipes/src/virtio_nic_drv/content.mk
Normal file
@ -0,0 +1,2 @@
|
||||
SRC_DIR = src/drivers/nic/virtio
|
||||
include $(GENODE_DIR)/repos/base/recipes/src/content.inc
|
1
repos/os/recipes/src/virtio_nic_drv/hash
Normal file
1
repos/os/recipes/src/virtio_nic_drv/hash
Normal file
@ -0,0 +1 @@
|
||||
2020-07-01-b 330640a1c3dddf5e18087e03155638fbd4bbd567
|
5
repos/os/recipes/src/virtio_nic_drv/used_apis
Normal file
5
repos/os/recipes/src/virtio_nic_drv/used_apis
Normal file
@ -0,0 +1,5 @@
|
||||
base
|
||||
os
|
||||
virtio
|
||||
nic_session
|
||||
platform_session
|
44
repos/os/src/drivers/nic/virtio/README
Normal file
44
repos/os/src/drivers/nic/virtio/README
Normal file
@ -0,0 +1,44 @@
|
||||
This directory contains the implementation of Genode's VirtIO NIC driver.
|
||||
|
||||
Brief
|
||||
=====
|
||||
|
||||
The driver implements virtual ethernet card driver as defined in section
|
||||
5.1 of Virtial I/O Device (VIRTIO) Version 1.0 specification. The device
|
||||
is exposed to the rest of the system via standard Genode NIC session
|
||||
interface.
|
||||
|
||||
This driver does not require, or utilize any advanced VirtIO ethernet
|
||||
device features such as VLAN filtering, or checksum offloading.
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
The following config illustrates how the driver is configured:
|
||||
|
||||
!<start name="virtio_nic">
|
||||
! <resource name="ram" quantum="640K"/>
|
||||
! <provides><service name="Nic"/></provides>
|
||||
! <config rx_queue_size="16" rx_buffer_size="2020"
|
||||
! tx_queue_size="16" tx_buffer_size="2020"
|
||||
! mac="aa:bb:cc:dd:ee:ff"
|
||||
! verbose="false" />
|
||||
!</start>
|
||||
|
||||
The rx_queue_size and tx_queue_size parameters define the maximum number
|
||||
buffers the transmit and receive virtio queues can hold. The buffers
|
||||
used by both queues are pre-allocated on device initialization. The
|
||||
rx_buffer_size and tx_buffer_size can be used to specify sizes of those
|
||||
pre-allocated buffers.
|
||||
|
||||
The default values of the presented configuration parameters the driver
|
||||
uses were selected to minimize driver memory usage without negatively
|
||||
affecting performance. Testing was done on Qemu arm_v8a, arm_v8a, and
|
||||
x86_64 machines. These values might not be suitable for more advanted
|
||||
usage scenarios.
|
||||
|
||||
The mac configuration parameter can be used to override the default
|
||||
device MAC address obtained from VirtIO configuration space.
|
||||
|
||||
The driver does produce some additional logs when verbose parameter is set
|
||||
to true.
|
523
repos/os/src/drivers/nic/virtio/component.h
Normal file
523
repos/os/src/drivers/nic/virtio/component.h
Normal file
@ -0,0 +1,523 @@
|
||||
/*
|
||||
* \brief VirtIO NIC driver component
|
||||
* \author Piotr Tworek
|
||||
* \date 2019-09-27
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Need to come before attached_rom_dataspace.h */
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/log.h>
|
||||
#include <base/signal.h>
|
||||
#include <irq_session/client.h>
|
||||
#include <nic/component.h>
|
||||
#include <root/component.h>
|
||||
#include <util/misc_math.h>
|
||||
#include <util/register.h>
|
||||
#include <virtio/queue.h>
|
||||
|
||||
namespace Virtio_nic {
|
||||
using namespace Genode;
|
||||
struct Main;
|
||||
class Root;
|
||||
class Session_component;
|
||||
}
|
||||
|
||||
|
||||
class Virtio_nic::Session_component : public Nic::Session_component
|
||||
{
|
||||
private:
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Session_component(Session_component const &);
|
||||
Session_component &operator = (Session_component const &);
|
||||
|
||||
struct Unsupported_version : Genode::Exception { };
|
||||
struct Device_init_failed : Genode::Exception { };
|
||||
struct Features_init_failed : Genode::Exception { };
|
||||
struct Queue_init_failed : Genode::Exception { };
|
||||
|
||||
struct Hardware_features
|
||||
{
|
||||
Nic::Mac_address mac = { };
|
||||
bool link_status_available = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* See section 5.1.6 of VirtIO 1.0 specification.
|
||||
*/
|
||||
struct Virtio_net_header
|
||||
{
|
||||
enum Flags : Genode::uint16_t
|
||||
{
|
||||
NEEDS_CSUM = 1,
|
||||
};
|
||||
|
||||
enum GSO : Genode::uint16_t
|
||||
{
|
||||
NONE = 0,
|
||||
TCPV4 = 1,
|
||||
UDP = 3,
|
||||
TCPV6 = 4,
|
||||
ECN = 0x80,
|
||||
};
|
||||
|
||||
uint8_t flags = 0;
|
||||
uint8_t gso_type = GSO::NONE;
|
||||
uint16_t hdr_len = 0;
|
||||
uint16_t gso_size = 0;
|
||||
uint16_t csum_start = 0;
|
||||
uint16_t csum_offset = 0;
|
||||
uint16_t num_buffers = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* VirtIO feature bits relevant to this VirtIO net driver implementation.
|
||||
*/
|
||||
struct Features : Genode::Register<64>
|
||||
{
|
||||
struct CSUM : Bitfield<0, 1> { };
|
||||
struct GUEST_CSUM : Bitfield<1, 1> { };
|
||||
struct MTU : Bitfield<3, 1> { };
|
||||
struct MAC : Bitfield<5, 1> { };
|
||||
struct GSO : Bitfield<6, 1> { };
|
||||
struct GUEST_TSO4 : Bitfield<7, 1> { };
|
||||
struct GUEST_TSO6 : Bitfield<8, 1> { };
|
||||
struct GUEST_ECN : Bitfield<9, 1> { };
|
||||
struct GUEST_UFO : Bitfield<10, 1> { };
|
||||
struct HOST_TSO4 : Bitfield<11, 1> { };
|
||||
struct HOST_TSO6 : Bitfield<12, 1> { };
|
||||
struct HOST_ECN : Bitfield<13, 1> { };
|
||||
struct HOST_UFO : Bitfield<14, 1> { };
|
||||
struct MRG_RXBUF : Bitfield<15, 1> { };
|
||||
struct STATUS : Bitfield<16, 1> { };
|
||||
struct CTRL_VQ : Bitfield<17, 1> { };
|
||||
struct CTRL_RX : Bitfield<18, 1> { };
|
||||
struct CTRL_VLAN : Bitfield<19, 1> { };
|
||||
struct GUEST_ANNOUNCE : Bitfield<21, 1> { };
|
||||
struct MQ : Bitfield<22, 1> { };
|
||||
struct CTRL_MAC_ADDR : Bitfield<23, 1> { };
|
||||
struct EVENT_IDX : Bitfield<29, 1> { };
|
||||
struct VERSION_1 : Bitfield<32, 1> { };
|
||||
};
|
||||
|
||||
/**
|
||||
* See section 5.1.4 of VirtIO 1.0 specification.
|
||||
*/
|
||||
enum { CONFIG_MAC_BASE = 0, CONFIG_STATUS = 6 };
|
||||
enum { STATUS_LINK_UP = 1 << 0 };
|
||||
|
||||
/**
|
||||
* Available VirtIO queue numbers, see section 5.1.2 of VirtIO 1.0 specification.
|
||||
*/
|
||||
enum Vq_id : Genode::uint16_t { RX_VQ = 0, TX_VQ = 1 };
|
||||
|
||||
/**
|
||||
* Each VirtIO queue contains fixed number of buffers. The most common size of the buffer
|
||||
* is 1526 bytes (size of ethernet frame + Virtio_net_header). VirtIO queue size must be
|
||||
* a power of 2. Each VirtIO queue needs some additional space for the descriptor table,
|
||||
* available and used rings. The default VirtIO queue parameter values defined here have
|
||||
* been selected to make Ram_dataspace used by both TX and RX VirtIO queues consume around
|
||||
* 32Kb of RAM.
|
||||
*/
|
||||
static const uint16_t DEFAULT_VQ_SIZE = 16;
|
||||
static const uint16_t DEFAULT_VQ_BUF_SIZE = 2020;
|
||||
|
||||
struct Rx_queue_traits
|
||||
{
|
||||
static const bool device_write_only = true;
|
||||
static const bool has_data_payload = true;
|
||||
};
|
||||
|
||||
struct Tx_queue_traits
|
||||
{
|
||||
static const bool device_write_only = false;
|
||||
static const bool has_data_payload = true;
|
||||
};
|
||||
|
||||
typedef Virtio::Queue<Virtio_net_header, Rx_queue_traits> Rx_queue_type;
|
||||
typedef Virtio::Queue<Virtio_net_header, Tx_queue_traits> Tx_queue_type;
|
||||
typedef Genode::Signal_handler<Session_component> Signal_handler;
|
||||
|
||||
|
||||
bool const _verbose;
|
||||
Virtio::Device &_device;
|
||||
Hardware_features const _hw_features;
|
||||
Rx_queue_type _rx_vq;
|
||||
Tx_queue_type _tx_vq;
|
||||
Irq_session_client _irq;
|
||||
Signal_handler _irq_handler;
|
||||
bool _link_up = false;
|
||||
|
||||
|
||||
void _init_virtio_device()
|
||||
{
|
||||
using Status = Virtio::Device::Status;
|
||||
|
||||
if (!_device.set_status(Status::RESET)) {
|
||||
error("Failed to reset the device!");
|
||||
throw Device_init_failed();
|
||||
}
|
||||
|
||||
if (!_device.set_status(Status::ACKNOWLEDGE)) {
|
||||
error("Failed to acknowledge the device!");
|
||||
throw Device_init_failed();
|
||||
}
|
||||
|
||||
if (!_device.set_status(Status::DRIVER)) {
|
||||
_device.set_status(Status::FAILED);
|
||||
error("Device initialization failed!");
|
||||
throw Device_init_failed();
|
||||
}
|
||||
}
|
||||
|
||||
static Nic::Mac_address _read_mac_address(Virtio::Device &device)
|
||||
{
|
||||
Nic::Mac_address mac = {};
|
||||
|
||||
/* See section 2.3.1 of VirtIO 1.0 spec for rationale. */
|
||||
uint32_t before = 0, after = 0;
|
||||
do {
|
||||
before = device.get_config_generation();
|
||||
for (size_t idx = 0; idx < sizeof(mac.addr); ++idx) {
|
||||
mac.addr[idx] = device.read_config(
|
||||
CONFIG_MAC_BASE + idx, Virtio::Device::ACCESS_8BIT);
|
||||
}
|
||||
after = device.get_config_generation();
|
||||
} while (after != before);
|
||||
|
||||
return mac;
|
||||
}
|
||||
|
||||
Hardware_features _init_hw_features(Xml_node const &xml)
|
||||
{
|
||||
_init_virtio_device();
|
||||
|
||||
using Status = Virtio::Device::Status;
|
||||
|
||||
const uint32_t low = _device.get_features(0);
|
||||
const uint32_t high = _device.get_features(1);
|
||||
const Features::access_t device_features = ((uint64_t)high << 32) | low;
|
||||
Features::access_t driver_features = 0;
|
||||
|
||||
/* This driver does not support legacy VirtIO versions. */
|
||||
if (!Features::VERSION_1::get(device_features)) {
|
||||
error("Unsupprted VirtIO device version!");
|
||||
throw Features_init_failed();
|
||||
}
|
||||
Features::VERSION_1::set(driver_features);
|
||||
|
||||
Hardware_features hw_features;
|
||||
|
||||
if (Features::MAC::get(device_features)) {
|
||||
Features::MAC::set(driver_features);
|
||||
hw_features.mac = _read_mac_address(_device);
|
||||
}
|
||||
|
||||
hw_features.mac = xml.attribute_value("mac", hw_features.mac);
|
||||
|
||||
if (hw_features.mac == Nic::Mac_address()) {
|
||||
error("HW mac address missing and not provided via config!");
|
||||
throw Features_init_failed();
|
||||
}
|
||||
|
||||
if (Features::STATUS::get(device_features)) {
|
||||
Features::STATUS::set(driver_features);
|
||||
hw_features.link_status_available = true;
|
||||
}
|
||||
|
||||
_device.set_features(0, (uint32_t)driver_features);
|
||||
_device.set_features(1, (uint32_t)(driver_features >> 32));
|
||||
|
||||
if (!_device.set_status(Status::FEATURES_OK)) {
|
||||
_device.set_status(Status::FAILED);
|
||||
error("Device feature negotiation failed!");
|
||||
throw Features_init_failed();
|
||||
}
|
||||
|
||||
return hw_features;
|
||||
}
|
||||
|
||||
Genode::uint16_t _vq_size(Vq_id vq, Xml_node const &xml, char const *cfg_attr)
|
||||
{
|
||||
const uint16_t max_vq_size = _device.get_max_queue_size(vq);
|
||||
|
||||
if (max_vq_size == 0) {
|
||||
error("VirtIO queue ", (int)vq, " is not available!");
|
||||
throw Queue_init_failed();
|
||||
}
|
||||
|
||||
const uint16_t vq_size = Genode::min(xml.attribute_value(cfg_attr, DEFAULT_VQ_SIZE),
|
||||
max_vq_size);
|
||||
|
||||
if (_verbose)
|
||||
log("VirtIO queue ", (int)vq, " size: ", vq_size, " (max: ", max_vq_size, ")");
|
||||
|
||||
return vq_size;
|
||||
}
|
||||
|
||||
uint16_t _buf_size(Vq_id vq, Xml_node const &xml, char const *cfg_attr)
|
||||
{
|
||||
const uint16_t vq_buf_size = xml.attribute_value(cfg_attr, DEFAULT_VQ_BUF_SIZE );
|
||||
if (_verbose)
|
||||
log("VirtIO queue ", (int)vq, " buffer size: ", Number_of_bytes(vq_buf_size), "b");
|
||||
return vq_buf_size;
|
||||
}
|
||||
|
||||
void _setup_virtio_queues()
|
||||
{
|
||||
if (!_device.configure_queue(RX_VQ, _rx_vq.description())) {
|
||||
error("Failed to initialize rx VirtIO queue!");
|
||||
throw Queue_init_failed();
|
||||
}
|
||||
|
||||
if (!_device.configure_queue(TX_VQ, _tx_vq.description())) {
|
||||
error("Failed to initialize tx VirtIO queue!");
|
||||
throw Queue_init_failed();
|
||||
}
|
||||
|
||||
using Status = Virtio::Device::Status;
|
||||
if (!_device.set_status(Status::DRIVER_OK)) {
|
||||
_device.set_status(Status::FAILED);
|
||||
error("Failed to initialize VirtIO queues!");
|
||||
throw Queue_init_failed();
|
||||
}
|
||||
}
|
||||
|
||||
void _handle_irq()
|
||||
{
|
||||
const uint32_t reasons = _device.read_isr();
|
||||
|
||||
enum { IRQ_USED_RING_UPDATE = 1, IRQ_CONFIG_CHANGE = 2 };
|
||||
|
||||
if (_tx_vq.has_used_buffers())
|
||||
_tx_vq.ack_all_transfers();
|
||||
|
||||
if (reasons & IRQ_USED_RING_UPDATE) {
|
||||
_receive();
|
||||
}
|
||||
|
||||
if ((reasons & IRQ_CONFIG_CHANGE) && _hw_features.link_status_available &&
|
||||
(link_state() != _link_up)) {
|
||||
_link_up = !_link_up;
|
||||
if (_verbose)
|
||||
log("Link status changed: ", (_link_up ? "on-line" : "off-line"));
|
||||
_link_state_changed();
|
||||
}
|
||||
|
||||
_irq.ack_irq();
|
||||
}
|
||||
|
||||
bool _send()
|
||||
{
|
||||
if (!_tx.sink()->ready_to_ack())
|
||||
return false;
|
||||
|
||||
if (!_tx.sink()->packet_avail())
|
||||
return false;
|
||||
|
||||
auto packet = _tx.sink()->get_packet();
|
||||
if (!packet.size() || !_tx.sink()->packet_valid(packet)) {
|
||||
warning("Invalid tx packet");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (link_state()) {
|
||||
Virtio_net_header hdr;
|
||||
auto const *data = _tx.sink()->packet_content(packet);
|
||||
if (!_tx_vq.write_data(hdr, data, packet.size(), false)) {
|
||||
warning("Failed to push packet into tx VirtIO queue!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_tx.sink()->acknowledge_packet(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
void _receive()
|
||||
{
|
||||
auto rcv_func = [&] (Virtio_net_header const &,
|
||||
char const *data,
|
||||
size_t size) {
|
||||
if (!_rx.source()->ready_to_submit()) {
|
||||
Genode::warning("Not ready to submit!");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
auto p = _rx.source()->alloc_packet(size);
|
||||
char *dst = _rx.source()->packet_content(p);
|
||||
Genode::memcpy(dst, data, size);
|
||||
_rx.source()->submit_packet(p);
|
||||
} catch (Session::Rx::Source::Packet_alloc_failed) {
|
||||
Genode::warning("Packet alloc failed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
while (_rx_vq.has_used_buffers())
|
||||
_rx_vq.read_data(rcv_func);
|
||||
|
||||
/**
|
||||
* Inform the device the buffers we've just consumed are ready
|
||||
* to used again
|
||||
*/
|
||||
_device.notify_buffers_available(RX_VQ);
|
||||
}
|
||||
|
||||
void _handle_packet_stream() override
|
||||
{
|
||||
while (_rx.source()->ack_avail())
|
||||
_rx.source()->release_packet(_rx.source()->get_acked_packet());
|
||||
|
||||
/* Reclaim all buffers processed by the device. */
|
||||
if (_tx_vq.has_used_buffers())
|
||||
_tx_vq.ack_all_transfers();
|
||||
|
||||
bool sent_packets = false;
|
||||
while (_send())
|
||||
sent_packets = true;
|
||||
|
||||
if (sent_packets) {
|
||||
_device.notify_buffers_available(TX_VQ);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
bool link_state() override
|
||||
{
|
||||
/**
|
||||
* According to docs when STATUS feature is not available or has not
|
||||
* been negotiated the driver should assume the link is always active.
|
||||
* See section 5.1.4.2 of VIRTIO 1.0 specification.
|
||||
*/
|
||||
if (!_hw_features.link_status_available)
|
||||
return true;
|
||||
|
||||
uint32_t before = 0, after = 0;
|
||||
uint8_t status = 0;
|
||||
do {
|
||||
before = _device.get_config_generation();
|
||||
status = _device.read_config(CONFIG_STATUS, Virtio::Device::ACCESS_8BIT);
|
||||
after = _device.get_config_generation();
|
||||
} while (after != before);
|
||||
|
||||
return status & STATUS_LINK_UP;
|
||||
}
|
||||
|
||||
Nic::Mac_address mac_address() override { return _hw_features.mac; }
|
||||
|
||||
Session_component(Genode::Env &env,
|
||||
Genode::Allocator &rx_block_md_alloc,
|
||||
Virtio::Device &device,
|
||||
Irq_session_capability irq_cap,
|
||||
Genode::Xml_node const &xml,
|
||||
Genode::size_t const tx_buf_size,
|
||||
Genode::size_t const rx_buf_size)
|
||||
try : Nic::Session_component(tx_buf_size, rx_buf_size, Genode::CACHED,
|
||||
rx_block_md_alloc, env),
|
||||
_verbose(xml.attribute_value("verbose", false)),
|
||||
_device(device),
|
||||
_hw_features(_init_hw_features(xml)),
|
||||
_rx_vq(env.ram(), env.rm(),
|
||||
_vq_size(RX_VQ, xml, "rx_queue_size"),
|
||||
_buf_size(RX_VQ, xml, "rx_buffer_size")),
|
||||
_tx_vq(env.ram(), env.rm(),
|
||||
_vq_size(TX_VQ, xml, "tx_queue_size"),
|
||||
_buf_size(TX_VQ, xml, "tx_buffer_size")),
|
||||
_irq(irq_cap),
|
||||
_irq_handler(env.ep(), *this, &Session_component::_handle_irq),
|
||||
_link_up(link_state())
|
||||
{
|
||||
_setup_virtio_queues();
|
||||
_irq.sigh(_irq_handler);
|
||||
_irq.ack_irq();
|
||||
|
||||
_link_state_changed();
|
||||
|
||||
if (_verbose)
|
||||
Genode::log("Mac address: ", mac_address());
|
||||
}
|
||||
catch (Tx_queue_type::Invalid_buffer_size)
|
||||
{
|
||||
error("Invalid TX VirtIO queue buffer size specified!");
|
||||
throw;
|
||||
}
|
||||
catch (Rx_queue_type::Invalid_buffer_size)
|
||||
{
|
||||
error("Invalid RX VirtIO queue buffer size specified!");
|
||||
throw;
|
||||
}
|
||||
|
||||
~Session_component() {
|
||||
_device.set_status(Virtio::Device::Status::RESET);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Virtio_nic::Root : public Genode::Root_component<Session_component, Genode::Single_client>
|
||||
{
|
||||
private:
|
||||
|
||||
struct Device_not_found : Genode::Exception { };
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Root(Root const &) = delete;
|
||||
Root &operator = (Root const &) = delete;
|
||||
|
||||
Genode::Env &_env;
|
||||
Virtio::Device &_device;
|
||||
Irq_session_capability _irq_cap;
|
||||
|
||||
Session_component *_create_session(const char *args) override
|
||||
{
|
||||
size_t ram_quota = Arg_string::find_arg(args, "ram_quota" ).ulong_value(0);
|
||||
size_t tx_buf_size = Arg_string::find_arg(args, "tx_buf_size").ulong_value(0);
|
||||
size_t rx_buf_size = Arg_string::find_arg(args, "rx_buf_size").ulong_value(0);
|
||||
|
||||
/*
|
||||
* Check if donated ram quota suffices for both communication
|
||||
* buffers and check for overflow
|
||||
*/
|
||||
if (tx_buf_size + rx_buf_size < tx_buf_size ||
|
||||
tx_buf_size + rx_buf_size > ram_quota) {
|
||||
error("insufficient 'ram_quota', got ", ram_quota, ", "
|
||||
"need ", tx_buf_size + rx_buf_size);
|
||||
throw Genode::Insufficient_ram_quota();
|
||||
}
|
||||
|
||||
Attached_rom_dataspace rom(_env, "config");
|
||||
|
||||
try {
|
||||
return new (md_alloc()) Session_component(
|
||||
_env, *md_alloc(), _device, _irq_cap, rom.xml(),
|
||||
tx_buf_size, rx_buf_size);
|
||||
} catch (...) { throw Service_denied(); }
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Root(Env &env,
|
||||
Allocator &md_alloc,
|
||||
Virtio::Device &device,
|
||||
Irq_session_capability irq_cap)
|
||||
: Root_component<Session_component, Genode::Single_client>(env.ep(), md_alloc),
|
||||
_env(env), _device(device), _irq_cap(irq_cap)
|
||||
{ }
|
||||
};
|
32
repos/os/src/drivers/nic/virtio/config.xsd
Normal file
32
repos/os/src/drivers/nic/virtio/config.xsd
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
|
||||
<xs:include schemaLocation="base_types.xsd"/>
|
||||
<xs:include schemaLocation="net_types.xsd"/>
|
||||
<xs:include schemaLocation="virtio_types.xsd"/>
|
||||
|
||||
<xs:simpleType name="RxBufferSize">
|
||||
<xs:restriction base="xs:unsignedShort">
|
||||
<xs:minInclusive value="1526" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="TxBufferSize">
|
||||
<xs:restriction base="xs:unsignedShort">
|
||||
<xs:minInclusive value="256" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:element name="config">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="verbose" type="Boolean" />
|
||||
<xs:attribute name="mac" type="Mac_address" />
|
||||
<xs:attribute name="rx_queue_size" type="Virtio_queue_size" />
|
||||
<xs:attribute name="tx_queue_size" type="Virtio_queue_size" />
|
||||
<xs:attribute name="tx_buffer_size" type="TxBufferSize" />
|
||||
<xs:attribute name="rx_buffer_size" type="RxBufferSize" />
|
||||
</xs:complexType>
|
||||
</xs:element><!-- config -->
|
||||
|
||||
</xs:schema>
|
||||
|
86
repos/os/src/drivers/nic/virtio/mmio_device.cc
Normal file
86
repos/os/src/drivers/nic/virtio/mmio_device.cc
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* \brief VirtIO MMIO NIC driver
|
||||
* \author Piotr Tworek
|
||||
* \date 2019-09-27
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <platform_session/connection.h>
|
||||
#include <virtio/mmio_device.h>
|
||||
|
||||
#include "component.h"
|
||||
|
||||
namespace Virtio_mmio_nic {
|
||||
using namespace Genode;
|
||||
struct Main;
|
||||
}
|
||||
|
||||
struct Virtio_mmio_nic::Main
|
||||
{
|
||||
struct Device_not_found : Genode::Exception { };
|
||||
|
||||
struct Device_info {
|
||||
using String = Genode::String<64>;
|
||||
|
||||
String name {};
|
||||
size_t io_mem_offset = 0;
|
||||
|
||||
Device_info(Platform::Connection & platform) {
|
||||
bool found = false;
|
||||
platform.with_xml([&] (Xml_node & xml) {
|
||||
xml.for_each_sub_node("device", [&] (Xml_node node) {
|
||||
if (found) return;
|
||||
|
||||
node.for_each_sub_node("property", [&] (Xml_node node) {
|
||||
if ((node.attribute_value("name", String()) == "type") &&
|
||||
(node.attribute_value("value", String()) == "nic")) {
|
||||
found = true; }
|
||||
});
|
||||
|
||||
if (!found) return;
|
||||
|
||||
name = node.attribute_value("name", String());
|
||||
|
||||
node.for_each_sub_node("io_mem", [&] (Xml_node node) {
|
||||
io_mem_offset = node.attribute_value<size_t>("page_offset", 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
if (!found) {
|
||||
error("No device was found");
|
||||
throw Device_not_found();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Genode::Env &env;
|
||||
Genode::Heap heap { env.ram(), env.rm() };
|
||||
Platform::Connection platform { env };
|
||||
Device_info info { platform };
|
||||
Platform::Device_client platform_device { platform.acquire_device(info.name.string()) };
|
||||
Virtio::Device virtio_device { env, platform_device.io_mem_dataspace(),
|
||||
info.io_mem_offset };
|
||||
Virtio_nic::Root root { env, heap, virtio_device, platform_device.irq() };
|
||||
|
||||
Main(Env &env)
|
||||
try : env(env)
|
||||
{
|
||||
log("--- VirtIO MMIO NIC driver started ---");
|
||||
env.parent().announce(env.ep().manage(root));
|
||||
}
|
||||
catch (...) { env.parent().exit(-1); }
|
||||
};
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env)
|
||||
{
|
||||
static Virtio_mmio_nic::Main main(env);
|
||||
}
|
60
repos/os/src/drivers/nic/virtio/pci_device.cc
Normal file
60
repos/os/src/drivers/nic/virtio/pci_device.cc
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* \brief VirtIO PCI NIC driver
|
||||
* \author Piotr Tworek
|
||||
* \date 2019-09-27
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <platform_session/connection.h>
|
||||
#include <virtio/pci_device.h>
|
||||
|
||||
#include "component.h"
|
||||
|
||||
namespace Virtio_pci_nic {
|
||||
using namespace Genode;
|
||||
struct Main;
|
||||
}
|
||||
|
||||
struct Virtio_pci_nic::Main
|
||||
{
|
||||
struct Device_not_found : Genode::Exception { };
|
||||
|
||||
Genode::Env &env;
|
||||
Genode::Heap heap{ env.ram(), env.rm() };
|
||||
Platform::Connection pci { env };
|
||||
Platform::Device_client platform_device;
|
||||
Virtio::Device virtio_device { env, platform_device };
|
||||
Virtio_nic::Root root { env, heap, virtio_device, platform_device.irq(0) };
|
||||
|
||||
Platform::Device_capability find_platform_device()
|
||||
{
|
||||
Platform::Device_capability device_cap;
|
||||
pci.with_upgrade([&] () { device_cap = pci.first_device(); });
|
||||
|
||||
if (!device_cap.valid()) throw Device_not_found();
|
||||
|
||||
return device_cap;
|
||||
}
|
||||
|
||||
Main(Env &env)
|
||||
try : env(env), platform_device(find_platform_device())
|
||||
{
|
||||
log("--- VirtIO PCI driver started ---");
|
||||
env.parent().announce(env.ep().manage(root));
|
||||
}
|
||||
catch (...) { env.parent().exit(-1); }
|
||||
};
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env)
|
||||
{
|
||||
static Virtio_pci_nic::Main main(env);
|
||||
}
|
3
repos/os/src/drivers/nic/virtio/spec/arm/target.mk
Normal file
3
repos/os/src/drivers/nic/virtio/spec/arm/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
REQUIRES = arm
|
||||
|
||||
include $(REP_DIR)/src/drivers/nic/virtio/target_mmio.inc
|
3
repos/os/src/drivers/nic/virtio/spec/arm_64/target.mk
Normal file
3
repos/os/src/drivers/nic/virtio/spec/arm_64/target.mk
Normal file
@ -0,0 +1,3 @@
|
||||
REQUIRES = arm_64
|
||||
|
||||
include $(REP_DIR)/src/drivers/nic/virtio/target_mmio.inc
|
8
repos/os/src/drivers/nic/virtio/spec/x86/target.mk
Normal file
8
repos/os/src/drivers/nic/virtio/spec/x86/target.mk
Normal file
@ -0,0 +1,8 @@
|
||||
TARGET = virtio_pci_nic
|
||||
REQUIRES = x86
|
||||
SRC_CC = pci_device.cc
|
||||
LIBS = base
|
||||
INC_DIR = $(REP_DIR)/src/drivers/nic/virtio
|
||||
CONFIG_XSD = ../../config.xsd
|
||||
|
||||
vpath % $(REP_DIR)/src/drivers/nic/virtio
|
7
repos/os/src/drivers/nic/virtio/target_mmio.inc
Normal file
7
repos/os/src/drivers/nic/virtio/target_mmio.inc
Normal file
@ -0,0 +1,7 @@
|
||||
TARGET = virtio_mmio_nic
|
||||
SRC_CC = mmio_device.cc
|
||||
LIBS = base
|
||||
INC_DIR = $(REP_DIR)/src/drivers/nic/virtio
|
||||
CONFIG_XSD = ../../config.xsd
|
||||
|
||||
vpath % $(REP_DIR)/src/drivers/nic/virtio
|
@ -487,7 +487,7 @@ proc drivers_nic_pkg { } {
|
||||
if {[have_spec imx6q_sabrelite]} { return drivers_nic-imx6q_sabrelite }
|
||||
if {[have_spec imx7d_sabre]} { return drivers_nic-imx7d_sabre }
|
||||
if {[have_spec imx8q_evk]} { return drivers_nic-imx8q_evk }
|
||||
|
||||
if {[have_spec virt_qemu]} { return drivers_nic-virt_qemu }
|
||||
|
||||
puts stderr "drivers_nic package undefined for this build configuration"
|
||||
exit 1
|
||||
|
@ -29,7 +29,12 @@ proc append_qemu_nic_args { { extra_netdev_args "" } } {
|
||||
append qemu_args ",$extra_netdev_args"
|
||||
}
|
||||
|
||||
append qemu_args " -net nic,model=[qemu_nic_model],netdev=net0 "
|
||||
if {[have_spec virt_qemu]} {
|
||||
append qemu_args " -global virtio-mmio.force-legacy=false "
|
||||
append qemu_args " -device virtio-net-device,bus=virtio-mmio-bus.0,netdev=net0 "
|
||||
} else {
|
||||
append qemu_args " -net nic,model=[qemu_nic_model],netdev=net0 "
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user