This commit adds sections to the virtio docs with the currently supported transfer methods, drivers and samples Signed-off-by: Jakub Michalski <jmichalski@antmicro.com>
156 lines
7.6 KiB
ReStructuredText
156 lines
7.6 KiB
ReStructuredText
Virtual I/O (VIRTIO)
|
|
##########################
|
|
|
|
Overview
|
|
********
|
|
|
|
Virtual I/O (VIRTIO) is a protocol used for communication with various devices, typically used in
|
|
virtualized environments. Its main goal is to provide an efficient and standardized mechanism for
|
|
interfacing with virtual devices from within a virtual machine. The communication relies on virtqueues
|
|
and standard transfer methods like PCI or MMIO.
|
|
|
|
Concepts
|
|
********
|
|
|
|
Virtio defines various components used during communication and initialization. It specifies both the
|
|
host (named "device" in the specification) and guest (named "driver" in the specification) sides.
|
|
Currently Zephyr can only work as a guest. On top of the facilities exposed by the Virtio driver,
|
|
a driver for a specific device (e.g. network card) can be implemented.
|
|
|
|
A high-level overview of a system with a Virtio device is shown below.
|
|
|
|
.. graphviz::
|
|
:caption: Virtual I/O overview
|
|
|
|
digraph {
|
|
|
|
subgraph cluster_host {
|
|
style=filled;
|
|
color=lightgrey;
|
|
label = "Host";
|
|
labeljust=r;
|
|
|
|
virtio_device [label = "virtio device"];
|
|
}
|
|
|
|
transfer_method [label = "virtio transfer method"];
|
|
|
|
subgraph cluster_guest {
|
|
style=filled;
|
|
color=lightgrey;
|
|
label = "Guest";
|
|
labeljust=r;
|
|
|
|
virtio_driver [label = "virtio driver"];
|
|
specific_device_driver [label = "specific device driver"];
|
|
device_user [label = "device user"];
|
|
}
|
|
|
|
virtio_device -> transfer_method;
|
|
transfer_method -> virtio_device;
|
|
transfer_method -> virtio_driver;
|
|
virtio_driver -> transfer_method;
|
|
virtio_driver -> specific_device_driver;
|
|
specific_device_driver -> virtio_driver;
|
|
specific_device_driver -> device_user;
|
|
device_user -> specific_device_driver;
|
|
}
|
|
|
|
Configuration space
|
|
===================
|
|
Each device provides configuration space, used for initialization and configuration. It allows
|
|
selection of device and driver features, enabling specific virtqueues and setting their addresses.
|
|
Once the device is configured, most of its configuration cannot be changed without resetting the device.
|
|
The exact layout of the configuration space depends on the transfer method.
|
|
|
|
Driver and device features
|
|
--------------------------
|
|
The configuration space provides a way to negotiate feature bits, determining some non-mandatory
|
|
capabilities of the devices. The exact available feature bits depend on the device and platform.
|
|
|
|
Device-specific configuration
|
|
-----------------------------
|
|
Some of the devices offer device-specific configuration space, providing additional configuration options.
|
|
|
|
Virtqueues
|
|
==========
|
|
The main mechanism used for transferring data between host and guest is a virtqueue. Specific
|
|
devices have different numbers of virtqueues, for example devices supporting bidirectional transfer
|
|
usually have one or more tx/rx virtqueue pairs. Virtio specifies two types of virtqueues: split
|
|
virtqueues and packed virtqueues. Zephyr currently supports only split virtqueues.
|
|
|
|
Split virtqueues
|
|
----------------
|
|
A split virtqueue consists of three parts: descriptor table, available ring and used ring.
|
|
|
|
The descriptor table holds descriptors of buffers, that is their physical addresses, lengths and flags.
|
|
Each descriptor is either device writeable or driver writeable. The descriptors can be chained, creating
|
|
descriptor chains. Typically a chain begins with descriptors containing the data for the device to read
|
|
and ends with the device writeable part, where the device places its response.
|
|
|
|
The main part of the available ring is a circular buffer of references (in the form of indexes) to the
|
|
descriptors in the descriptor table. Once the guest decides to send the data to the host, it adds the index of
|
|
the head of the descriptor chain to the top of the available ring.
|
|
|
|
The used ring is similar to the available ring, but it's used by the host to return descriptors to the guest. In
|
|
addition to storing descriptor indexes, it also provides information about the amount of data written to them.
|
|
|
|
Common Virtio libraries
|
|
***********************
|
|
|
|
Zephyr provides an API for interfacing with Virtio devices and virtqueues, which allows performing necessary operations
|
|
over the lifetime of the Virtio device.
|
|
|
|
Device initialization
|
|
=====================
|
|
Once the Virtio driver finishes performing low-level initialization common to the all devices using a given transfer method,
|
|
like finding device on the bus and mapping Virtio structures, the device specific driver steps in and performs the next
|
|
stages of initialization with the help of the Virtio API.
|
|
|
|
The first thing the device-specific driver does is feature bits negotiation. It uses :c:func:`virtio_read_device_feature_bit`
|
|
to determine which features the device offers, and then selects the ones it needs using :c:func:`virtio_write_driver_feature_bit`.
|
|
After all required features have been selected, the device-specific driver calls :c:func:`virtio_commit_feature_bits`. Then, virtqueues
|
|
are initialized with :c:func:`virtio_init_virtqueues`. This function enumerates the virtqueues, invoking the provided callback
|
|
:c:type:`virtio_enumerate_queues` to determine the required size of each virtqueue. Initialization process is finalized by calling
|
|
:c:func:`virtio_finalize_init`. From this point, if none of the functions returned errors, the virtqueues are operational. If the
|
|
specific device provides one, the device-specific config can be obtained by calling :c:func:`virtio_get_device_specific_config`.
|
|
|
|
Virtqueue operation
|
|
===================
|
|
Once the virtqueues are operational, they can be used to send and receive data. To do so, the pointer to the nth
|
|
virtqueue has to be acquired using :c:func:`virtio_get_virtqueue`. To send data consisting of a descriptor chain,
|
|
:c:func:`virtq_add_buffer_chain` has to be used. Along the descriptor chain, it takes pointer to the callback that
|
|
will be invoked once the device returns the given descriptor chain. After that, the virtqueue has to be notified using
|
|
:c:func:`virtio_notify_virtqueue` from the Virtio API.
|
|
|
|
Guest-side Virtio drivers
|
|
*************************
|
|
Currently Zephyr provides drivers for Virtio over PCI and Virtio over MMIO and drivers for two devices using virtio - virtiofs, used
|
|
to access the filesystem of the host and virtio-entropy, used as an entropy source.
|
|
|
|
Virtiofs
|
|
=========
|
|
This driver provides support for `virtiofs <https://virtio-fs.gitlab.io/>`_ - a filesystem allowing a virtual machine guest to access
|
|
a directory on the host. It uses FUSE messages to communicate between the host and the guest in order to perform filesystem operations such as
|
|
opening and reading files. Every time the guest wants to perform some filesystem operation it places in the virtqueue a descriptor chain
|
|
starting with the device readable part, containing the FUSE input header and input data, and ending it with the device writeable part, with place
|
|
for the FUSE output header and output data.
|
|
|
|
Virtio-entropy
|
|
==============
|
|
This driver allows using virtio-entropy as an entropy source in Zephyr. The operation of this device is simple - the driver places a
|
|
buffer in the virtqueue and receives it back, filled with random data.
|
|
|
|
Virtio samples
|
|
**************
|
|
A sample showcasing the use of a driver relying on Virtio is provided in :zephyr:code-sample:`virtiofs`. If you wish
|
|
to check code interfacing directly with the Virtio driver, you can check the virtiofs driver, especially :c:func:`virtiofs_init`
|
|
for initialization and :c:func:`virtiofs_send_receive` with the :c:func:`virtiofs_recv_cb` for data transfer to/from
|
|
the Virtio device.
|
|
|
|
API Reference
|
|
*************
|
|
|
|
.. doxygengroup:: virtio_interface
|
|
.. doxygengroup:: virtqueue_interface
|