• 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 //! 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