• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //! # #[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 #![deny(unused_must_use, missing_docs)]
46 #![allow(clippy::identity_op)]
47 #![allow(dead_code)]
48 
49 #[cfg(any(feature = "alloc", test))]
50 extern crate alloc;
51 
52 pub mod device;
53 mod hal;
54 mod queue;
55 pub mod transport;
56 mod volatile;
57 
58 use core::{
59     fmt::{self, Display, Formatter},
60     ptr::{self, NonNull},
61 };
62 
63 pub use self::hal::{BufferDirection, Hal, PhysAddr};
64 
65 /// The page size in bytes supported by the library (4 KiB).
66 pub const PAGE_SIZE: usize = 0x1000;
67 
68 /// The type returned by driver methods.
69 pub type Result<T = ()> = core::result::Result<T, Error>;
70 
71 /// The error type of VirtIO drivers.
72 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
73 pub enum Error {
74     /// There are not enough descriptors available in the virtqueue, try again later.
75     QueueFull,
76     /// The device is not ready.
77     NotReady,
78     /// The device used a different descriptor chain to the one we were expecting.
79     WrongToken,
80     /// The queue is already in use.
81     AlreadyUsed,
82     /// Invalid parameter.
83     InvalidParam,
84     /// Failed to alloc DMA memory.
85     DmaError,
86     /// I/O Error
87     IoError,
88     /// The request was not supported by the device.
89     Unsupported,
90     /// The config space advertised by the device is smaller than the driver expected.
91     ConfigSpaceTooSmall,
92     /// The device doesn't have any config space, but the driver expects some.
93     ConfigSpaceMissing,
94     /// Error from the socket device.
95     SocketDeviceError(device::socket::SocketError),
96 }
97 
98 impl Display for Error {
fmt(&self, f: &mut Formatter) -> fmt::Result99     fn fmt(&self, f: &mut Formatter) -> fmt::Result {
100         match self {
101             Self::QueueFull => write!(f, "Virtqueue is full"),
102             Self::NotReady => write!(f, "Device not ready"),
103             Self::WrongToken => write!(
104                 f,
105                 "Device used a different descriptor chain to the one we were expecting"
106             ),
107             Self::AlreadyUsed => write!(f, "Virtqueue is already in use"),
108             Self::InvalidParam => write!(f, "Invalid parameter"),
109             Self::DmaError => write!(f, "Failed to allocate DMA memory"),
110             Self::IoError => write!(f, "I/O Error"),
111             Self::Unsupported => write!(f, "Request not supported by device"),
112             Self::ConfigSpaceTooSmall => write!(
113                 f,
114                 "Config space advertised by the device is smaller than expected"
115             ),
116             Self::ConfigSpaceMissing => {
117                 write!(
118                     f,
119                     "The device doesn't have any config space, but the driver expects some"
120                 )
121             }
122             Self::SocketDeviceError(e) => write!(f, "Error from the socket device: {e:?}"),
123         }
124     }
125 }
126 
127 impl From<device::socket::SocketError> for Error {
from(e: device::socket::SocketError) -> Self128     fn from(e: device::socket::SocketError) -> Self {
129         Self::SocketDeviceError(e)
130     }
131 }
132 
133 /// Align `size` up to a page.
align_up(size: usize) -> usize134 fn align_up(size: usize) -> usize {
135     (size + PAGE_SIZE) & !(PAGE_SIZE - 1)
136 }
137 
138 /// The number of pages required to store `size` bytes, rounded up to a whole number of pages.
pages(size: usize) -> usize139 fn pages(size: usize) -> usize {
140     (size + PAGE_SIZE - 1) / PAGE_SIZE
141 }
142 
143 // TODO: Use NonNull::slice_from_raw_parts once it is stable.
144 /// 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]>145 fn nonnull_slice_from_raw_parts<T>(data: NonNull<T>, len: usize) -> NonNull<[T]> {
146     NonNull::new(ptr::slice_from_raw_parts_mut(data.as_ptr(), len)).unwrap()
147 }
148