|  | 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 |