virtio_drivers/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::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::Hal;
27//! # #[cfg(feature = "alloc")]
28//! use virtio_drivers::{
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#![cfg_attr(docsrs, feature(doc_cfg))]
46#![deny(
47 unsafe_op_in_unsafe_fn,
48 unused_must_use,
49 missing_docs,
50 clippy::undocumented_unsafe_blocks
51)]
52#![allow(clippy::identity_op)]
53#![allow(dead_code)]
54
55#[cfg(any(feature = "alloc", test))]
56extern crate alloc;
57
58pub mod config;
59pub mod device;
60#[cfg(feature = "embedded-io")]
61mod embedded_io;
62mod hal;
63pub mod queue;
64pub mod transport;
65
66use device::socket::SocketError;
67use thiserror::Error;
68
69pub use self::hal::{BufferDirection, Hal, PhysAddr};
70pub use safe_mmio::UniqueMmioPointer;
71
72/// The page size in bytes supported by the library (4 KiB).
73pub const PAGE_SIZE: usize = 0x1000;
74
75const PAGE_SIZE_PHYS: PhysAddr = PAGE_SIZE as PhysAddr;
76
77/// The type returned by driver methods.
78pub type Result<T = ()> = core::result::Result<T, Error>;
79
80/// The error type of VirtIO drivers.
81#[derive(Copy, Clone, Debug, Eq, Error, PartialEq)]
82pub enum Error {
83 /// There are not enough descriptors available in the virtqueue, try again later.
84 #[error("Virtqueue is full")]
85 QueueFull,
86 /// The device is not ready.
87 #[error("Device not ready")]
88 NotReady,
89 /// The device used a different descriptor chain to the one we were expecting.
90 #[error("Device used a different descriptor chain to the one we were expecting")]
91 WrongToken,
92 /// The queue is already in use.
93 #[error("Virtqueue is already in use")]
94 AlreadyUsed,
95 /// Invalid parameter.
96 #[error("Invalid parameter")]
97 InvalidParam,
98 /// Failed to allocate DMA memory.
99 #[error("Failed to allocate DMA memory")]
100 DmaError,
101 /// I/O error
102 #[error("I/O error")]
103 IoError,
104 /// The request was not supported by the device.
105 #[error("Request not supported by device")]
106 Unsupported,
107 /// The config space advertised by the device is smaller than the driver expected.
108 #[error("Config space advertised by the device is smaller than expected")]
109 ConfigSpaceTooSmall,
110 /// The device doesn't have any config space, but the driver expects some.
111 #[error("The device doesn't have any config space, but the driver expects some")]
112 ConfigSpaceMissing,
113 /// Error from the socket device.
114 #[error("Error from the socket device: {0}")]
115 SocketDeviceError(#[from] SocketError),
116}
117
118#[cfg(feature = "alloc")]
119impl From<alloc::string::FromUtf8Error> for Error {
120 fn from(_value: alloc::string::FromUtf8Error) -> Self {
121 Self::IoError
122 }
123}
124
125/// Align `size` up to a page.
126fn align_up(size: usize) -> usize {
127 (size + PAGE_SIZE) & !(PAGE_SIZE - 1)
128}
129
130/// Align `size` up to a page.
131fn align_up_phys(size: PhysAddr) -> PhysAddr {
132 (size + PAGE_SIZE_PHYS) & !(PAGE_SIZE_PHYS - 1)
133}
134
135/// The number of pages required to store `size` bytes, rounded up to a whole number of pages.
136fn pages(size: usize) -> usize {
137 size.div_ceil(PAGE_SIZE)
138}