Loading...
Searching...
No Matches
virtio
// vi:ft=cpp
/* SPDX-License-Identifier: MIT */
/*
* Copyright (C) 2014-2020, 2023-2024 Kernkonzept GmbH.
* Author(s): Alexander Warg <alexander.warg@kernkonzept.com>
*
*/
#pragma once
#include <l4/sys/types.h>
#include <l4/cxx/bitfield>
#include <l4/cxx/minmax>
#include <l4/cxx/utils>
#include <limits.h>
#include <string.h>
#include <stdio.h>
#include "../virtqueue"
namespace L4virtio {
namespace Svr {
struct Dev_status
{
unsigned char raw;
Dev_status() = default;
explicit Dev_status(l4_uint32_t v) : raw(v) {}
CXX_BITFIELD_MEMBER(0, 0, acked, raw);
CXX_BITFIELD_MEMBER(1, 1, driver, raw);
CXX_BITFIELD_MEMBER(2, 2, driver_ok, raw);
CXX_BITFIELD_MEMBER(3, 3, features_ok, raw);
CXX_BITFIELD_MEMBER(6, 7, fail_state, raw);
CXX_BITFIELD_MEMBER(6, 6, device_needs_reset, raw);
CXX_BITFIELD_MEMBER(7, 7, failed, raw);
bool running() const
{
return (raw == 0xf);
}
};
struct Dev_features
{
l4_uint32_t raw;
Dev_features() = default;
explicit Dev_features(l4_uint32_t v) : raw(v) {}
CXX_BITFIELD_MEMBER(28, 28, ring_indirect_desc, raw);
CXX_BITFIELD_MEMBER(29, 29, ring_event_idx, raw);
};
class Virtqueue : public L4virtio::Virtqueue
{
public:
class Head_desc
{
friend class Virtqueue;
private:
Virtqueue::Desc const *_d;
Head_desc(Virtqueue *r, unsigned i) : _d(r->desc(i)) {}
public:
Head_desc() : _d(0) {}
bool valid() const { return _d; }
explicit operator bool () const
{ return valid(); }
Desc const *desc() const
{ return _d; }
};
struct Request : Head_desc
{
Virtqueue *ring = nullptr;
Request() = default;
private:
friend class Virtqueue;
Request(Virtqueue *r, unsigned i) : Head_desc(r, i), ring(r) {}
};
Request next_avail()
{
if (L4_LIKELY(_current_avail != _avail->idx))
{
rmb();
unsigned head = _current_avail & _idx_mask;
++_current_avail;
return Request(this, _avail->ring[head]);
}
return Request();
}
void rewind_avail(Head_desc const &d)
{
unsigned head_idx = d._d - _desc;
// Calculate the distance between _current_avail and head_idx, taking into
// account that _current_avail might have wrapped around with respect to
// _idx_mask in the meantime.
_current_avail -= (_current_avail - head_idx) & _idx_mask;
}
bool desc_avail() const
{
return _current_avail != _avail->idx;
}
void consumed(Head_desc const &r, l4_uint32_t len = 0)
{
l4_uint16_t i = _used->idx & _idx_mask;
_used->ring[i] = Used_elem(r._d - _desc, len);
wmb();
++_used->idx;
}
template<typename ITER>
void consumed(ITER const &begin, ITER const &end)
{
l4_uint16_t added = 0;
l4_uint16_t idx = _used->idx;
for (auto elem = begin ; elem != end; ++elem, ++added)
_used->ring[(idx + added) & _idx_mask]
= Used_elem(elem->first._d - _desc, elem->second);
wmb();
_used->idx += added;
}
template<typename QUEUE_OBSERVER>
void finish(Head_desc &d, QUEUE_OBSERVER *o, l4_uint32_t len = 0)
{
consumed(d, len);
o->notify_queue(this);
d._d = 0;
}
template<typename ITER, typename QUEUE_OBSERVER>
void finish(ITER const &begin, ITER const &end, QUEUE_OBSERVER *o)
{
consumed(begin, end);
o->notify_queue(this);
}
void disable_notify()
{
if (L4_LIKELY(ready()))
_used->flags.no_notify() = 1;
}
void enable_notify()
{
if (L4_LIKELY(ready()))
_used->flags.no_notify() = 0;
}
Desc const *desc(unsigned idx) const
{ return _desc + idx; }
};
struct Data_buffer
{
char *pos;
l4_uint32_t left;
Data_buffer() = default;
template<typename T>
explicit Data_buffer(T *p)
: pos(reinterpret_cast<char *>(p)), left(sizeof(T))
{}
template<typename T>
void set(T *p)
{
pos = reinterpret_cast<char *>(p);
left = sizeof(T);
}
l4_uint32_t copy_to(Data_buffer *dst, l4_uint32_t max = UINT_MAX)
{
unsigned long bytes = cxx::min(cxx::min(left, dst->left), max);
memcpy(dst->pos, pos, bytes);
left -= bytes;
pos += bytes;
dst->left -= bytes;
dst->pos += bytes;
return bytes;
}
l4_uint32_t skip(l4_uint32_t bytes)
{
unsigned long b = cxx::min(left, bytes);
left -= b;
pos += b;
return b;
}
bool done() const
{ return left == 0; }
};
class Request_processor;
struct Bad_descriptor
{
enum Error
{
Bad_address,
Bad_rights,
Bad_flags,
Bad_next,
Bad_size
};
Request_processor const *proc;
// The error code
Error error;
Bad_descriptor(Request_processor const *proc, Error e)
: proc(proc), error(e)
{}
char const *message() const
{
static char const *const err[] =
{
[Bad_address] = "Descriptor address cannot be translated",
[Bad_rights] = "Insufficient memory access rights",
[Bad_flags] = "Invalid descriptor flags",
[Bad_next] = "The descriptor's `next` index is invalid",
[Bad_size] = "Invalid size of the memory block"
};
if (error >= (sizeof(err) / sizeof(err[0])) || !err[error])
return "Unknown error";
return err[error];
}
};
class Request_processor
{
private:
Virtqueue::Desc const *_table;
Virtqueue::Desc _current;
l4_uint16_t _num;
public:
template<typename DESC_MAN, typename ...ARGS>
void start(DESC_MAN *dm, Virtqueue *ring, Virtqueue::Head_desc const &request, ARGS... args)
{
_current = cxx::access_once(request.desc());
if (_current.flags.indirect())
{
dm->load_desc(_current, this, &_table);
_num = _current.len / sizeof(Virtqueue::Desc);
if (L4_UNLIKELY(!_num))
throw Bad_descriptor(this, Bad_descriptor::Bad_size);
_current = cxx::access_once(_table);
}
else
{
_table = ring->desc(0);
_num = ring->num();
}
dm->load_desc(_current, this, cxx::forward<ARGS>(args)...);
}
template<typename DESC_MAN, typename ...ARGS>
Virtqueue::Request const &start(DESC_MAN *dm, Virtqueue::Request const &request, ARGS... args)
{
start(dm, request.ring, request, cxx::forward<ARGS>(args)...);
return request;
}
Virtqueue::Desc::Flags current_flags() const
{ return _current.flags; }
bool has_more() const
{ return _current.flags.next(); }
template<typename DESC_MAN, typename ...ARGS>
bool next(DESC_MAN *dm, ARGS... args)
{
if (!_current.flags.next())
return false;
if (L4_UNLIKELY(_current.next >= _num))
throw Bad_descriptor(this, Bad_descriptor::Bad_next);
_current = cxx::access_once(_table + _current.next);
if (0) // we ignore this for performance reasons
if (L4_UNLIKELY(_current.flags.indirect()))
throw Bad_descriptor(this, Bad_descriptor::Bad_flags);
// must throw an exception in case of a bad descriptor
dm->load_desc(_current, this, cxx::forward<ARGS>(args)...);
return true;
}
};
}
}
L4virtio::Svr::Request_processor
Encapsulate the state for processing a VIRTIO request.
Definition virtio:473
L4virtio::Svr::Request_processor::next
bool next(DESC_MAN *dm, ARGS... args)
Switch to the next descriptor in a descriptor chain.
Definition virtio:570
L4virtio::Svr::Request_processor::current_flags
Virtqueue::Desc::Flags current_flags() const
Get the flags of the currently processed descriptor.
Definition virtio:545
L4virtio::Svr::Request_processor::start
Virtqueue::Request const & start(DESC_MAN *dm, Virtqueue::Request const &request, ARGS... args)
Start processing a new request.
Definition virtio:534
L4virtio::Svr::Request_processor::has_more
bool has_more() const
Are there more chained descriptors?
Definition virtio:553
L4virtio::Svr::Request_processor::start
void start(DESC_MAN *dm, Virtqueue *ring, Virtqueue::Head_desc const &request, ARGS... args)
Start processing a new request.
Definition virtio:501
L4virtio::Svr::Virtqueue::Head_desc
VIRTIO request, essentially a descriptor from the available ring.
Definition virtio:94
L4virtio::Svr::Virtqueue::Head_desc::valid
bool valid() const
Definition virtio:105
L4virtio::Svr::Virtqueue::Head_desc::desc
Desc const * desc() const
Definition virtio:112
L4virtio::Svr::Virtqueue::Head_desc::Head_desc
Head_desc()
Make invalid (NULL) request.
Definition virtio:102
L4virtio::Svr::Virtqueue
Virtqueue implementation for the device.
Definition virtio:88
L4virtio::Svr::Virtqueue::desc_avail
bool desc_avail() const
Test for available descriptors.
Definition virtio:175
L4virtio::Svr::Virtqueue::enable_notify
void enable_notify()
Clear the 'no notify' flag for this queue.
Definition virtio:284
L4virtio::Svr::Virtqueue::consumed
void consumed(ITER const &begin, ITER const &end)
Put multiple descriptors into the used ring.
Definition virtio:213
L4virtio::Svr::Virtqueue::desc
Desc const * desc(unsigned idx) const
Get a descriptor from the descriptor list.
Definition virtio:298
L4virtio::Svr::Virtqueue::next_avail
Request next_avail()
Get the next available descriptor from the available ring.
Definition virtio:136
L4virtio::Svr::Virtqueue::finish
void finish(ITER const &begin, ITER const &end, QUEUE_OBSERVER *o)
Add a range of descriptors to the used ring, and notify an observer once.
Definition virtio:262
L4virtio::Svr::Virtqueue::disable_notify
void disable_notify()
Set the 'no notify' flag for this queue.
Definition virtio:273
L4virtio::Svr::Virtqueue::finish
void finish(Head_desc &d, QUEUE_OBSERVER *o, l4_uint32_t len=0)
Add a descriptor to the used ring, and notify an observer.
Definition virtio:240
L4virtio::Svr::Virtqueue::rewind_avail
void rewind_avail(Head_desc const &d)
Return unfinished descriptors to the available ring, i.e.
Definition virtio:160
L4virtio::Svr::Virtqueue::consumed
void consumed(Head_desc const &r, l4_uint32_t len=0)
Put the given descriptor into the used ring.
Definition virtio:190
L4virtio::Virtqueue::Desc
Descriptor in the descriptor table.
Definition virtqueue:87
L4virtio::Virtqueue
Low-level Virtqueue.
Definition virtqueue:81
L4virtio::Virtqueue::_used
Used * _used
pointer to used ring.
Definition virtqueue:199
L4virtio::Virtqueue::Virtqueue
Virtqueue()=default
Create a disabled virtqueue.
L4virtio::Virtqueue::ready
bool ready() const
Test if this queue is in working state.
Definition virtqueue:400
L4virtio::Virtqueue::_idx_mask
l4_uint16_t _idx_mask
mask used for indexing into the descriptor table and the rings.
Definition virtqueue:208
L4virtio::Virtqueue::_desc
Desc * _desc
pointer to descriptor table, NULL if queue is off.
Definition virtqueue:197
L4virtio::Virtqueue::_avail
Avail * _avail
pointer to available ring.
Definition virtqueue:198
L4virtio::Virtqueue::_current_avail
l4_uint16_t _current_avail
The life counter for the queue.
Definition virtqueue:202
L4virtio::Virtqueue::num
unsigned num() const
Definition virtqueue:404
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_UNLIKELY
#define L4_UNLIKELY(x)
Expression is unlikely to execute.
Definition compiler.h:284
L4_LIKELY
#define L4_LIKELY(x)
Expression is likely to execute.
Definition compiler.h:283
types.h
Common L4 ABI Data Types.
L4virtio
L4-VIRTIO Transport C++ API.
Definition l4virtio:26
cxx::access_once
T access_once(T const *a)
Read the value at an address at most once.
Definition utils:40
string.h
String.
L4virtio::Svr::Bad_descriptor
Exception used by Queue to indicate descriptor errors.
Definition virtio:398
L4virtio::Svr::Bad_descriptor::proc
Request_processor const * proc
The processor that triggered the exception.
Definition virtio:410
L4virtio::Svr::Bad_descriptor::Error
Error
The error code.
Definition virtio:401
L4virtio::Svr::Bad_descriptor::Bad_rights
@ Bad_rights
Missing access rights on memory.
Definition virtio:403
L4virtio::Svr::Bad_descriptor::Bad_address
@ Bad_address
Address cannot be translated.
Definition virtio:402
L4virtio::Svr::Bad_descriptor::Bad_flags
@ Bad_flags
Invalid combination of descriptor flags.
Definition virtio:404
L4virtio::Svr::Bad_descriptor::Bad_next
@ Bad_next
Invalid next index.
Definition virtio:405
L4virtio::Svr::Bad_descriptor::Bad_size
@ Bad_size
Invalid size of memory block.
Definition virtio:406
L4virtio::Svr::Bad_descriptor::message
char const * message() const
Get a human readable description of the error code.
Definition virtio:430
L4virtio::Svr::Bad_descriptor::Bad_descriptor
Bad_descriptor(Request_processor const *proc, Error e)
Make a bad descriptor exception.
Definition virtio:421
L4virtio::Svr::Data_buffer::set
void set(T *p)
Set buffer for object p.
Definition virtio:337
L4virtio::Svr::Data_buffer::copy_to
l4_uint32_t copy_to(Data_buffer *dst, l4_uint32_t max=UINT_MAX)
Copy contents from this buffer to the destination buffer.
Definition virtio:354
L4virtio::Svr::Data_buffer::left
l4_uint32_t left
Bytes left in buffer.
Definition virtio:309
L4virtio::Svr::Data_buffer::pos
char * pos
Current buffer position.
Definition virtio:308
L4virtio::Svr::Data_buffer::skip
l4_uint32_t skip(l4_uint32_t bytes)
Skip given number of bytes in this buffer.
Definition virtio:375
L4virtio::Svr::Data_buffer::done
bool done() const
Check if there are no more bytes left in the buffer.
Definition virtio:388
L4virtio::Svr::Data_buffer::Data_buffer
Data_buffer(T *p)
Create buffer for object p.
Definition virtio:323
L4virtio::Svr::Dev_features::raw
l4_uint32_t raw
The raw value of the features bitmap.
Definition virtio:68
L4virtio::Svr::Dev_features::ring_indirect_desc
constexpr ring_indirect_desc_bfm_t::Val ring_indirect_desc() const
Get the ring_indirect_desc bits (28 to 28) of raw.
Definition virtio:74
L4virtio::Svr::Dev_features::ring_event_idx
constexpr ring_event_idx_bfm_t::Val ring_event_idx() const
Get the ring_event_idx bits (29 to 29) of raw.
Definition virtio:75
L4virtio::Svr::Dev_features::Dev_features
Dev_features(l4_uint32_t v)
Make Features from a raw bitmap.
Definition virtio:72
L4virtio::Svr::Dev_status::failed
constexpr failed_bfm_t::Val failed() const
Get the failed bits (7 to 7) of raw.
Definition virtio:46
L4virtio::Svr::Dev_status::device_needs_reset
constexpr device_needs_reset_bfm_t::Val device_needs_reset() const
Get the device_needs_reset bits (6 to 6) of raw.
Definition virtio:45
L4virtio::Svr::Dev_status::running
bool running() const
Check if the device is in running state.
Definition virtio:57
L4virtio::Svr::Dev_status::driver_ok
constexpr driver_ok_bfm_t::Val driver_ok() const
Get the driver_ok bits (2 to 2) of raw.
Definition virtio:42
L4virtio::Svr::Dev_status::acked
constexpr acked_bfm_t::Val acked() const
Get the acked bits (0 to 0) of raw.
Definition virtio:40
L4virtio::Svr::Dev_status::Dev_status
Dev_status(l4_uint32_t v)
Make Status from raw value.
Definition virtio:38
L4virtio::Svr::Dev_status::features_ok
constexpr features_ok_bfm_t::Val features_ok() const
Get the features_ok bits (3 to 3) of raw.
Definition virtio:43
L4virtio::Svr::Dev_status::driver
constexpr driver_bfm_t::Val driver() const
Get the driver bits (1 to 1) of raw.
Definition virtio:41
L4virtio::Svr::Dev_status::fail_state
constexpr fail_state_bfm_t::Val fail_state() const
Get the fail_state bits (6 to 7) of raw.
Definition virtio:44
L4virtio::Svr::Dev_status::raw
unsigned char raw
Raw value of the VIRTIO device status register.
Definition virtio:34
L4virtio::Virtqueue::Desc::Flags
Type for descriptor flags.
Definition virtqueue:93
L4virtio::Virtqueue::Used_elem
Type of an element of the used ring.
Definition virtqueue:155