15 #include <l4/re/util/object_registry>
20 #include <l4/l4virtio/l4virtio>
21 #include <l4/l4virtio/virtqueue>
22 #include <l4/l4virtio/virtio_block.h>
23 #include <l4/sys/consts.h>
70 _config_cap =
L4Re::chkcap(L4Re::Util::make_unique_cap<L4Re::Dataspace>(),
71 "Allocate config dataspace capability");
75 "Request virtio config page");
84 "Attach config dataspace");
86 if (memcmp(&_config->magic,
"virt", 4) != 0)
89 if (_config->version != 2)
99 if (_config->fail_state())
105 _host_irq =
L4Re::chkcap(L4Re::Util::make_unique_cap<L4::Irq>(),
106 "Allocate host IRQ capability");
109 "Request device notification interrupt.");
115 _driver_notification =
116 L4Re::chkcap(L4Re::Util::make_unique_cap<L4::Semaphore>(),
117 "Allocate notification capability");
120 "Create semaphore for notifications from device");
123 "Bind driver notification interrupt");
152 "Require Virtio 1.0 device; Legacy device not supported.");
154 _config->driver_features_map[0] &= _config->dev_features_map[0];
155 _config->driver_features_map[1] &= _config->dev_features_map[1];
164 if (_config->fail_state())
190 *devaddr = next_device_address(size);
206 auto *queueconf = &_config->queues()[num];
207 queueconf->num = size;
208 queueconf->desc_addr = desc_addr;
209 queueconf->avail_addr = avail_addr;
210 queueconf->used_addr = used_addr;
211 queueconf->ready = 1;
223 return _config->queues()[num].num_max;
287 if (head != Virtqueue::Eoq)
302 _host_irq->trigger();
333 L4Re::Rm::Unique_region<L4virtio::Device::Config_hdr *> _config;
349 typedef std::function<void(
unsigned char)> Callback;
359 Request() : tail(Virtqueue::Eoq), callback(0) {}
374 Handle() : head(Virtqueue::Eoq) {}
375 bool valid()
const {
return head != Virtqueue::Eoq; }
408 if (_config->num_queues != 1)
419 l4_uint64_t const status_offset = header_offset + queuesz * Header_size;
424 totalsz += usermem_offset;
426 _queue_ds =
L4Re::chkcap(L4Re::Util::make_unique_cap<L4Re::Dataspace>(),
427 "Allocate queue dataspace capability");
429 L4Re::chksys(e->mem_alloc()->alloc(totalsz, _queue_ds.get(),
432 "Allocate memory for virtio structures");
439 "Attach dataspace for virtio structures");
443 "Register queue dataspace with device");
445 _queue.
init_queue(queuesz, _queue_region.get());
450 _header_addr = devaddr + header_offset;
454 _status_addr = devaddr + status_offset;
455 _status = _queue_region.get() + status_offset;
457 user_devaddr =
Ptr<void>(devaddr + usermem_offset);
459 *userdata = _queue_region.get() + usermem_offset;
462 _pending.assign(queuesz, Request());
465 _config->driver_features_map[0] = fmask0;
466 _config->driver_features_map[1] = fmask1;
490 if (descno == Virtqueue::Eoq)
491 return Handle(Virtqueue::Eoq);
494 Request &req = _pending[descno];
504 desc.
len = Header_size;
508 req.callback = callback;
527 if (descno == Virtqueue::Eoq)
530 Request &req = _pending[handle.head];
540 if (_headers[handle.head].
type > 0)
564 if (descno == Virtqueue::Eoq)
567 Request &req = _pending[handle.head];
581 send(_queue, handle.head);
599 if (descno == Virtqueue::Eoq)
613 _pending[handle.head].tail = descno;
616 unsigned char status = _status[descno];
617 free_request(handle);
632 void free_request(Handle handle)
634 if (handle.head != Virtqueue::Eoq
635 && _pending[handle.head].tail != Virtqueue::Eoq)
637 _pending[handle.head].tail = Virtqueue::Eoq;
649 descno != Virtqueue::Eoq;
653 if (descno >= _queue.
num() || _pending[descno].tail == Virtqueue::Eoq)
656 unsigned char status = _status[descno];
657 free_request(
Handle(descno));
659 if (_pending[descno].callback)
660 _pending[descno].callback(status);
668 L4Re::Rm::Unique_region<unsigned char *> _queue_region;
670 unsigned char *_status;
674 std::vector<Request> _pending;
#define L4_PAGESHIFT
Size of a page, log2-based.
unsigned int l4_size_t
Unsigned size type.
bit manipulation functions
static Env const * env() noexcept
Returns the initial environment for the current task.
@ Continuous
Allocate physically contiguous memory.
@ Pinned
Deprecated, use L4Re::Dma_space instead.
l4_msgtag_t bind(unsigned irqnum, L4::Cap< Triggerable > irq, l4_utcb_t *utcb=l4_utcb()) noexcept
Bind an interrupt line of an interrupt controller to an interrupt object.
long register_ds(L4::Ipc::Cap< L4Re::Dataspace > ds_cap, l4_uint64_t base, l4_umword_t offset, l4_umword_t size)
Register a shared data space with VIRTIO host.
long device_notification_irq(unsigned index, L4::Ipc::Out< L4::Cap< L4::Triggerable > > irq)
Get the notification interrupt corresponding to the given index.
long config_queue(unsigned queue)
Trigger queue configuration of the given queue.
long device_config(L4::Ipc::Out< L4::Cap< L4Re::Dataspace > > config_ds, l4_addr_t *ds_offset)
Get the dataspace with the L4virtio configuration page.
long set_status(unsigned status)
Write the VIRTIO status register.
Handle to an ongoing request.
Simple class for accessing a virtio block device synchronously.
int send_request(Handle handle)
Process request asynchronously.
int add_block(Handle handle, Ptr< void > addr, l4_uint32_t size)
Add a data block to a request that has already been set up.
int process_request(Handle handle)
Process request synchronously.
void setup_device(L4::Cap< L4virtio::Device > srvcap, l4_size_t usermem, void **userdata, Ptr< void > &user_devaddr, l4_uint32_t fmask0=-1U, l4_uint32_t fmask1=-1U)
Setup a connection to a device and set up shared memory.
void process_used_queue()
Process and free all items in the used queue.
l4virtio_block_config_t const & device_config() const
Return a reference to the device configuration.
Handle start_request(l4_uint64_t sector, l4_uint32_t type, Callback callback)
Start the setup of a new request.
Client-side implementation for a general virtio device.
int wait(int index) const
Wait for a notification from the device.
int max_queue_size(int num) const
Maximum queue size allowed by the device.
int wait_for_next_used(Virtqueue &queue) const
Wait for the next item to arrive in the used queue and return it.
int bind_notification_irq(unsigned index, L4::Cap< L4::Triggerable > irq) const
Register a triggerable to receive notifications from the device.
void driver_connect(L4::Cap< L4virtio::Device > srvcap, bool manage_notify=true)
Contacts the device and starts the initial handshake.
void send(Virtqueue &queue, l4_uint16_t descno)
Send a request to the device.
bool fail_state() const
Return true if the device is in a fail state.
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.
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.
int driver_acknowledge()
Finalize handshake with the device.
int send_and_wait(Virtqueue &queue, l4_uint16_t descno)
Send a request to the device and wait for it to be processed.
Driver-side implementation of a Virtqueue.
void free_descriptor(l4_uint16_t head, l4_uint16_t tail)
Free a chained list of descriptors in the descriptor queue.
void enqueue_descriptor(l4_uint16_t descno)
Enqueue a descriptor in the available ring.
Desc & desc(l4_uint16_t descno)
Return a reference to a descriptor in the descriptor table.
l4_uint16_t alloc_descriptor()
Allocate and return an unused descriptor from the descriptor table.
l4_uint16_t find_next_used(l4_uint32_t *len=nullptr)
Return the next finished block.
void init_queue(unsigned num, void *desc, void *avail, void *used)
Initialize this virtqueue.
Descriptor in the descriptor table.
l4_uint16_t next
Index of the next chained descriptor.
l4_uint32_t len
Length of described buffer.
Flags flags
Descriptor flags.
Ptr< void > addr
Address stored in descriptor.
unsigned long avail_offset() const
Get the offset of the available ring from the descriptor table.
static unsigned long total_size(unsigned num)
Calculate the total size for a virtqueue of the given dimensions.
unsigned long used_offset() const
Get the offset of the used ring from the descriptor table.
bool no_notify_host() const
Get the no notify flag of this queue.
Common factory related definitions.
unsigned long l4_umword_t
Unsigned machine word.
unsigned long l4_addr_t
Address type.
unsigned int l4_uint32_t
Unsigned 32bit value.
unsigned short int l4_uint16_t
Unsigned 16bit value.
unsigned long long l4_uint64_t
Unsigned 64bit value.
@ L4_EEXIST
Already exists.
@ L4_EINVAL
Invalid argument.
@ L4_ENODEV
No such thing.
long l4_error(l4_msgtag_t tag) L4_NOTHROW
Return error code of a system call return message tag or the tag label.
l4_umword_t l4_ipc_error(l4_msgtag_t tag, l4_utcb_t *utcb) L4_NOTHROW
Get the error code for an object invocation.
#define L4_SUPERPAGESIZE
Size of a large page.
l4_addr_t l4_round_page(l4_addr_t address) L4_NOTHROW
Round address up to the next page.
#define L4_PAGESIZE
Minimal page size (in bytes).
#define L4_PAGEMASK
Mask for the page number.
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.
l4_utcb_t * l4_utcb(void) L4_NOTHROW L4_PURE
Get the UTCB address.
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)
int l4util_bsr(l4_umword_t word)
Bit scan reverse.
struct l4virtio_block_header_t l4virtio_block_header_t
Header structure of a request for a block device.
@ L4VIRTIO_BLOCK_S_IOERR
IO error on device.
@ L4VIRTIO_BLOCK_S_UNSUPP
Operation is not supported.
@ L4VIRTIO_BLOCK_S_OK
Request finished successfully.
unsigned l4virtio_get_feature(l4_uint32_t *feature_map, unsigned feat)
Check if the given bit in a feature map is set.
@ L4VIRTIO_FEATURE_VERSION_1
Virtio protocol version 1 supported. Must be 1 for L4virtio.
@ L4VIRTIO_STATUS_DRIVER
Guest OS knows how to drive device.
@ L4VIRTIO_STATUS_ACKNOWLEDGE
Guest OS has found device.
@ L4VIRTIO_STATUS_FEATURES_OK
Driver has acknowledged feature set.
@ L4VIRTIO_STATUS_DRIVER_OK
Driver is set up.
@ L4VIRTIO_ID_BLOCK
General block device.
atomic operations header and generic implementations
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.
long chksys(long err, char const *extra="", long ret=0)
Generate C++ exception on error.
T chkcap(T &&cap, char const *extra="", long err=-L4_ENOMEM)
Check for valid capability or raise C++ exception.
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.
L4-VIRTIO Transport C++ API.
Semaphore class definition.
@ Search_addr
Search for a suitable address range.
write_bfm_t::Val write() const
Get the write bits ( 1 to 1 ) of raw.
next_bfm_t::Val next() const
Get the next bits ( 0 to 0 ) of raw.
l4_uint16_t raw
raw flags value of a virtio descriptor.
Device configuration for block devices.
Unique_cap / Unique_del_cap.