Loading...
Searching...
No Matches
l4virtio
// vi:ft=cpp
/* SPDX-License-Identifier: MIT */
/*
* Copyright (C) 2015-2020, 2022, 2024 Kernkonzept GmbH.
* Author(s): Sarah Hoffmann <sarah.hoffmann@kernkonzept.com>
*
*/
#pragma once
#include <l4/sys/factory>
#include <l4/sys/semaphore>
#include <l4/re/dataspace>
#include <l4/re/env>
#include <l4/re/util/unique_cap>
#include <l4/re/util/object_registry>
#include <l4/re/error_helper>
#include <l4/util/atomic.h>
#include <l4/util/bitops.h>
#include <l4/l4virtio/l4virtio>
#include <l4/l4virtio/virtqueue>
#include <l4/sys/consts.h>
#include <cstring>
namespace L4virtio { namespace Driver {
class Device
{
public:
void driver_connect(L4::Cap<L4virtio::Device> srvcap, bool manage_notify = true)
{
_device = srvcap;
_next_devaddr = L4_SUPERPAGESIZE;
auto *e = L4Re::Env::env();
// Set up the virtio configuration page.
_config_cap = L4Re::chkcap(L4Re::Util::make_unique_cap<L4Re::Dataspace>(),
"Allocate config dataspace capability");
l4_addr_t ds_offset;
L4Re::chksys(_device->device_config(_config_cap.get(), &ds_offset),
"Request virtio config page");
if (ds_offset & ~L4_PAGEMASK)
L4Re::chksys(-L4_EINVAL, "Virtio config page is page aligned.");
L4Re::chksys(e->rm()->attach(&_config, L4_PAGESIZE,
L4Re::Rm::F::Search_addr | L4Re::Rm::F::RW,
L4::Ipc::make_cap_rw(_config_cap.get()), ds_offset,
L4_PAGESHIFT),
"Attach config dataspace");
if (memcmp(&_config->magic, "virt", 4) != 0)
L4Re::chksys(-L4_ENODEV, "Device config has wrong magic value");
if (_config->version != 2)
L4Re::chksys(-L4_ENODEV, "Invalid virtio version, must be 2");
_device->set_status(0); // reset
int status = L4VIRTIO_STATUS_ACKNOWLEDGE;
_device->set_status(status);
status |= L4VIRTIO_STATUS_DRIVER;
_device->set_status(status);
if (_config->fail_state())
L4Re::chksys(-L4_EIO, "Device failure during initialisation.");
// Set up the interrupt used to notify the device about events.
// (only supporting one interrupt with index 0 at the moment)
_host_irq = L4Re::chkcap(L4Re::Util::make_unique_cap<L4::Irq>(),
"Allocate host IRQ capability");
L4Re::chksys(_device->device_notification_irq(0, _host_irq.get()),
"Request device notification interrupt.");
// Set up the interrupt to get notifications from the device.
// (only supporting one interrupt with index 0 at the moment)
if (manage_notify)
{
_driver_notification =
L4Re::chkcap(L4Re::Util::make_unique_cap<L4::Semaphore>(),
"Allocate notification capability");
L4Re::chksys(l4_error(e->factory()->create(_driver_notification.get())),
"Create semaphore for notifications from device");
L4Re::chksys(_device->bind(0, _driver_notification.get()),
"Bind driver notification interrupt");
}
}
int bind_notification_irq(unsigned index, L4::Cap<L4::Triggerable> irq) const
{ return l4_error(_device->bind(index, irq)); }
bool fail_state() const { return _config->fail_state(); }
bool feature_negotiated(unsigned int feat) const
{ return l4virtio_get_feature(_config->driver_features_map, feat); }
int driver_acknowledge()
{
if (!l4virtio_get_feature(_config->dev_features_map,
L4VIRTIO_FEATURE_VERSION_1))
L4Re::chksys(-L4_ENODEV,
"Require Virtio 1.0 device; Legacy device not supported.");
_config->driver_features_map[0] &= _config->dev_features_map[0];
_config->driver_features_map[1] &= _config->dev_features_map[1];
_device->set_status(_config->status | L4VIRTIO_STATUS_FEATURES_OK);
if (!(_config->status & L4VIRTIO_STATUS_FEATURES_OK))
L4Re::chksys(-L4_EINVAL, "Negotiation of device features.");
_device->set_status(_config->status | L4VIRTIO_STATUS_DRIVER_OK);
if (_config->fail_state())
return -L4_EIO;
return L4_EOK;
}
int register_ds(L4::Cap<L4Re::Dataspace> ds, l4_umword_t offset,
l4_umword_t size, l4_uint64_t *devaddr)
{
*devaddr = next_device_address(size);
return _device->register_ds(L4::Ipc::make_cap_rw(ds), *devaddr, offset, size);
}
int config_queue(int num, unsigned size, l4_uint64_t desc_addr,
l4_uint64_t avail_addr, l4_uint64_t used_addr)
{
auto *queueconf = &_config->queues()[num];
queueconf->num = size;
queueconf->desc_addr = desc_addr;
queueconf->avail_addr = avail_addr;
queueconf->used_addr = used_addr;
queueconf->ready = 1;
return _device->config_queue(num);
}
int max_queue_size(int num) const
{
return _config->queues()[num].num_max;
}
int send_and_wait(Virtqueue &queue, l4_uint16_t descno)
{
send(queue, descno);
// wait for a reply, we assume that no other
// request will get in the way.
auto head = wait_for_next_used(queue);
if (head < 0)
return head;
return (head == descno) ? L4_EOK : -L4_EINVAL;
}
int wait(int index) const
{
if (index != 0)
return -L4_EEXIST;
return l4_ipc_error(_driver_notification->down(), l4_utcb());
}
int wait_for_next_used(Virtqueue &queue, l4_uint32_t *len = nullptr) const
{
while (true)
{
int err = wait(0);
if (err < 0)
return err;
auto head = queue.find_next_used(len);
if (head != Virtqueue::Eoq) // spurious interrupt?
return head;
}
}
void send(Virtqueue &queue, l4_uint16_t descno)
{
queue.enqueue_descriptor(descno);
notify(queue);
}
void notify(Virtqueue &queue)
{
if (!queue.no_notify_host())
_host_irq->trigger();
}
private:
l4_uint64_t next_device_address(l4_umword_t size)
{
l4_umword_t ret;
size = l4_round_page(size);
do
{
ret = _next_devaddr;
if (l4_umword_t(~0) - ret < size)
L4Re::chksys(-L4_ENOMEM, "Out of device address space.");
}
while (!l4util_cmpxchg(&_next_devaddr, ret, ret + size));
return ret;
}
protected:
L4::Cap<L4virtio::Device> _device;
L4Re::Rm::Unique_region<L4virtio::Device::Config_hdr *> _config;
l4_umword_t _next_devaddr;
L4Re::Util::Unique_cap<L4::Semaphore> _driver_notification;
private:
L4Re::Util::Unique_cap<L4::Irq> _host_irq;
L4Re::Util::Unique_cap<L4Re::Dataspace> _config_cap;
};
} }
bitops.h
bit manipulation functions
L4Re::Env::env
static Env const * env() noexcept
Returns the initial environment for the current task.
Definition env:96
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::wait
int wait(int index) const
Wait for a notification from the device.
Definition l4virtio:268
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::bind_notification_irq
int bind_notification_irq(unsigned index, L4::Cap< L4::Triggerable > irq) const
Register a triggerable to receive notifications from the device.
Definition l4virtio:129
L4virtio::Driver::Device::wait_for_next_used
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
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::fail_state
bool fail_state() const
Return true if the device is in a fail state.
Definition l4virtio:133
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::feature_negotiated
bool feature_negotiated(unsigned int feat) const
Check if a particular feature bit was negotiated with the device.
Definition l4virtio:145
L4virtio::Driver::Device::driver_acknowledge
int driver_acknowledge()
Finalize handshake with the device.
Definition l4virtio:156
L4virtio::Driver::Device::send_and_wait
int send_and_wait(Virtqueue &queue, l4_uint16_t descno)
Send a request to the device and wait for it to be processed.
Definition l4virtio:247
L4virtio::Driver::Virtqueue
Driver-side implementation of a Virtqueue.
Definition virtqueue:471
L4virtio::Driver::Virtqueue::enqueue_descriptor
void enqueue_descriptor(l4_uint16_t descno)
Enqueue a descriptor in the available ring.
Definition virtqueue:574
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::Virtqueue::no_notify_host
bool no_notify_host() const
Get the no notify flag of this queue.
Definition virtqueue:426
dataspace
Dataspace interface.
env
Environment interface.
error_helper
Error helper.
factory
Common factory related definitions.
l4_umword_t
unsigned long l4_umword_t
Unsigned machine word.
Definition l4int.h:40
l4_addr_t
unsigned long l4_addr_t
Address type.
Definition l4int.h:34
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_ENODEV
@ L4_ENODEV
No such thing.
Definition err.h:44
L4_EIO
@ L4_EIO
I/O error.
Definition err.h:35
L4_EOK
@ L4_EOK
Ok.
Definition err.h:32
L4_ENOMEM
@ L4_ENOMEM
No memory.
Definition err.h:39
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_ipc_error
l4_umword_t l4_ipc_error(l4_msgtag_t tag, l4_utcb_t *utcb) L4_NOTHROW
Get the IPC error code for an IPC operation.
Definition ipc.h:629
L4_SUPERPAGESIZE
#define L4_SUPERPAGESIZE
Size of a large page.
Definition consts.h:418
l4_round_page
l4_addr_t l4_round_page(l4_addr_t address) L4_NOTHROW
Round address up to the next page.
Definition consts.h:473
L4_PAGESIZE
#define L4_PAGESIZE
Minimal page size (in bytes).
Definition consts.h:391
L4_PAGESHIFT
#define L4_PAGESHIFT
Size of a page, log2-based.
Definition consts.h:26
L4_PAGEMASK
#define L4_PAGEMASK
Mask for the page number.
Definition consts.h:400
l4_utcb
l4_utcb_t * l4_utcb(void) L4_NOTHROW L4_PURE
Get the UTCB address.
Definition utcb.h:346
l4util_cmpxchg
int l4util_cmpxchg(volatile l4_umword_t *dest, l4_umword_t cmp_val, l4_umword_t new_val)
Atomic compare and exchange (machine wide fields).
Definition atomic.h:379
l4virtio_get_feature
unsigned l4virtio_get_feature(l4_uint32_t *feature_map, unsigned feat)
Check if the given bit in a feature map is set.
Definition virtio.h:296
L4VIRTIO_FEATURE_VERSION_1
@ L4VIRTIO_FEATURE_VERSION_1
Virtio protocol version 1 supported. Must be 1 for L4virtio.
Definition virtio.h:101
L4VIRTIO_STATUS_DRIVER
@ L4VIRTIO_STATUS_DRIVER
Guest OS knows how to drive device.
Definition virtio.h:90
L4VIRTIO_STATUS_ACKNOWLEDGE
@ L4VIRTIO_STATUS_ACKNOWLEDGE
Guest OS has found device.
Definition virtio.h:89
L4VIRTIO_STATUS_FEATURES_OK
@ L4VIRTIO_STATUS_FEATURES_OK
Driver has acknowledged feature set.
Definition virtio.h:92
L4VIRTIO_STATUS_DRIVER_OK
@ L4VIRTIO_STATUS_DRIVER_OK
Driver is set up.
Definition virtio.h:91
consts.h
Common constants.
atomic.h
atomic operations header and generic implementations
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
semaphore
Semaphore class definition.
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
unique_cap
Unique_cap / Unique_del_cap.