Loading...
Searching...
No Matches
virtio-block
// vi:ft=cpp
/* SPDX-License-Identifier: MIT */
/*
* Copyright (C) 2017-2021, 2024 Kernkonzept GmbH.
* Author(s): Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
*
*/
#pragma once
#include <l4/cxx/unique_ptr>
#include <l4/re/util/unique_cap>
#include <climits>
#include <l4/l4virtio/virtio.h>
#include <l4/l4virtio/virtio_block.h>
#include <l4/l4virtio/server/l4virtio>
#include <l4/sys/cxx/ipc_epiface>
namespace L4virtio { namespace Svr {
template <typename Ds_data> class Block_dev_base;
template<typename Ds_data>
class Block_request
{
friend class Block_dev_base<Ds_data>;
enum { Header_size = sizeof(l4virtio_block_header_t) };
public:
struct Data_block
{
Driver_mem_region_t<Ds_data> *mem;
void *addr;
l4_uint32_t len;
Data_block() = default;
Data_block(Driver_mem_region_t<Ds_data> *m, Virtqueue::Desc const &desc,
Request_processor const *)
: mem(m), addr(m->local(desc.addr)), len(desc.len)
{}
};
unsigned data_size() const
{
Request_processor rp;
Data_block data;
rp.start(_mem_list, _request, &data);
unsigned total = data.len;
try
{
while (rp.has_more())
{
rp.next(_mem_list, &data);
total += data.len;
}
}
catch (Bad_descriptor const &e)
{
// need to convert the exception because e contains a raw pointer to rp
throw L4::Runtime_error(-L4_EIO, "bad virtio descriptor");
}
if (total < Header_size + 1)
throw L4::Runtime_error(-L4_EIO, "virtio request too short");
return total - Header_size - 1;
}
bool has_more()
{
// peek into the remaining data
while (_data.len == 0 && _rp.has_more())
_rp.next(_mem_list, &_data);
// there always must be one byte left for status
return (_data.len > 1 || _rp.has_more());
}
Data_block next_block()
{
Data_block out;
if (_data.len == 0)
{
if (!_rp.has_more())
throw L4::Runtime_error(-L4_EEXIST,
"No more data blocks in virtio request");
if (_todo_blocks == 0)
throw Bad_descriptor(&_rp, Bad_descriptor::Bad_size);
--_todo_blocks;
_rp.next(_mem_list, &_data);
}
if (_data.len > _max_block_size)
throw Bad_descriptor(&_rp, Bad_descriptor::Bad_size);
out = _data;
if (!_rp.has_more())
{
--(out.len);
_data.len = 1;
_data.addr = static_cast<char *>(_data.addr) + out.len;
}
else
_data.len = 0; // is consumed
return out;
}
l4virtio_block_header_t const &header() const
{ return _header; }
private:
Block_request(Virtqueue::Request req, Driver_mem_list_t<Ds_data> *mem_list,
unsigned max_blocks, l4_uint32_t max_block_size)
: _mem_list(mem_list),
_request(req),
_todo_blocks(max_blocks),
_max_block_size(max_block_size)
{
// read header which should be in the first block
_rp.start(mem_list, _request, &_data);
--_todo_blocks;
if (_data.len < Header_size)
throw Bad_descriptor(&_rp, Bad_descriptor::Bad_size);
_header = *(static_cast< l4virtio_block_header_t *>(_data.addr));
_data.addr = static_cast<char *>(_data.addr) + Header_size;
_data.len -= Header_size;
// if there is no space for status bit we cannot really recover
if (!_rp.has_more() && _data.len == 0)
throw Bad_descriptor(&_rp, Bad_descriptor::Bad_size);
}
int release_request(Virtqueue *queue, l4_uint8_t status, unsigned sz)
{
// write back status
// If there was an error on the way or the status byte is in its
// own block, fast-forward to the last block.
if (_rp.has_more())
{
while (_rp.next(_mem_list, &_data) && _todo_blocks > 0)
--_todo_blocks;
if (_todo_blocks > 0 && _data.len > 0)
*(static_cast< l4_uint8_t *>(_data.addr) + _data.len - 1) = status;
else
return -L4_EIO; // too many data blocks
}
else if (_data.len > 0)
*(static_cast< l4_uint8_t *>(_data.addr)) = status;
else
return -L4_EIO; // no space for final status byte
// now release the head
queue->consumed(_request, sz);
return L4_EOK;
}
Driver_mem_list_t<Ds_data> *_mem_list;
l4virtio_block_header_t _header;
Request_processor _rp;
Data_block _data;
Virtqueue::Request _request;
unsigned _todo_blocks;
l4_uint32_t _max_block_size;
};
struct Block_features : public Dev_config::Features
{
Block_features() = default;
Block_features(l4_uint32_t raw) : Dev_config::Features(raw) {}
CXX_BITFIELD_MEMBER( 1, 1, size_max, raw);
CXX_BITFIELD_MEMBER( 2, 2, seg_max, raw);
CXX_BITFIELD_MEMBER( 4, 4, geometry, raw);
CXX_BITFIELD_MEMBER( 5, 5, ro, raw);
CXX_BITFIELD_MEMBER( 6, 6, blk_size, raw);
CXX_BITFIELD_MEMBER( 9, 9, flush, raw);
CXX_BITFIELD_MEMBER(10, 10, topology, raw);
CXX_BITFIELD_MEMBER(11, 11, config_wce, raw);
CXX_BITFIELD_MEMBER(12, 12, mq, raw);
CXX_BITFIELD_MEMBER(13, 13, discard, raw);
CXX_BITFIELD_MEMBER(14, 14, write_zeroes, raw);
};
template <typename Ds_data>
class Block_dev_base : public L4virtio::Svr::Device_t<Ds_data>
{
private:
L4Re::Util::Unique_cap<L4::Irq> _kick_guest_irq;
Virtqueue _queue;
unsigned _vq_max;
l4_uint32_t _max_block_size = UINT_MAX;
Dev_config_t<l4virtio_block_config_t> _dev_config;
public:
typedef Block_request<Ds_data> Request;
protected:
Block_features negotiated_features() const
{ return _dev_config.negotiated_features(0); }
Block_features device_features() const
{ return _dev_config.host_features(0); }
void set_device_features(Block_features df)
{ _dev_config.host_features(0) = df.raw; }
void set_size_max(l4_uint32_t sz)
{
_dev_config.priv_config()->size_max = sz;
Block_features df = device_features();
df.size_max() = true;
set_device_features(df);
_max_block_size = sz;
}
void set_seg_max(l4_uint32_t sz)
{
_dev_config.priv_config()->seg_max = sz;
Block_features df = device_features();
df.seg_max() = true;
set_device_features(df);
}
void set_geometry(l4_uint16_t cylinders, l4_uint8_t heads, l4_uint8_t sectors)
{
l4virtio_block_config_t volatile *pc = _dev_config.priv_config();
pc->geometry.cylinders = cylinders;
pc->geometry.heads = heads;
pc->geometry.sectors = sectors;
Block_features df = device_features();
df.geometry() = true;
set_device_features(df);
}
void set_blk_size(l4_uint32_t sz)
{
_dev_config.priv_config()->blk_size = sz;
Block_features df = device_features();
df.blk_size() = true;
set_device_features(df);
}
void set_topology(l4_uint8_t physical_block_exp,
l4_uint8_t alignment_offset,
l4_uint32_t min_io_size,
l4_uint32_t opt_io_size)
{
l4virtio_block_config_t volatile *pc = _dev_config.priv_config();
pc->topology.physical_block_exp = physical_block_exp;
pc->topology.alignment_offset = alignment_offset;
pc->topology.min_io_size = min_io_size;
pc->topology.opt_io_size = opt_io_size;
Block_features df = device_features();
df.topology() = true;
set_device_features(df);
}
void set_flush()
{
Block_features df = device_features();
df.flush() = true;
set_device_features(df);
}
void set_config_wce(l4_uint8_t writeback)
{
l4virtio_block_config_t volatile *pc = _dev_config.priv_config();
pc->writeback = writeback;
Block_features df = device_features();
df.config_wce() = true;
set_device_features(df);
}
l4_uint8_t get_writeback()
{
l4virtio_block_config_t volatile *pc = _dev_config.priv_config();
return pc->writeback;
}
void set_discard(l4_uint32_t max_discard_sectors, l4_uint32_t max_discard_seg,
l4_uint32_t discard_sector_alignment)
{
l4virtio_block_config_t volatile *pc = _dev_config.priv_config();
pc->max_discard_sectors = max_discard_sectors;
pc->max_discard_seg = max_discard_seg;
pc->discard_sector_alignment = discard_sector_alignment;
Block_features df = device_features();
df.discard() = true;
set_device_features(df);
}
void set_write_zeroes(l4_uint32_t max_write_zeroes_sectors,
l4_uint32_t max_write_zeroes_seg,
l4_uint8_t write_zeroes_may_unmap)
{
l4virtio_block_config_t volatile *pc = _dev_config.priv_config();
pc->max_write_zeroes_sectors = max_write_zeroes_sectors;
pc->max_write_zeroes_seg = max_write_zeroes_seg;
pc->write_zeroes_may_unmap = write_zeroes_may_unmap;
Block_features df = device_features();
df.write_zeroes() = true;
set_device_features(df);
}
public:
Block_dev_base(l4_uint32_t vendor, unsigned queue_size, l4_uint64_t capacity,
bool read_only)
: L4virtio::Svr::Device_t<Ds_data>(&_dev_config),
_vq_max(queue_size),
_dev_config(vendor, L4VIRTIO_ID_BLOCK, 1)
{
this->reset_queue_config(0, queue_size);
Block_features df(0);
df.ring_indirect_desc() = true;
df.ro() = read_only;
set_device_features(df);
_dev_config.set_host_feature(L4VIRTIO_FEATURE_VERSION_1);
_dev_config.priv_config()->capacity = capacity;
}
virtual void reset_device() = 0;
virtual bool queue_stopped() = 0;
void finalize_request(cxx::unique_ptr<Request> req, unsigned sz,
l4_uint8_t status = L4VIRTIO_BLOCK_S_OK)
{
if (_dev_config.status().fail_state() || !_queue.ready())
return;
if (req->release_request(&_queue, status, sz) < 0)
this->device_error();
if (_queue.no_notify_guest())
return;
_dev_config.add_irq_status(L4VIRTIO_IRQ_STATUS_VRING);
_kick_guest_irq->trigger();
// Request can be dropped here.
}
int reconfig_queue(unsigned idx) override
{
if (idx == 0 && this->setup_queue(&_queue, 0, _vq_max))
return 0;
return -L4_EINVAL;
}
void reset() override
{
_queue.disable();
_dev_config.reset_queue(0, _vq_max);
_dev_config.reset_hdr();
reset_device();
}
protected:
bool check_for_new_requests()
{
if (!_queue.ready() || queue_stopped())
return false;
if (_dev_config.status().fail_state())
return false;
return _queue.desc_avail();
}
cxx::unique_ptr<Request> get_request()
{
cxx::unique_ptr<Request> req;
if (!_queue.ready() || queue_stopped())
return req;
if (_dev_config.status().fail_state())
return req;
auto r = _queue.next_avail();
if (!r)
return req;
try
{
cxx::unique_ptr<Request> cur{
new Request(r, &(this->_mem_info), _vq_max, _max_block_size)};
req = cxx::move(cur);
}
catch (Bad_descriptor const &e)
{
this->device_error();
return req;
}
return req;
}
private:
void register_single_driver_irq() override
{
_kick_guest_irq = L4Re::Util::Unique_cap<L4::Irq>(
L4Re::chkcap(this->server_iface()->template rcv_cap<L4::Irq>(0)));
L4Re::chksys(this->server_iface()->realloc_rcv_cap(0));
}
void trigger_driver_config_irq() override
{
_dev_config.add_irq_status(L4VIRTIO_IRQ_STATUS_CONFIG);
_kick_guest_irq->trigger();
}
bool check_queues() override
{
if (!_queue.ready())
{
reset();
return false;
}
return true;
}
};
template <typename Ds_data>
struct Block_dev
: Block_dev_base<Ds_data>,
L4::Epiface_t<Block_dev<Ds_data>, L4virtio::Device>
{
private:
class Irq_object : public L4::Irqep_t<Irq_object>
{
public:
Irq_object(Block_dev<Ds_data> *parent) : _parent(parent) {}
void handle_irq()
{
_parent->kick();
}
private:
Block_dev<Ds_data> *_parent;
};
Irq_object _irq_handler;
protected:
L4::Epiface *irq_iface()
{ return &_irq_handler; }
public:
Block_dev(l4_uint32_t vendor, unsigned queue_size, l4_uint64_t capacity,
bool read_only)
: Block_dev_base<Ds_data>(vendor, queue_size, capacity, read_only),
_irq_handler(this)
{}
L4::Cap<void> register_obj(L4::Registry_iface *registry,
char const *service = 0)
{
L4Re::chkcap(registry->register_irq_obj(this->irq_iface()));
L4::Cap<void> ret;
if (service)
ret = registry->register_obj(this, service);
else
ret = registry->register_obj(this);
L4Re::chkcap(ret);
return ret;
}
L4::Cap<void> register_obj(L4::Registry_iface *registry,
L4::Cap<L4::Rcv_endpoint> ep)
{
L4Re::chkcap(registry->register_irq_obj(this->irq_iface()));
return L4Re::chkcap(registry->register_obj(this, ep));
}
typedef Block_request<Ds_data> Request;
virtual bool process_request(cxx::unique_ptr<Request> &&req) = 0;
protected:
L4::Ipc_svr::Server_iface *server_iface() const override
{
return this->L4::Epiface::server_iface();
}
void kick()
{
for (;;)
{
auto req = this->get_request();
if (!req)
return;
if (!this->process_request(cxx::move(req)))
return;
}
}
private:
L4::Cap<L4::Irq> device_notify_irq() const override
{
return L4::cap_cast<L4::Irq>(_irq_handler.obj_cap());
}
};
} }
L4::Registry_iface::register_irq_obj
virtual L4::Cap< L4::Irq > register_irq_obj(L4::Epiface *o)=0
Register o as server-side object for asynchronous IRQs.
L4::Registry_iface::register_obj
virtual L4::Cap< void > register_obj(L4::Epiface *o, char const *service)=0
Register an L4::Epiface for an IPC gate available in the applications environment under the name serv...
L4::Runtime_error
Exception for an abstract runtime error.
Definition exceptions:129
L4virtio::Svr::Block_dev_base
Base class for virtio block devices.
Definition virtio-block:260
L4virtio::Svr::Block_dev_base::set_write_zeroes
void set_write_zeroes(l4_uint32_t max_write_zeroes_sectors, l4_uint32_t max_write_zeroes_seg, l4_uint8_t write_zeroes_may_unmap)
Sets constraints for and enables the write zeroes command.
Definition virtio-block:422
L4virtio::Svr::Block_dev_base::queue_stopped
virtual bool queue_stopped()=0
Return true, if the queues should not be processed further.
L4virtio::Svr::Block_dev_base::get_writeback
l4_uint8_t get_writeback()
Get the writeback field from the configuration space.
Definition virtio-block:388
L4virtio::Svr::Block_dev_base::get_request
cxx::unique_ptr< Request > get_request()
Return one request if available.
Definition virtio-block:530
L4virtio::Svr::Block_dev_base::set_blk_size
void set_blk_size(l4_uint32_t sz)
Sets block disk size to be reported to the client.
Definition virtio-block:332
L4virtio::Svr::Block_dev_base::set_config_wce
void set_config_wce(l4_uint8_t writeback)
Sets cache mode and enables the writeback toggle.
Definition virtio-block:375
L4virtio::Svr::Block_dev_base::Block_dev_base
Block_dev_base(l4_uint32_t vendor, unsigned queue_size, l4_uint64_t capacity, bool read_only)
Create a new virtio block device.
Definition virtio-block:444
L4virtio::Svr::Block_dev_base::set_flush
void set_flush()
Enables the flush command.
Definition virtio-block:364
L4virtio::Svr::Block_dev_base::set_size_max
void set_size_max(l4_uint32_t sz)
Sets the maximum size of any single segment reported to client.
Definition virtio-block:290
L4virtio::Svr::Block_dev_base::reset_device
virtual void reset_device()=0
Reset the actual hardware device.
L4virtio::Svr::Block_dev_base::set_discard
void set_discard(l4_uint32_t max_discard_sectors, l4_uint32_t max_discard_seg, l4_uint32_t discard_sector_alignment)
Sets constraints for and enables the discard command.
Definition virtio-block:402
L4virtio::Svr::Block_dev_base::reset
void reset() override
reset callback, called for doing a device reset
Definition virtio-block:509
L4virtio::Svr::Block_dev_base::set_geometry
void set_geometry(l4_uint16_t cylinders, l4_uint8_t heads, l4_uint8_t sectors)
Set disk geometry that is reported to the client.
Definition virtio-block:315
L4virtio::Svr::Block_dev_base::set_seg_max
void set_seg_max(l4_uint32_t sz)
Sets the maximum number of segments in a request that is reported to client.
Definition virtio-block:304
L4virtio::Svr::Block_dev_base::set_topology
void set_topology(l4_uint8_t physical_block_exp, l4_uint8_t alignment_offset, l4_uint32_t min_io_size, l4_uint32_t opt_io_size)
Sets the I/O alignment information reported back to the client.
Definition virtio-block:348
L4virtio::Svr::Block_dev_base::finalize_request
void finalize_request(cxx::unique_ptr< Request > req, unsigned sz, l4_uint8_t status=L4VIRTIO_BLOCK_S_OK)
Releases resources related to a request and notifies the client.
Definition virtio-block:483
L4virtio::Svr::Block_dev_base::reconfig_queue
int reconfig_queue(unsigned idx) override
callback for client queue-config request
Definition virtio-block:501
L4virtio::Svr::Block_request
A request to read or write data.
Definition virtio-block:29
L4virtio::Svr::Block_request::data_size
unsigned data_size() const
Compute the total size of the data in the request.
Definition virtio-block:63
L4virtio::Svr::Block_request::header
l4virtio_block_header_t const & header() const
Return the block request header.
Definition virtio-block:148
L4virtio::Svr::Block_request::has_more
bool has_more()
Check if the request contains more data blocks.
Definition virtio-block:95
L4virtio::Svr::Block_request::next_block
Data_block next_block()
Return next block in scatter-gather list.
Definition virtio-block:113
L4virtio::Svr::Dev_config::add_irq_status
void add_irq_status(l4_uint32_t status)
Adds irq status bit.
Definition l4virtio:265
L4virtio::Svr::Dev_config::status
Status status() const
Get current device status (trusted).
Definition l4virtio:222
L4virtio::Svr::Device_t
Server-side L4-VIRTIO device stub.
Definition l4virtio:802
L4virtio::Svr::Device_t< Ds_data >::device_error
void device_error()
Definition l4virtio:1024
L4virtio::Svr::Device_t< Ds_data >::trigger_driver_config_irq
virtual void trigger_driver_config_irq()=0
L4virtio::Svr::Device_t< Ds_data >::_mem_info
Mem_list _mem_info
Definition l4virtio:807
L4virtio::Svr::Device_t< Ds_data >::Device_t
Device_t(Dev_config *dev_config)
Definition l4virtio:891
L4virtio::Svr::Device_t< Ds_data >::register_single_driver_irq
virtual void register_single_driver_irq()
Definition l4virtio:841
L4virtio::Svr::Device_t< Ds_data >::setup_queue
bool setup_queue(Virtqueue *q, unsigned qn, unsigned num_max)
Definition l4virtio:1047
L4virtio::Svr::Device_t< Ds_data >::device_notify_irq
virtual L4::Cap< L4::Irq > device_notify_irq() const
Definition l4virtio:848
L4virtio::Svr::Device_t< Ds_data >::reset_queue_config
void reset_queue_config(unsigned idx, unsigned num_max, bool inc_generation=false)
Definition l4virtio:1002
L4virtio::Svr::Device_t< Ds_data >::check_queues
virtual bool check_queues()=0
L4virtio::Svr::Driver_mem_list_t
List of driver memory regions assigned to a single L4-VIRTIO transport instance.
Definition l4virtio:630
L4virtio::Svr::Driver_mem_region_t
Region of driver memory, that shall be managed locally.
Definition l4virtio:451
L4virtio::Svr::Driver_mem_region_t::local
T * local(Ptr< T > p) const
Get the local address for driver address p.
Definition l4virtio:616
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::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
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::Virtqueue::Desc
Descriptor in the descriptor table.
Definition virtqueue:87
L4virtio::Virtqueue::Desc::len
l4_uint32_t len
Length of described buffer.
Definition virtqueue:109
L4virtio::Virtqueue::Desc::addr
Ptr< void > addr
Address stored in descriptor.
Definition virtqueue:108
L4virtio::Virtqueue
Low-level Virtqueue.
Definition virtqueue:81
L4virtio::Virtqueue::no_notify_guest
bool no_notify_guest() const
Get the no IRQ flag of this queue.
Definition virtqueue:414
L4virtio::Virtqueue::ready
bool ready() const
Test if this queue is in working state.
Definition virtqueue:400
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_EEXIST
@ L4_EEXIST
Already exists.
Definition err.h:43
L4_EINVAL
@ L4_EINVAL
Invalid argument.
Definition err.h:46
L4_EIO
@ L4_EIO
I/O error.
Definition err.h:35
L4_EOK
@ L4_EOK
Ok.
Definition err.h:32
l4virtio_block_header_t
struct l4virtio_block_header_t l4virtio_block_header_t
Header structure of a request for a block device.
L4VIRTIO_BLOCK_S_OK
@ L4VIRTIO_BLOCK_S_OK
Request finished successfully.
Definition virtio_block.h:34
L4VIRTIO_FEATURE_VERSION_1
@ L4VIRTIO_FEATURE_VERSION_1
Virtio protocol version 1 supported. Must be 1 for L4virtio.
Definition virtio.h:101
L4VIRTIO_ID_BLOCK
@ L4VIRTIO_ID_BLOCK
General block device.
Definition virtio.h:65
L4VIRTIO_IRQ_STATUS_VRING
@ L4VIRTIO_IRQ_STATUS_VRING
VRING IRQ pending flag.
Definition virtio.h:112
L4VIRTIO_IRQ_STATUS_CONFIG
@ L4VIRTIO_IRQ_STATUS_CONFIG
CONFIG IRQ pending flag.
Definition virtio.h:113
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::cap_cast
Cap< T > cap_cast(Cap< F > const &c) noexcept
static_cast for capabilities.
Definition capability.h:416
L4virtio
L4-VIRTIO Transport C++ API.
Definition l4virtio:26
L4::Epiface::server_iface
Server_iface * server_iface() const
Get pointer to server interface at which the object is currently registered.
Definition ipc_epiface:213
L4virtio::Svr::Bad_descriptor
Exception used by Queue to indicate descriptor errors.
Definition virtio:398
L4virtio::Svr::Bad_descriptor::Bad_size
@ Bad_size
Invalid size of memory block.
Definition virtio:406
L4virtio::Svr::Dev_features::raw
l4_uint32_t raw
The raw value of the features bitmap.
Definition virtio:68
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_block_config_t
Device configuration for block devices.
Definition virtio_block.h:69
l4virtio_block_header_t
Header structure of a request for a block device.
Definition virtio_block.h:43
unique_cap
Unique_cap / Unique_del_cap.