Skip to main content

virtio_drivers_and_devices/
lib.rs

1//! VirtIO guest drivers.
2//!
3//! These drivers can be used by bare-metal code (such as a bootloader or OS kernel) running in a VM
4//! to interact with VirtIO devices provided by the VMM (such as QEMU or crosvm).
5//!
6//! # Usage
7//!
8//! You must first implement the [`Hal`] trait, to allocate DMA regions and translate between
9//! physical addresses (as seen by devices) and virtual addresses (as seen by your program). You can
10//! then construct the appropriate transport for the VirtIO device, e.g. for an MMIO device (perhaps
11//! discovered from the device tree):
12//!
13//! ```
14//! use core::ptr::NonNull;
15//! use virtio_drivers_and_devices::transport::mmio::{MmioTransport, VirtIOHeader};
16//!
17//! # fn example(mmio_device_address: usize, mmio_size: usize) {
18//! let header = NonNull::new(mmio_device_address as *mut VirtIOHeader).unwrap();
19//! let transport = unsafe { MmioTransport::new(header, mmio_size) }.unwrap();
20//! # }
21//! ```
22//!
23//! You can then check what kind of VirtIO device it is and construct the appropriate driver:
24//!
25//! ```
26//! # use virtio_drivers_and_devices::Hal;
27//! # #[cfg(feature = "alloc")]
28//! use virtio_drivers_and_devices::{
29//!     device::console::VirtIOConsole,
30//!     transport::{mmio::MmioTransport, DeviceType, Transport},
31//! };
32
33//!
34//! # #[cfg(feature = "alloc")]
35//! # fn example<HalImpl: Hal>(transport: MmioTransport) {
36//! if transport.device_type() == DeviceType::Console {
37//!     let mut console = VirtIOConsole::<HalImpl, _>::new(transport).unwrap();
38//!     // Send a byte to the console.
39//!     console.send(b'H').unwrap();
40//! }
41//! # }
42//! ```
43
44#![cfg_attr(not(test), no_std)]
45#![deny(unused_must_use, missing_docs, clippy::undocumented_unsafe_blocks)]
46#![allow(clippy::identity_op)]
47#![allow(dead_code)]
48
49#[cfg(any(feature = "alloc", test))]
50extern crate alloc;
51
52mod config;
53pub mod device;
54#[cfg(feature = "embedded-io")]
55mod embedded_io;
56mod hal;
57mod queue;
58pub mod transport;
59mod volatile;
60
61use core::ptr::{self, NonNull};
62use device::socket::SocketError;
63use thiserror::Error;
64
65pub use self::hal::{BufferDirection, DeviceHal, Hal, PhysAddr};
66
67/// The page size in bytes supported by the library (4 KiB).
68pub const PAGE_SIZE: usize = 0x1000;
69
70/// The type returned by driver methods.
71pub type Result<T = ()> = core::result::Result<T, Error>;
72
73/// The error type of VirtIO drivers.
74#[derive(Copy, Clone, Debug, Eq, Error, PartialEq)]
75pub enum Error {
76    /// There are not enough descriptors available in the virtqueue, try again later.
77    #[error("Virtqueue is full")]
78    QueueFull,
79    /// The device is not ready.
80    #[error("Device not ready")]
81    NotReady,
82    /// The device used a different descriptor chain to the one we were expecting.
83    #[error("Device used a different descriptor chain to the one we were expecting")]
84    WrongToken,
85    /// The queue is already in use.
86    #[error("Virtqueue is already in use")]
87    AlreadyUsed,
88    /// Invalid parameter.
89    #[error("Invalid parameter")]
90    InvalidParam,
91    /// Failed to allocate DMA memory.
92    #[error("Failed to allocate DMA memory")]
93    DmaError,
94    /// I/O error
95    #[error("I/O error")]
96    IoError,
97    /// The request was not supported by the device.
98    #[error("Request not supported by device")]
99    Unsupported,
100    /// The config space advertised by the device is smaller than the driver expected.
101    #[error("Config space advertised by the device is smaller than expected")]
102    ConfigSpaceTooSmall,
103    /// The device doesn't have any config space, but the driver expects some.
104    #[error("The device doesn't have any config space, but the driver expects some")]
105    ConfigSpaceMissing,
106    /// Error from the socket device.
107    #[error("Error from the socket device: {0}")]
108    SocketDeviceError(#[from] SocketError),
109    /// Invalid descriptor or descriptor chain.
110    #[error("Popped an invalid descriptor or descriptor chain")]
111    InvalidDescriptor,
112}
113
114#[cfg(feature = "alloc")]
115impl From<alloc::string::FromUtf8Error> for Error {
116    fn from(_value: alloc::string::FromUtf8Error) -> Self {
117        Self::IoError
118    }
119}
120
121/// Align `size` up to a page.
122fn align_up(size: usize) -> usize {
123    (size + PAGE_SIZE) & !(PAGE_SIZE - 1)
124}
125
126/// The number of pages required to store `size` bytes, rounded up to a whole number of pages.
127fn pages(size: usize) -> usize {
128    size.div_ceil(PAGE_SIZE)
129}
130
131// TODO: Use NonNull::slice_from_raw_parts once it is stable.
132/// Creates a non-null raw slice from a non-null thin pointer and length.
133fn nonnull_slice_from_raw_parts<T>(data: NonNull<T>, len: usize) -> NonNull<[T]> {
134    NonNull::new(ptr::slice_from_raw_parts_mut(data.as_ptr(), len)).unwrap()
135}