VirtIO Framework
Introduction
NuttX implements a complete VirtIO framework based on OpenAMP. The framework supports various VirtIO drivers compatible with the VirtIO standard (such as VirtIO-Net, VirtIO-Block, etc.) at the upper layer, and different VirtIO transport layer implementations (including VirtIO-MMIO, VirtIO-PCI, VirtIO-Remoteproc, etc.) at the lower layer.
Architecture
Framework Overview
The VirtIO framework consists of three main layers:
+------------------------------------------------------------------+
| VirtIO Driver Layer |
| +----------+ +----------+ +----------+ +-----+ +-------------+ |
| | VirtIO | | VirtIO | | VirtIO | | ... | | VirtIO | |
| | Sock | | Net | | Blk | | | | Rpmsg | |
| +----------+ +----------+ +----------+ +-----+ +-------------+ |
+------------------------------------------------------------------+
|
v
+------------------------------------------------------------------+
| VirtIO Framework Layer |
| VirtIO Framework |
+------------------------------------------------------------------+
|
+---------------------+---------------------+
| | |
v v v
+----------------+ +----------------+ +----------------+
| VirtIO MMIO | | VirtIO PCI | | VirtIO Rptun |
+----------------+ +----------------+ +----------------+
| Memory Map | | PCI Framework | | Rptun |
+----------------+ +----------------+ +----------------+
| | PCI Controller | | Rptun Driver |
| +----------------+ +----------------+
| | | +----------------------+
| | +-----------------> | Remoteproc Processor |
v v +----------------------+
+------------------------------------------------------------------+
| Hypervisor |
+------------------------------------------------------------------+
The framework can be divided into the following three parts:
VirtIO Driver Layer: The driver layer is responsible for interfacing VirtIO with the NuttX driver framework. The driver layer completes device initialization and data interaction by calling the unified interfaces provided by VirtIO.
VirtIO Framework Layer: The VirtIO layer provides unified interfaces for drivers, supporting registration, unregistration, and matching mechanisms for Drivers and Devices.
VirtIO Transport Layer: The transport layer provides support for different transport methods, including MMIO, RemoteProc, and PCI.
Workflow
The following describes the matching process and calling relationship between VirtIO Device and VirtIO Driver:
+------------------------------------------------------------------+
| VirtIO Drivers |
| +--------+ +--------+ +--------+ +--------+ +--------+ +-------+ |
| |VirtIO | |VirtIO | |VirtIO | |VirtIO | |VirtIO | | ... | |
| |Net | |Sock | |Blk | |Serial | |Sound | | | |
| +--------+ +--------+ +--------+ +--------+ +--------+ +-------+ |
+------------------------------------------------------------------+
|
| virtio_register_driver()
v
+------------------------------------------------------------------+
| VirtIO Bus |
+------------------------------------------------------------------+
^
| virtio_register_device()
|
+------------------------------------------------------------------+
| Transport Layer |
| +----------------+ +----------------+ +----------------+ |
| | VirtIO-MMIO | | VirtIO-PCI | | VirtIO-Rptun | |
| | Device Memory | | PCI Framework | | Resource Table | |
| | and IRQ | | Device in PCI | | | |
| +----------------+ +----------------+ +----------------+ |
+------------------------------------------------------------------+
||
|| Match Success
vv
+------------------------------------------------------------------+
| virtio_xxx_probe() |
| Reset, configure, feature negotiation, memory allocation |
+------------------------------------------------------------------+
|
| VirtIO API
v
+------------------------------------------------------------------+
| Port to NuttX Various Frameworks |
| Normal Driver/Socket etc operations |
+------------------------------------------------------------------+
|
v
+------------------------------------------------------------------+
| VFS |
+------------------------------------------------------------------+
Driver Registration: During NuttX initialization,
virtio_register_drivers()is called to register all supported VirtIO Drivers to the VirtIO bus.Device Registration: The registration process is initiated by the transport layer:
MMIO transport layer calls
virtio_register_mmio_device()REMOTEPROC transport layer calls
rptun_register_device()PCI transport layer calls
virtio_pci_probe()
After the transport layer completes initialization, it calls
virtio_register_device()to register the VirtIO Device to the VirtIO bus.Driver and Device Matching: When a device is registered to the bus, the system attempts to match Driver and Device. If the match is successful, the
probefunction implemented by the Driver is executed. In theprobefunction, the driver initializes, configures, and performs feature negotiation on the VirtIO Device. Depending on the complexity and type of the device, it may also need to initialize private structures or perform additional operations.Register NuttX Driver: Call the API provided by the NuttX driver framework to register the driver to the Virtual File System (VFS) for user access.
Runtime: During runtime, the Driver calls the
virtqueuecommon interfaces provided by OpenAMP to exchange data and notifications in VirtIO standard format, thereby implementing driver functionality.
Source Code
nuttx/
├── drivers/
│ └── virtio/
│ └── virtio.c # VirtIO framework core implementation
├── include/
│ └── nuttx/
│ └── virtio/
│ └── virtio.h # VirtIO header file
└── openamp/
└── open-amp/ # OpenAMP repository
API Reference
This section describes the interfaces that need to be called during VirtIO driver adaptation.
NuttX Log Interfaces
vrtinfo(...)INFO level VirtIO system log interface.
vrtwarn(...)WARNING level VirtIO system log interface.
vrterr(...)ERROR level VirtIO system log interface.
NuttX VirtIO Framework Interface
-
int virtio_register_driver(FAR struct virtio_driver *driver)
Register a VirtIO Driver to the VirtIO bus. When a corresponding device already exists in the bus, it will immediately match and call the
probefunction implemented by the driver. If there is no corresponding device in the bus, the driver’sprobefunction will be called back after a corresponding VirtIO device is registered to the VirtIO bus to complete driver initialization.- Parameters:
driver – Pointer to the virtio_driver structure
- Returns:
0 on success, negative errno on failure
OpenAMP Interfaces
Prerequisites
Driver TX virtqueue: The driver’s transmit queue. Get a buffer from the
used ringoftxvq, fill in the data to be sent, and then add it to theavail ringoftxvqto complete the data transmission process.Driver RX virtqueue: The driver’s receive queue. Get a buffer from the
used ringofrxvq, read the data from it, and then return it to theavail ringofrxvqto complete the data reception process.
Interface Description
-
void *virtqueue_get_buffer(struct virtqueue *vq, uint32_t *len, uint16_t *idx)
Get a buffer from the
used ringof virtqueue.- Parameters:
vq – Pointer to the virtqueue
len – Length of the obtained buffer
idx – Index of the obtained buffer in the
used ring
- Returns:
Pointer to the buffer, or NULL if no buffer available
-
int virtqueue_add_buffer(struct virtqueue *vq, struct virtqueue_buf *buf_list, int readable, int writable, void *cookie)
Add a buffer to the
avail ringof virtqueue.- Parameters:
vq – Pointer to the virtqueue
buf_list – Array of buffers to be added
readable – Number of readable buffers in
buf_list, indicating the part that the Device should readwritable – Number of writable buffers in
buf_list, indicating the part that the Device should fillcookie – Cache pointer, this value will be returned when calling
virtqueue_get_bufferto get the buffer
- Returns:
0 on success, negative errno on failure
-
void virtqueue_kick(struct virtqueue *vq)
Notify the Device. Usually called after sending data to the device side or returning a buffer to the device side to notify the device that it can proceed to the next operation.
- Parameters:
vq – Pointer to the virtqueue
-
void virtqueue_enable_cb(struct virtqueue *vq)
Enable virtqueue interrupt.
- Parameters:
vq – Pointer to the virtqueue
-
void virtqueue_disable_cb(struct virtqueue *vq)
Disable virtqueue interrupt.
- Parameters:
vq – Pointer to the virtqueue