Loading...
Searching...
No Matches
virtio-net
// vi:ft=cpp
/* SPDX-License-Identifier: MIT */
/*
* Copyright (C) 2022, 2024 Kernkonzept GmbH.
* Author(s): Stephan Gerhold <stephan.gerhold@kernkonzept.com>
*/
#pragma once
#include <cstring>
#include <functional>
#include <l4/cxx/exceptions>
#include <l4/cxx/minmax>
#include <l4/re/dataspace>
#include <l4/re/env>
#include <l4/re/error_helper>
#include <l4/re/util/unique_cap>
#include <l4/sys/consts.h>
#include <l4/l4virtio/client/l4virtio>
#include <l4/l4virtio/l4virtio>
#include <l4/l4virtio/virtio_net.h>
#include <l4/l4virtio/virtqueue>
namespace L4virtio { namespace Driver {
class Virtio_net_device : public L4virtio::Driver::Device
{
public:
struct Packet
{
l4virtio_net_header_t hdr;
l4_uint8_t data[1500 + 14]; /* MTU + Ethernet header */
};
int rx_queue_size() const
{ return max_queue_size(0); }
int tx_queue_size() const
{ return max_queue_size(1); }
void setup_device(L4::Cap<L4virtio::Device> srvcap)
{
// Contact device.
driver_connect(srvcap, false);
if (_config->device != L4VIRTIO_ID_NET)
L4Re::chksys(-L4_ENODEV, "Device is not a network device.");
if (_config->num_queues < 2)
L4Re::chksys(-L4_EINVAL, "Invalid number of queues reported.");
auto rxqsz = rx_queue_size();
auto txqsz = tx_queue_size();
// Allocate memory for RX/TX queue and RX/TX packet buffers
auto rxqoff = 0;
auto txqoff = l4_round_size(rxqoff + rxqsz * _rxq.total_size(rxqsz),
L4virtio::Virtqueue::Desc_align);
auto rxpktoff = l4_round_size(txqoff + txqsz * _txq.total_size(txqsz),
L4virtio::Virtqueue::Desc_align);
auto txpktoff = rxpktoff + rxqsz * sizeof(Packet);
auto totalsz = txpktoff + txqsz * sizeof(Packet);
_queue_ds = L4Re::chkcap(L4Re::Util::make_unique_cap<L4Re::Dataspace>(),
"Allocate queue dataspace capability");
auto *e = L4Re::Env::env();
L4Re::chksys(e->mem_alloc()->alloc(totalsz, _queue_ds.get(),
L4Re::Mem_alloc::Continuous
| L4Re::Mem_alloc::Pinned),
"Allocate memory for virtio structures");
L4Re::chksys(e->rm()->attach(&_queue_region, totalsz,
L4Re::Rm::F::Search_addr | L4Re::Rm::F::RW,
L4::Ipc::make_cap_rw(_queue_ds.get()), 0,
L4_PAGESHIFT),
"Attach dataspace for virtio structures");
l4_uint64_t devaddr;
L4Re::chksys(register_ds(_queue_ds.get(), 0, totalsz, &devaddr),
"Register queue dataspace with device");
_rxq.init_queue(rxqsz, _queue_region.get() + rxqoff);
_txq.init_queue(txqsz, _queue_region.get() + txqoff);
config_queue(0, rxqsz, devaddr + rxqoff,
devaddr + rxqoff + _rxq.avail_offset(),
devaddr + rxqoff + _rxq.used_offset());
config_queue(1, txqsz, devaddr + txqoff,
devaddr + txqoff + _txq.avail_offset(),
devaddr + txqoff + _txq.used_offset());
_rxpkts = reinterpret_cast< Packet*>(_queue_region.get() + rxpktoff);
_txpkts = reinterpret_cast< Packet*>(_queue_region.get() + txpktoff);
// Prepare descriptors to save work later
for (l4_uint16_t descno = 0; descno < rxqsz; ++descno)
{
auto &desc = _rxq.desc(descno);
desc.addr = L4virtio::Ptr<void>(devaddr + rxpktoff +
descno * sizeof(Packet));
desc.len = sizeof(Packet);
desc.flags.write() = 1;
}
for (l4_uint16_t descno = 0; descno < txqsz; ++descno)
{
auto &desc = _txq.desc(descno);
desc.addr = L4virtio::Ptr<void>(devaddr + txpktoff +
descno * sizeof(Packet));
desc.len = sizeof(Packet);
}
// Setup notification IRQ
_driver_notification_irq =
L4Re::chkcap(L4Re::Util::make_unique_cap<L4::Irq>(),
"Allocate notification capability");
L4Re::chksys(l4_error(e->factory()->create(_driver_notification_irq.get())),
"Create irq for notifications from device");
L4Re::chksys(_device->bind(0, _driver_notification_irq.get()),
"Bind driver notification interrupt");
// Finish handshake with device
l4virtio_set_feature(_config->driver_features_map,
L4VIRTIO_FEATURE_VERSION_1);
l4virtio_set_feature(_config->driver_features_map, L4VIRTIO_NET_F_MAC);
driver_acknowledge();
}
l4virtio_net_config_t const &device_config() const
{
return *_config->device_config<l4virtio_net_config_t>();
}
int bind_rx_notification_irq(L4::Cap<L4::Thread> thread, l4_umword_t label)
{
return l4_error(_driver_notification_irq->bind_thread(thread, label));
}
Packet &rx_pkt(l4_uint16_t descno)
{
if (descno >= _rxq.num())
throw L4::Bounds_error("Invalid used descriptor number in RX queue");
return _rxpkts[descno];
}
l4_uint16_t wait_rx(l4_uint32_t *len = nullptr)
{
l4_uint16_t descno;
// Wait until used descriptor becomes available.
for (;;)
{
descno = _rxq.find_next_used(len);
if (descno != Virtqueue::Eoq)
break;
L4Re::chksys(_driver_notification_irq->receive(), "Wait for RX");
}
if (len)
// Ensure that the length provided by the device in wait_for_next_used()
// is not larger than the buffer and subtract the length of the header.
*len = cxx::min(*len - sizeof(_rxpkts[0].hdr), sizeof(_rxpkts[0].data));
return descno;
}
void finish_rx(l4_uint16_t descno)
{
_rxq.free_descriptor(descno, descno);
}
void queue_rx()
{
l4_uint16_t descno;
while ((descno = _rxq.alloc_descriptor()) != Virtqueue::Eoq)
_rxq.enqueue_descriptor(descno);
notify(_rxq);
}
bool tx(std::function<l4_uint32_t(Packet&)> prepare)
{
auto descno = _txq.alloc_descriptor();
if (descno == Virtqueue::Eoq)
{
// Try again after cleaning old descriptors that have already been used
free_used_tx_descriptors();
descno = _txq.alloc_descriptor();
if (descno == Virtqueue::Eoq)
return false;
}
auto &pkt = _txpkts[descno];
auto &desc = _txq.desc(descno);
desc.len = sizeof(pkt.hdr) + prepare(pkt);
send(_txq, descno);
return true;
}
private:
void free_used_tx_descriptors()
{
l4_uint16_t used;
while ((used = _txq.find_next_used()) != Virtqueue::Eoq)
{
if (used >= _txq.num())
throw L4::Bounds_error("Invalid used descriptor number in TX queue");
_txq.free_descriptor(used, used);
}
}
private:
L4Re::Util::Unique_cap<L4Re::Dataspace> _queue_ds;
L4Re::Rm::Unique_region<l4_uint8_t *> _queue_region;
L4Re::Util::Unique_cap<L4::Irq> _driver_notification_irq;
L4virtio::Driver::Virtqueue _rxq, _txq;
Packet *_rxpkts, *_txpkts;
};
} }
L4Re::Env::env
static Env const * env() noexcept
Returns the initial environment for the current task.
Definition env:96
L4Re::Mem_alloc::Continuous
@ Continuous
Allocate physically contiguous memory.
Definition mem_alloc:64
L4Re::Mem_alloc::Pinned
@ Pinned
Deprecated, use L4Re::Dma_space instead.
Definition mem_alloc:65
L4Re::Rm::Unique_region
Unique region.
Definition rm:436
L4::Bounds_error
Access out of bounds.
Definition exceptions:279
L4::Cap
C++ interface for capabilities.
Definition capability.h:224
L4virtio::Driver::Device
Client-side implementation for a general virtio device.
Definition l4virtio:32
L4virtio::Driver::Device::max_queue_size
int max_queue_size(int num) const
Maximum queue size allowed by the device.
Definition l4virtio:230
L4virtio::Driver::Device::driver_connect
void driver_connect(L4::Cap< L4virtio::Device > srvcap, bool manage_notify=true)
Contacts the device and starts the initial handshake.
Definition l4virtio:56
L4virtio::Driver::Device::send
void send(Virtqueue &queue, l4_uint16_t descno)
Send a request to the device.
Definition l4virtio:312
L4virtio::Driver::Device::register_ds
int register_ds(L4::Cap< L4Re::Dataspace > ds, l4_umword_t offset, l4_umword_t size, l4_uint64_t *devaddr)
Share a dataspace with the device.
Definition l4virtio:196
L4virtio::Driver::Device::config_queue
int config_queue(int num, unsigned size, l4_uint64_t desc_addr, l4_uint64_t avail_addr, l4_uint64_t used_addr)
Send the virtqueue configuration to the device.
Definition l4virtio:212
L4virtio::Driver::Device::driver_acknowledge
int driver_acknowledge()
Finalize handshake with the device.
Definition l4virtio:156
L4virtio::Driver::Virtio_net_device
Simple class for accessing a virtio net device.
Definition virtio-net:31
L4virtio::Driver::Virtio_net_device::rx_queue_size
int rx_queue_size() const
Return the maximum receive queue size allowed by the device.
Definition virtio-net:47
L4virtio::Driver::Virtio_net_device::finish_rx
void finish_rx(l4_uint16_t descno)
Free an RX descriptor number to make it available for the RX queue again.
Definition virtio-net:230
L4virtio::Driver::Virtio_net_device::rx_pkt
Packet & rx_pkt(l4_uint16_t descno)
Return a reference to the RX packet buffer of the specified descriptor, e.g.
Definition virtio-net:180
L4virtio::Driver::Virtio_net_device::device_config
l4virtio_net_config_t const & device_config() const
Return a reference to the device configuration.
Definition virtio-net:158
L4virtio::Driver::Virtio_net_device::bind_rx_notification_irq
int bind_rx_notification_irq(L4::Cap< L4::Thread > thread, l4_umword_t label)
Bind the rx notification IRQ to the specified thread.
Definition virtio-net:169
L4virtio::Driver::Virtio_net_device::wait_rx
l4_uint16_t wait_rx(l4_uint32_t *len=nullptr)
Block until a network packet has been received from the device and return the descriptor number.
Definition virtio-net:202
L4virtio::Driver::Virtio_net_device::tx_queue_size
int tx_queue_size() const
Return the maximum transmit queue size allowed by the device.
Definition virtio-net:54
L4virtio::Driver::Virtio_net_device::setup_device
void setup_device(L4::Cap< L4virtio::Device > srvcap)
Establish a connection to the device and set up shared memory.
Definition virtio-net:66
L4virtio::Driver::Virtio_net_device::tx
bool tx(std::function< l4_uint32_t(Packet &)> prepare)
Attempt to allocate a descriptor in the TX queue and transmit the packet, after calling the prepare c...
Definition virtio-net:260
L4virtio::Driver::Virtio_net_device::queue_rx
void queue_rx()
Queue new available descriptors in the RX queue.
Definition virtio-net:238
L4virtio::Driver::Virtqueue
Driver-side implementation of a Virtqueue.
Definition virtqueue:471
L4virtio::Driver::Virtqueue::free_descriptor
void free_descriptor(l4_uint16_t head, l4_uint16_t tail)
Free a chained list of descriptors in the descriptor queue.
Definition virtqueue:631
L4virtio::Driver::Virtqueue::find_next_used
l4_uint16_t find_next_used(l4_uint32_t *len=nullptr)
Return the next finished block.
Definition virtqueue:609
L4virtio::Ptr
Pointer used in virtio descriptors.
Definition virtqueue:48
L4virtio::Virtqueue::num
unsigned num() const
Definition virtqueue:404
dataspace
Dataspace interface.
env
Environment interface.
error_helper
Error helper.
exceptions
Base exceptions.
l4_umword_t
unsigned long l4_umword_t
Unsigned machine word.
Definition l4int.h:40
l4_uint8_t
unsigned char l4_uint8_t
Unsigned 8bit value.
Definition l4int.h:25
l4_uint32_t
unsigned int l4_uint32_t
Unsigned 32bit value.
Definition l4int.h:29
l4_uint16_t
unsigned short int l4_uint16_t
Unsigned 16bit value.
Definition l4int.h:27
l4_uint64_t
unsigned long long l4_uint64_t
Unsigned 64bit value.
Definition l4int.h:31
L4_EINVAL
@ L4_EINVAL
Invalid argument.
Definition err.h:46
L4_ENODEV
@ L4_ENODEV
No such thing.
Definition err.h:44
l4_error
long l4_error(l4_msgtag_t tag) L4_NOTHROW
Get IPC error code if any or message tag label otherwise for an IPC call.
Definition ipc.h:646
L4_PAGESHIFT
#define L4_PAGESHIFT
Size of a page, log2-based.
Definition consts.h:26
l4_round_size
l4_addr_t l4_round_size(l4_addr_t value, unsigned char bits) L4_NOTHROW
Round value up to the next alignment with bits size.
Definition consts.h:484
l4virtio_set_feature
void l4virtio_set_feature(l4_uint32_t *feature_map, unsigned feat)
Set the given feature bit in a feature map.
Definition virtio.h:272
L4VIRTIO_FEATURE_VERSION_1
@ L4VIRTIO_FEATURE_VERSION_1
Virtio protocol version 1 supported. Must be 1 for L4virtio.
Definition virtio.h:101
L4VIRTIO_ID_NET
@ L4VIRTIO_ID_NET
Virtual ethernet card.
Definition virtio.h:64
consts.h
Common constants.
L4Re::Util::make_unique_cap
Unique_cap< T > make_unique_cap()
Allocate a capability slot and wrap it in an Unique_cap.
Definition unique_cap:55
L4Re::Util::Unique_cap
L4::Detail::Unique_cap_impl< T, Smart_cap_auto< L4_FP_ALL_SPACES > > Unique_cap
Unique capability that implements automatic free and unmap of the capability selector.
Definition unique_cap:43
L4Re::chksys
long chksys(long err, char const *extra="", long ret=0)
Generate C++ exception on error.
Definition error_helper:72
L4Re::chkcap
T chkcap(T &&cap, char const *extra="", long err=-L4_ENOMEM)
Check for valid capability or raise C++ exception.
Definition error_helper:149
L4::Ipc::make_cap_rw
Cap< T > make_cap_rw(L4::Cap< T > cap) noexcept
Make an L4::Ipc::Cap<T> for the given capability with L4_CAP_FPAGE_RW rights.
Definition ipc_types:795
L4virtio
L4-VIRTIO Transport C++ API.
Definition l4virtio:26
L4Re::Rm::F::RW
@ RW
Readable and writable region.
Definition rm:139
L4Re::Rm::F::Search_addr
@ Search_addr
Search for a suitable address range.
Definition rm:114
L4virtio::Driver::Virtio_net_device::Packet
Structure for a network packet (header including data) with maximum size, assuming that no extra feat...
Definition virtio-net:38
l4virtio_net_config_t
Device configuration for network devices.
Definition virtio_net.h:35
l4virtio_net_header_t
Header structure of a request for a network device.
Definition virtio_net.h:21
unique_cap
Unique_cap / Unique_del_cap.