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) {
18 //! let header = NonNull::new(mmio_device_address as *mut VirtIOHeader).unwrap();
19 //! let transport = unsafe { MmioTransport::new(header) }.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 //! use virtio_drivers::{
28 //! device::console::VirtIOConsole,
29 //! transport::{mmio::MmioTransport, DeviceType, Transport},
30 //! };
31
32 //!
33 //! # fn example<HalImpl: Hal>(transport: MmioTransport) {
34 //! if transport.device_type() == DeviceType::Console {
35 //! let mut console = VirtIOConsole::<HalImpl, _>::new(transport).unwrap();
36 //! // Send a byte to the console.
37 //! console.send(b'H').unwrap();
38 //! }
39 //! # }
40 //! ```
41
42 #![cfg_attr(not(test), no_std)]
43 #![deny(unused_must_use, missing_docs)]
44 #![allow(clippy::identity_op)]
45 #![allow(dead_code)]
46
47 #[cfg(any(feature = "alloc", test))]
48 extern crate alloc;
49
50 pub mod device;
51 mod hal;
52 mod queue;
53 pub mod transport;
54 mod volatile;
55
56 use core::{
57 fmt::{self, Display, Formatter},
58 ptr::{self, NonNull},
59 };
60
61 pub use self::hal::{BufferDirection, Hal, PhysAddr};
62
63 /// The page size in bytes supported by the library (4 KiB).
64 pub const PAGE_SIZE: usize = 0x1000;
65
66 /// The type returned by driver methods.
67 pub type Result<T = ()> = core::result::Result<T, Error>;
68
69 /// The error type of VirtIO drivers.
70 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
71 pub enum Error {
72 /// There are not enough descriptors available in the virtqueue, try again later.
73 QueueFull,
74 /// The device is not ready.
75 NotReady,
76 /// The device used a different descriptor chain to the one we were expecting.
77 WrongToken,
78 /// The queue is already in use.
79 AlreadyUsed,
80 /// Invalid parameter.
81 InvalidParam,
82 /// Failed to alloc DMA memory.
83 DmaError,
84 /// I/O Error
85 IoError,
86 /// The request was not supported by the device.
87 Unsupported,
88 /// The config space advertised by the device is smaller than the driver expected.
89 ConfigSpaceTooSmall,
90 /// The device doesn't have any config space, but the driver expects some.
91 ConfigSpaceMissing,
92 /// Error from the socket device.
93 SocketDeviceError(device::socket::SocketError),
94 }
95
96 impl Display for Error {
fmt(&self, f: &mut Formatter) -> fmt::Result97 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
98 match self {
99 Self::QueueFull => write!(f, "Virtqueue is full"),
100 Self::NotReady => write!(f, "Device not ready"),
101 Self::WrongToken => write!(
102 f,
103 "Device used a different descriptor chain to the one we were expecting"
104 ),
105 Self::AlreadyUsed => write!(f, "Virtqueue is already in use"),
106 Self::InvalidParam => write!(f, "Invalid parameter"),
107 Self::DmaError => write!(f, "Failed to allocate DMA memory"),
108 Self::IoError => write!(f, "I/O Error"),
109 Self::Unsupported => write!(f, "Request not supported by device"),
110 Self::ConfigSpaceTooSmall => write!(
111 f,
112 "Config space advertised by the device is smaller than expected"
113 ),
114 Self::ConfigSpaceMissing => {
115 write!(
116 f,
117 "The device doesn't have any config space, but the driver expects some"
118 )
119 }
120 Self::SocketDeviceError(e) => write!(f, "Error from the socket device: {e:?}"),
121 }
122 }
123 }
124
125 impl From<device::socket::SocketError> for Error {
from(e: device::socket::SocketError) -> Self126 fn from(e: device::socket::SocketError) -> Self {
127 Self::SocketDeviceError(e)
128 }
129 }
130
131 /// Align `size` up to a page.
align_up(size: usize) -> usize132 fn align_up(size: usize) -> usize {
133 (size + PAGE_SIZE) & !(PAGE_SIZE - 1)
134 }
135
136 /// The number of pages required to store `size` bytes, rounded up to a whole number of pages.
pages(size: usize) -> usize137 fn pages(size: usize) -> usize {
138 (size + PAGE_SIZE - 1) / PAGE_SIZE
139 }
140
141 // TODO: Use NonNull::slice_from_raw_parts once it is stable.
142 /// Creates a non-null raw slice from a non-null thin pointer and length.
nonnull_slice_from_raw_parts<T>(data: NonNull<T>, len: usize) -> NonNull<[T]>143 fn nonnull_slice_from_raw_parts<T>(data: NonNull<T>, len: usize) -> NonNull<[T]> {
144 NonNull::new(ptr::slice_from_raw_parts_mut(data.as_ptr(), len)).unwrap()
145 }
146