L4Re Operating System Framework – Interface and Usage Documentation
Loading...
Searching...
No Matches
virtio-net
1// vi:ft=cpp
2/* SPDX-License-Identifier: GPL-2.0-only OR License-Ref-kk-custom */
3/*
4 * Copyright (C) 2022 Kernkonzept GmbH.
5 * Author(s): Stephan Gerhold <stephan.gerhold@kernkonzept.com>
6 */
7#pragma once
8
9#include <cstring>
10#include <functional>
11
12#include <l4/cxx/exceptions>
13#include <l4/cxx/minmax>
14#include <l4/re/dataspace>
15#include <l4/re/env>
16#include <l4/re/error_helper>
17#include <l4/re/util/unique_cap>
18#include <l4/sys/consts.h>
19
20#include <l4/l4virtio/client/l4virtio>
21#include <l4/l4virtio/l4virtio>
22#include <l4/l4virtio/virtio_net.h>
23#include <l4/l4virtio/virtqueue>
24
25namespace L4virtio { namespace Driver {
26
31{
32public:
37 struct Packet
38 {
40 l4_uint8_t data[1500 + 14]; /* MTU + Ethernet header */
41 };
42
47 int rx_queue_size() const
48 { return max_queue_size(0); }
49
54 int tx_queue_size() const
55 { return max_queue_size(1); }
56
67 {
68 // Contact device.
69 driver_connect(srvcap);
70
71 if (_config->device != L4VIRTIO_ID_NET)
72 L4Re::chksys(-L4_ENODEV, "Device is not a network device.");
73
74 if (_config->num_queues < 2)
75 L4Re::chksys(-L4_EINVAL, "Invalid number of queues reported.");
76
77 auto rxqsz = rx_queue_size();
78 auto txqsz = tx_queue_size();
79
80 // Allocate memory for RX/TX queue and RX/TX packet buffers
81 auto rxqoff = 0;
82 auto txqoff = l4_round_size(rxqoff + rxqsz * _rxq.total_size(rxqsz),
83 L4virtio::Virtqueue::Desc_align);
84 auto rxpktoff = l4_round_size(txqoff + txqsz * _txq.total_size(txqsz),
85 L4virtio::Virtqueue::Desc_align);
86 auto txpktoff = rxpktoff + rxqsz * sizeof(Packet);
87 auto totalsz = txpktoff + txqsz * sizeof(Packet);
88
89 _queue_ds = L4Re::chkcap(L4Re::Util::make_unique_cap<L4Re::Dataspace>(),
90 "Allocate queue dataspace capability");
91 auto *e = L4Re::Env::env();
92 L4Re::chksys(e->mem_alloc()->alloc(totalsz, _queue_ds.get(),
95 "Allocate memory for virtio structures");
96
97 L4Re::chksys(e->rm()->attach(&_queue_region, totalsz,
99 L4::Ipc::make_cap_rw(_queue_ds.get()), 0,
101 "Attach dataspace for virtio structures");
102
103 l4_uint64_t devaddr;
104 L4Re::chksys(register_ds(_queue_ds.get(), 0, totalsz, &devaddr),
105 "Register queue dataspace with device");
106
107 _rxq.init_queue(rxqsz, _queue_region.get() + rxqoff);
108 _txq.init_queue(txqsz, _queue_region.get() + txqoff);
109
110 config_queue(0, rxqsz, devaddr + rxqoff,
111 devaddr + rxqoff + _rxq.avail_offset(),
112 devaddr + rxqoff + _rxq.used_offset());
113 config_queue(1, txqsz, devaddr + txqoff,
114 devaddr + txqoff + _txq.avail_offset(),
115 devaddr + txqoff + _txq.used_offset());
116
117 _rxpkts = reinterpret_cast<Packet*>(_queue_region.get() + rxpktoff);
118 _txpkts = reinterpret_cast<Packet*>(_queue_region.get() + txpktoff);
119
120 // Prepare descriptors to save work later
121 for (l4_uint16_t descno = 0; descno < rxqsz; ++descno)
122 {
123 auto &desc = _rxq.desc(descno);
124 desc.addr = L4virtio::Ptr<void>(devaddr + rxpktoff +
125 descno * sizeof(Packet));
126 desc.len = sizeof(Packet);
127 desc.flags.write() = 1;
128 }
129 for (l4_uint16_t descno = 0; descno < txqsz; ++descno)
130 {
131 auto &desc = _txq.desc(descno);
132 desc.addr = L4virtio::Ptr<void>(devaddr + txpktoff +
133 descno * sizeof(Packet));
134 desc.len = sizeof(Packet);
135 }
136
137 // Finish handshake with device
138 l4virtio_set_feature(_config->driver_features_map,
140 l4virtio_set_feature(_config->driver_features_map, L4VIRTIO_NET_F_MAC);
142 }
143
148 {
149 return *_config->device_config<l4virtio_net_config_t>();
150 }
151
159 {
160 if (descno >= _rxq.num())
161 throw L4::Bounds_error("Invalid used descriptor number in RX queue");
162 return _rxpkts[descno];
163 }
164
177 {
178 auto descno = L4Re::chksys(wait_for_next_used(_rxq, len), "Wait for RX");
179 if (len)
180 // Ensure that the length provided by the device in wait_for_next_used()
181 // is not larger than the buffer and subtract the length of the header.
182 *len = cxx::min(*len - sizeof(_rxpkts[0].hdr), sizeof(_rxpkts[0].data));
183 return descno;
184 }
185
195 {
196 _rxq.free_descriptor(descno, descno);
197 }
198
202 void queue_rx()
203 {
204 l4_uint16_t descno;
205 while ((descno = _rxq.alloc_descriptor()) != Virtqueue::Eoq)
206 _rxq.enqueue_descriptor(descno);
207 notify(_rxq);
208 }
209
224 bool tx(std::function<l4_uint32_t(Packet&)> prepare)
225 {
226 auto descno = _txq.alloc_descriptor();
227 if (descno == Virtqueue::Eoq)
228 {
229 // Try again after cleaning old descriptors that have already been used
230 free_used_tx_descriptors();
231 descno = _txq.alloc_descriptor();
232 if (descno == Virtqueue::Eoq)
233 return false;
234 }
235
236 auto &pkt = _txpkts[descno];
237 auto &desc = _txq.desc(descno);
238 desc.len = sizeof(pkt.hdr) + prepare(pkt);
239 send(_txq, descno);
240 return true;
241 }
242
243private:
244 void free_used_tx_descriptors()
245 {
246 l4_uint16_t used;
247 while ((used = _txq.find_next_used()) != Virtqueue::Eoq)
248 {
249 if (used >= _txq.num())
250 throw L4::Bounds_error("Invalid used descriptor number in TX queue");
251 _txq.free_descriptor(used, used);
252 }
253 }
254
255private:
259 Packet *_rxpkts, *_txpkts;
260};
261
262} }
static Env const * env() noexcept
Returns the initial environment for the current task.
Definition env:103
@ Continuous
Allocate physically contiguous memory.
Definition mem_alloc:73
@ Pinned
Deprecated, use L4Re::Dma_space instead.
Definition mem_alloc:74
Unique region.
Definition rm:413
T get() const noexcept
Return the address.
Definition rm:486
Access out of bounds.
Definition exceptions:290
C++ interface for capabilities.
Definition capability.h:222
Client-side implementation for a general virtio device.
Definition l4virtio:32
int max_queue_size(int num) const
Maximum queue size allowed by the device.
Definition l4virtio:230
int wait_for_next_used(Virtqueue &queue, l4_uint32_t *len=nullptr) const
Wait for the next item to arrive in the used queue and return it.
Definition l4virtio:291
void driver_connect(L4::Cap< L4virtio::Device > srvcap, bool manage_notify=true)
Contacts the device and starts the initial handshake.
Definition l4virtio:56
void send(Virtqueue &queue, l4_uint16_t descno)
Send a request to the device.
Definition l4virtio:312
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
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
int driver_acknowledge()
Finalize handshake with the device.
Definition l4virtio:156
Simple class for accessing a virtio net device.
Definition virtio-net:31
int rx_queue_size() const
Return the maximum receive queue size allowed by the device.
Definition virtio-net:47
void finish_rx(l4_uint16_t descno)
Free an RX descriptor number to make it available for the RX queue again.
Definition virtio-net:194
Packet & rx_pkt(l4_uint16_t descno)
Return a reference to the RX packet buffer of the specified descriptor, e.g.
Definition virtio-net:158
l4virtio_net_config_t const & device_config() const
Return a reference to the device configuration.
Definition virtio-net:147
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:176
int tx_queue_size() const
Return the maximum transmit queue size allowed by the device.
Definition virtio-net:54
void setup_device(L4::Cap< L4virtio::Device > srvcap)
Establish a connection to the device and set up shared memory.
Definition virtio-net:66
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:224
void queue_rx()
Queue new available descriptors in the RX queue.
Definition virtio-net:202
Driver-side implementation of a Virtqueue.
Definition virtqueue:483
void free_descriptor(l4_uint16_t head, l4_uint16_t tail)
Free a chained list of descriptors in the descriptor queue.
Definition virtqueue:643
void enqueue_descriptor(l4_uint16_t descno)
Enqueue a descriptor in the available ring.
Definition virtqueue:586
l4_uint16_t alloc_descriptor()
Allocate and return an unused descriptor from the descriptor table.
Definition virtqueue:570
l4_uint16_t find_next_used(l4_uint32_t *len=nullptr)
Return the next finished block.
Definition virtqueue:621
void init_queue(unsigned num, void *desc, void *avail, void *used)
Initialize this virtqueue.
Definition virtqueue:534
Desc & desc(l4_uint16_t descno)
Return a reference to a descriptor in the descriptor table.
Definition virtqueue:602
Pointer used in virtio descriptors.
Definition virtqueue:64
l4_uint32_t len
Length of described buffer.
Definition virtqueue:125
Ptr< void > addr
Address stored in descriptor.
Definition virtqueue:124
unsigned long avail_offset() const
Get the offset of the available ring from the descriptor table.
Definition virtqueue:336
static unsigned long total_size(unsigned num)
Calculate the total size for a virtqueue of the given dimensions.
Definition virtqueue:258
unsigned long used_offset() const
Get the offset of the used ring from the descriptor table.
Definition virtqueue:342
unsigned num() const
Definition virtqueue:416
Dataspace interface.
Environment interface.
Error helper.
Base exceptions.
T1 min(T1 a, T1 b)
Get the minimum of a and b.
Definition minmax:35
unsigned char l4_uint8_t
Unsigned 8bit value.
Definition l4int.h:36
unsigned int l4_uint32_t
Unsigned 32bit value.
Definition l4int.h:40
unsigned short int l4_uint16_t
Unsigned 16bit value.
Definition l4int.h:38
unsigned long long l4_uint64_t
Unsigned 64bit value.
Definition l4int.h:42
@ L4_EINVAL
Invalid argument.
Definition err.h:56
@ L4_ENODEV
No such thing.
Definition err.h:55
#define L4_PAGESHIFT
Size of a page, log2-based.
Definition consts.h:37
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:473
void l4virtio_set_feature(l4_uint32_t *feature_map, unsigned feat)
Set the given feature bit in a feature map.
Definition virtio.h:255
@ L4VIRTIO_FEATURE_VERSION_1
Virtio protocol version 1 supported. Must be 1 for L4virtio.
Definition virtio.h:96
@ L4VIRTIO_ID_NET
Virtual ethernet card.
Definition virtio.h:63
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:54
long chksys(long err, char const *extra="", long ret=0)
Generate C++ exception on error.
Definition error_helper:68
T chkcap(T &&cap, char const *extra="", long err=-L4_ENOMEM)
Check for valid capability or raise C++ exception.
Definition error_helper:145
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:638
L4-VIRTIO Transport C++ API.
Definition l4virtio:26
@ RW
Readable and writable region.
Definition rm:140
@ Search_addr
Search for a suitable address range.
Definition rm:118
Structure for a network packet (header including data) with maximum size, assuming that no extra feat...
Definition virtio-net:38
Device configuration for network devices.
Definition virtio_net.h:35
Header structure of a request for a network device.
Definition virtio_net.h:21
Unique_cap / Unique_del_cap.