L4Re Operating System Framework
Interface and Usage Documentation
Loading...
Searching...
No Matches
virtqueue
1// vi:set ft=cpp: -*- Mode: C++ -*-
2/* SPDX-License-Identifier: MIT */
3/*
4 * (c) 2014 Alexander Warg <warg@os.inf.tu-dresden.de>
5 */
6
7#include <l4/re/util/debug>
8#include <l4/sys/types.h>
9#include <l4/sys/err.h>
10#include <l4/cxx/bitfield>
11#include <l4/cxx/exceptions>
12#include <cstdint>
13
14#pragma once
15
16namespace L4virtio {
17
18#if defined(__ARM_ARCH) && __ARM_ARCH == 7
19static inline void wmb() { asm volatile ("dmb ishst" : : : "memory"); }
20static inline void rmb() { asm volatile ("dmb ish" : : : "memory"); }
21static inline void mb() { asm volatile ("dmb ish" : : : "memory"); }
22#elif defined(__ARM_ARCH) && __ARM_ARCH >= 8
23static inline void wmb() { asm volatile ("dmb ishst" : : : "memory"); }
24static inline void rmb() { asm volatile ("dmb ishld" : : : "memory"); }
25static inline void mb() { asm volatile ("dmb ish" : : : "memory"); }
26#elif defined(__mips__)
27static inline void wmb() { asm volatile ("sync" : : : "memory"); }
28static inline void rmb() { asm volatile ("sync" : : : "memory"); }
29static inline void mb() { asm volatile ("sync" : : : "memory"); }
30#elif defined(__amd64__) || defined(__i386__) || defined(__i686__)
31static inline void wmb() { asm volatile ("sfence" : : : "memory"); }
32static inline void rmb() { asm volatile ("lfence" : : : "memory"); }
33static inline void mb() { asm volatile ("mfence" : : : "memory"); }
34#elif defined(__riscv)
35static inline void wmb() { asm volatile ("fence ow, ow" : : : "memory"); }
36static inline void rmb() { asm volatile ("fence ir, ir" : : : "memory"); }
37static inline void mb() { asm volatile ("fence iorw, iorw" : : : "memory"); }
38#else
39#warning Missing proper memory write barrier
40static inline void wmb() { asm volatile ("" : : : "memory"); }
41static inline void rmb() { asm volatile ("" : : : "memory"); }
42static inline void mb() { asm volatile ("" : : : "memory"); }
43#endif
44
45
52template< typename T >
53class Ptr
54{
55public:
58
59 Ptr() = default;
60
62 Ptr(Invalid_type) : _p(~0ULL) {}
63
65 explicit Ptr(l4_uint64_t vm_addr) : _p(vm_addr) {}
66
68 l4_uint64_t get() const { return _p; }
69
71 bool is_valid() const { return _p != ~0ULL; }
72
73private:
74 l4_uint64_t _p;
75};
76
77
87{
88public:
92 class Desc
93 {
94 public:
98 struct Flags
99 {
101 Flags() = default;
102
104 explicit Flags(l4_uint16_t v) : raw(v) {}
105
107 CXX_BITFIELD_MEMBER( 0, 0, next, raw);
109 CXX_BITFIELD_MEMBER( 1, 1, write, raw);
111 CXX_BITFIELD_MEMBER( 2, 2, indirect, raw);
112 };
113
118
126 void dump(unsigned idx, L4Re::Util::Dbg dbg = L4Re::Util::Dbg{}) const
127 {
128 dbg.printf("D[%04x]: %08llx (%x) f=%04x n=%04x\n",
129 idx, addr.get(), len, flags.raw, next);
130 }
131 };
132
136 class Avail
137 {
138 public:
142 struct Flags
143 {
145 Flags() = default;
146
148 explicit Flags(l4_uint16_t v) : raw(v) {}
149
151 CXX_BITFIELD_MEMBER( 0, 0, no_irq, raw);
152 };
153
157 };
158
162 struct Used_elem
163 {
164 Used_elem() = default;
165
176 };
177
181 class Used
182 {
183 public:
187 struct Flags
188 {
190 Flags() = default;
191
193 explicit Flags(l4_uint16_t v) : raw(v) {}
194
196 CXX_BITFIELD_MEMBER( 0, 0, no_notify, raw);
197 };
198
202 };
203
204protected:
205 Desc *_desc = nullptr;
206 Avail *_avail = nullptr;
207 Used *_used = nullptr;
208
211
217
221 Virtqueue() = default;
222
223 Virtqueue(Virtqueue const &) = delete;
224 ~Virtqueue() = default;
225
226public:
232 void disable()
233 { _desc = 0; }
234
238 enum
239 {
240 Desc_align = 4, //< Alignment of the descriptor table.
241 Avail_align = 1, //< Alignment of the available ring.
242 Used_align = 2, //< Alignment of the used ring.
243 };
244
253 static unsigned long total_size(unsigned num)
254 {
255 static_assert(Desc_align >= Avail_align,
256 "virtqueue alignment assumptions broken");
257 return l4_round_size(desc_size(num) + avail_size(num), Used_align)
258 + used_size(num);
259 }
260
269 static unsigned long desc_size(unsigned num)
270 { return num * 16; }
271
277 static unsigned long desc_align()
278 { return Desc_align; }
279
287 static unsigned long avail_size(unsigned num)
288 { return 2 * num + 6; }
289
295 static unsigned long avail_align()
296 { return Avail_align; }
297
306 static unsigned long used_size(unsigned num)
307 { return 8 * num + 6; }
308
314 static unsigned long used_align()
315 { return Used_align; }
316
322 unsigned long total_size() const
323 {
324 return (reinterpret_cast<char *>(_used) - reinterpret_cast<char *>(_desc))
325 + used_size(num());
326 }
327
331 unsigned long avail_offset() const
332 { return reinterpret_cast<char *>(_avail) - reinterpret_cast<char *>(_desc); }
333
337 unsigned long used_offset() const
338 { return reinterpret_cast<char *>(_used) - reinterpret_cast<char *>(_desc); }
339
359 void setup(unsigned num, void *desc, void *avail, void *used,
360 L4Re::Util::Dbg msg_dev = L4Re::Util::Dbg{2})
361 {
362 if (num == 0 || (num & (num - 1)))
363 throw L4::Runtime_error(-L4_EINVAL, "Queue size is not a power of two.");
364 if (num > 0x10000)
365 throw L4::Runtime_error(-L4_EINVAL, "Queue too large.");
366
367 _idx_mask = num - 1;
368 _desc = static_cast<Desc*>(desc);
369 _avail = static_cast<Avail*>(avail);
370 _used = static_cast<Used*>(used);
371
372 _current_avail = 0;
373
374 msg_dev.printf("VQ[%p]: num=%d d:%p a:%p u:%p\n",
375 this, num, _desc, _avail, _used);
376 }
377
393 void setup_simple(unsigned num, void *ring,
394 L4Re::Util::Dbg msg_dev = L4Re::Util::Dbg{2})
395 {
396 l4_addr_t desc = reinterpret_cast<l4_addr_t>(ring);
397 l4_addr_t avail = l4_round_size(desc + desc_size(num), Avail_align);
398 void *used = reinterpret_cast<void *>(
399 l4_round_size(avail + avail_size(num), Used_align));
400 setup(num, ring, reinterpret_cast<void *>(avail), used, msg_dev);
401 }
402
411 void dump(Desc const *d, L4Re::Util::Dbg dbg = L4Re::Util::Dbg{}) const
412 { d->dump(d - _desc, dbg); }
413
419 bool ready() const
420 { return L4_LIKELY(_desc != 0); }
421
423 unsigned num() const
424 { return _idx_mask + 1; }
425
433 bool no_notify_guest() const
434 {
435 mb(); // All queue updates must be visible before the flag can be read!
436 return _avail->flags.no_irq();
437 }
438
446 bool no_notify_host() const
447 {
448 mb(); // All queue updates must be visible before the flag can be read!
449 return _used->flags.no_notify();
450 }
451
457 void no_notify_host(bool value)
458 {
459 _used->flags.no_notify() = value;
460 }
461
470 l4_uint16_t get_avail_idx() const { return _avail->idx; }
471
478
479};
480
481namespace Driver {
482
491class Virtqueue : public L4virtio::Virtqueue
492{
493private:
495 l4_uint16_t _next_free;
496
497public:
498 enum End_of_queue
499 {
500 // Indicates the end of the queue.
501 Eoq = 0xFFFF
502 };
503
504 Virtqueue() : _next_free(Eoq) {}
505
515 void initialize_rings(unsigned num)
516 {
517 _used->idx = 0;
518 _avail->idx = 0;
519
520 // setup the freelist
521 for (l4_uint16_t d = 0; d < num - 1; ++d)
522 _desc[d].next = d + 1;
523 _desc[num - 1].next = Eoq;
524 _next_free = 0;
525 }
526
545 void init_queue(unsigned num, void *desc, void *avail, void *used,
546 L4Re::Util::Dbg msg_dev = L4Re::Util::Dbg{2})
547 {
548 setup(num, desc, avail, used, msg_dev);
550 }
551
563 void init_queue(unsigned num, void *base,
564 L4Re::Util::Dbg msg_dev = L4Re::Util::Dbg{2})
565 {
566 setup_simple(num, base, msg_dev);
568 }
569
570
586 {
587 l4_uint16_t idx = _next_free;
588 if (idx == Eoq)
589 return Eoq;
590
591 _next_free = _desc[idx].next;
592
593 return idx;
594 }
595
602 {
603 if (descno > _idx_mask)
604 throw L4::Bounds_error();
605
606 _avail->ring[_avail->idx & _idx_mask] = descno; // _avail->idx expected to wrap
607 // Make the ring entry visible before publishing the new index, so the
608 // device never sees an advanced index pointing at a stale entry. Pairs
609 // with the rmb() on the consuming side.
610 wmb();
611 ++_avail->idx;
612 }
613
621 {
622 if (descno > _idx_mask)
623 throw L4::Bounds_error();
624
625 return _desc[descno];
626 }
627
640 {
641 if (_current_avail == _used->idx)
642 return Eoq;
643
644 // The device writes the used element (id and len) before it advances
645 // _used->idx. Having observed the new idx above, this barrier ensures
646 // we read the matching ring entry only afterwards and thus see the
647 // device's writes, not stale contents. Pairs with the wmb() the device
648 // side issues before publishing the index.
649 rmb();
650 auto elem = _used->ring[_current_avail++ & _idx_mask];
651
652 if (len)
653 *len = elem.len;
654
655 return elem.id;
656 }
657
668 {
669 if (head > _idx_mask || tail > _idx_mask)
670 throw L4::Bounds_error();
671
672 _desc[tail].next = _next_free;
673 _next_free = head;
674 }
675};
676
677}
678} // namespace L4virtio
Access out of bounds.
Definition exceptions:279
Exception for an abstract runtime error.
Definition exceptions:129
void free_descriptor(l4_uint16_t head, l4_uint16_t tail)
Free a chained list of descriptors in the descriptor queue.
Definition virtqueue:667
void enqueue_descriptor(l4_uint16_t descno)
Enqueue a descriptor in the available ring.
Definition virtqueue:601
l4_uint16_t alloc_descriptor()
Allocate and return an unused descriptor from the descriptor table.
Definition virtqueue:585
l4_uint16_t find_next_used(l4_uint32_t *len=nullptr)
Return the next finished block.
Definition virtqueue:639
Desc & desc(l4_uint16_t descno)
Return a reference to a descriptor in the descriptor table.
Definition virtqueue:620
void init_queue(unsigned num, void *base, L4Re::Util::Dbg msg_dev=L4Re::Util::Dbg{2})
Initialize this virtqueue.
Definition virtqueue:563
void initialize_rings(unsigned num)
Initialize the descriptor table and the index structures of this queue.
Definition virtqueue:515
void init_queue(unsigned num, void *desc, void *avail, void *used, L4Re::Util::Dbg msg_dev=L4Re::Util::Dbg{2})
Initialize this virtqueue.
Definition virtqueue:545
Pointer used in virtio descriptors.
Definition virtqueue:54
Ptr(l4_uint64_t vm_addr)
Make a Ptr from a raw 64bit address.
Definition virtqueue:65
l4_uint64_t get() const
Definition virtqueue:68
Invalid_type
Type for making an invalid (NULL) Ptr.
Definition virtqueue:57
@ Invalid
Use to set a Ptr to invalid (NULL).
Definition virtqueue:57
bool is_valid() const
Definition virtqueue:71
Ptr(Invalid_type)
Make and invalid Ptr.
Definition virtqueue:62
Type of available ring, this is read-only for the host.
Definition virtqueue:137
l4_uint16_t ring[]
array of available descriptor indexes.
Definition virtqueue:156
Flags flags
flags of available ring
Definition virtqueue:154
l4_uint16_t idx
available index written by guest
Definition virtqueue:155
Descriptor in the descriptor table.
Definition virtqueue:93
l4_uint16_t next
Index of the next chained descriptor.
Definition virtqueue:117
l4_uint32_t len
Length of described buffer.
Definition virtqueue:115
Flags flags
Descriptor flags.
Definition virtqueue:116
Ptr< void > addr
Address stored in descriptor.
Definition virtqueue:114
void dump(unsigned idx, L4Re::Util::Dbg dbg=L4Re::Util::Dbg{}) const
Dump a single descriptor.
Definition virtqueue:126
Used_elem ring[]
array of used descriptors.
Definition virtqueue:201
l4_uint16_t idx
index of the last entry in the ring.
Definition virtqueue:200
Flags flags
flags of the used ring.
Definition virtqueue:199
Low-level Virtqueue.
Definition virtqueue:87
void no_notify_host(bool value)
Set the no-notify flag for this queue.
Definition virtqueue:457
void disable()
Completely disable the queue.
Definition virtqueue:232
static unsigned long desc_size(unsigned num)
Calculate the size of the descriptor table for num entries.
Definition virtqueue:269
l4_uint16_t get_tail_avail_idx() const
Get tail-available index stored in local state (for debugging).
Definition virtqueue:477
void dump(Desc const *d, L4Re::Util::Dbg dbg=L4Re::Util::Dbg{}) const
Dump a descriptor for this queue.
Definition virtqueue:411
Used * _used
pointer to used ring.
Definition virtqueue:207
bool no_notify_guest() const
Get the no IRQ flag of this queue.
Definition virtqueue:433
static unsigned long avail_align()
Get the alignment in zero LSBs needed for the available ring.
Definition virtqueue:295
unsigned long avail_offset() const
Get the offset of the available ring from the descriptor table.
Definition virtqueue:331
static unsigned long used_align()
Get the alignment in zero LSBs needed for the used ring.
Definition virtqueue:314
void setup(unsigned num, void *desc, void *avail, void *used, L4Re::Util::Dbg msg_dev=L4Re::Util::Dbg{2})
Enable this queue.
Definition virtqueue:359
static unsigned long total_size(unsigned num)
Calculate the total size for a virtqueue of the given dimensions.
Definition virtqueue:253
static unsigned long desc_align()
Get the alignment in zero LSBs needed for the descriptor table.
Definition virtqueue:277
static unsigned long used_size(unsigned num)
Calculate the size of the used ring for num entries.
Definition virtqueue:306
Virtqueue()=default
Create a disabled virtqueue.
unsigned long used_offset() const
Get the offset of the used ring from the descriptor table.
Definition virtqueue:337
static unsigned long avail_size(unsigned num)
Calculate the size of the available ring for num entries.
Definition virtqueue:287
unsigned long total_size() const
Calculate the total size of this virtqueue.
Definition virtqueue:322
bool ready() const
Test if this queue is in working state.
Definition virtqueue:419
l4_uint16_t _idx_mask
mask used for indexing into the descriptor table and the rings.
Definition virtqueue:216
Desc * _desc
pointer to descriptor table, NULL if queue is off.
Definition virtqueue:205
l4_uint16_t get_avail_idx() const
Get available index from available ring (for debugging).
Definition virtqueue:470
bool no_notify_host() const
Get the no notify flag of this queue.
Definition virtqueue:446
Avail * _avail
pointer to available ring.
Definition virtqueue:206
void setup_simple(unsigned num, void *ring, L4Re::Util::Dbg msg_dev=L4Re::Util::Dbg{2})
Enable this queue.
Definition virtqueue:393
l4_uint16_t _current_avail
The life counter for the queue.
Definition virtqueue:210
unsigned num() const
Definition virtqueue:423
Error codes.
Base exceptions.
unsigned long l4_addr_t
Address type.
Definition l4int.h:34
unsigned int l4_uint32_t
Unsigned 32bit value.
Definition l4int.h:29
unsigned short int l4_uint16_t
Unsigned 16bit value.
Definition l4int.h:27
unsigned long long l4_uint64_t
Unsigned 64bit value.
Definition l4int.h:31
@ L4_EINVAL
Invalid argument.
Definition err.h:47
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:501
#define L4_LIKELY(x)
Expression is likely to execute.
Definition compiler.h:294
Common L4 ABI Data Types.
L4-VIRTIO Transport C++ API.
Definition l4virtio:26
Flags of the available ring.
Definition virtqueue:143
constexpr no_irq_bfm_t::Val no_irq() const
Get the no_irq bits (0 to 0) of raw.
Definition virtqueue:151
Flags(l4_uint16_t v)
Make Flags from the raw value.
Definition virtqueue:148
l4_uint16_t raw
raw 16bit flags value of the available ring.
Definition virtqueue:144
Type for descriptor flags.
Definition virtqueue:99
constexpr next_bfm_t::Val next() const
Get the next bits (0 to 0) of raw.
Definition virtqueue:107
constexpr write_bfm_t::Val write() const
Get the write bits (1 to 1) of raw.
Definition virtqueue:109
Flags(l4_uint16_t v)
Make Flags from raw 16bit value.
Definition virtqueue:104
l4_uint16_t raw
raw flags value of a virtio descriptor.
Definition virtqueue:100
constexpr indirect_bfm_t::Val indirect() const
Get the indirect bits (2 to 2) of raw.
Definition virtqueue:111
flags for the used ring.
Definition virtqueue:188
constexpr no_notify_bfm_t::Val no_notify() const
Get the no_notify bits (0 to 0) of raw.
Definition virtqueue:196
l4_uint16_t raw
raw flags value as specified by virtio.
Definition virtqueue:189
Flags(l4_uint16_t v)
make Flags from raw value
Definition virtqueue:193
Type of an element of the used ring.
Definition virtqueue:163
l4_uint32_t id
descriptor index
Definition virtqueue:174
l4_uint32_t len
length field
Definition virtqueue:175
Used_elem(l4_uint16_t id, l4_uint32_t len)
Initialize a used ring element.
Definition virtqueue:173